Host-mediated outbound HTTP for wasm plugins: guest http.RoundTripper over the http.request capability; the host performs the request under policy. New egress package defines the host-pattern grammar (exact or multi-label wildcard, public-suffix rejected), matching, resource bounds, and the ErrDenied sentinel (ABI_ERROR_CODE_EGRESS_DENIED). plugin.mod gains first-class AllowedHosts + MaxResponseMB; manifest gains allowed_hosts=34, max_response_mb=35. CoreServices.OutboundHTTP carries the transport. Golden roundtrip + denial-mapping tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
package egress
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePatternRejects(t *testing.T) {
|
|
for _, raw := range []string{
|
|
"", "*", "*.com", "*.co.uk", "*.uk",
|
|
"https://api.cal.com", "api.cal.com/path", "user@api.cal.com",
|
|
"*.*.cal.com", "api.cal.com:0", "api.cal.com:99999",
|
|
"*.", "-bad.com", "bad-.com",
|
|
} {
|
|
if _, err := ParsePattern(raw); err == nil {
|
|
t.Errorf("ParsePattern(%q) = nil error, want rejection", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParsePatternAccepts(t *testing.T) {
|
|
for _, raw := range []string{
|
|
"api.cal.com", "*.cal.com", "example.com",
|
|
"api.example.com:8443", "sub.deep.example.org",
|
|
} {
|
|
if _, err := ParsePattern(raw); err != nil {
|
|
t.Errorf("ParsePattern(%q) = %v, want accept", raw, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAllowlistAllows(t *testing.T) {
|
|
al, err := ParseAllowlist([]string{"api.cal.com", "*.example.org", "api.svc.com:8443"})
|
|
if err != nil {
|
|
t.Fatalf("ParseAllowlist: %v", err)
|
|
}
|
|
tests := []struct {
|
|
url string
|
|
allow bool
|
|
}{
|
|
{"https://api.cal.com/v1/slots", true},
|
|
{"https://api.cal.com:443/v1/slots", true},
|
|
{"https://cal.com/", false}, // apex not matched by exact api.cal.com
|
|
{"http://api.cal.com/", false}, // http refused
|
|
{"https://api.cal.com:8443/", false}, // wrong port
|
|
{"https://a.example.org/", true}, // wildcard depth 1
|
|
{"https://a.b.c.example.org/", true}, // wildcard multi-label
|
|
{"https://example.org/", false}, // wildcard excludes apex
|
|
{"https://evilexample.org/", false}, // suffix must be dot-anchored
|
|
{"https://api.svc.com:8443/", true}, // explicit port
|
|
{"https://api.svc.com/", false}, // default port not authorized
|
|
}
|
|
for _, tt := range tests {
|
|
u, _ := url.Parse(tt.url)
|
|
err := al.Allows(u)
|
|
if tt.allow && err != nil {
|
|
t.Errorf("Allows(%q) = %v, want allow", tt.url, err)
|
|
}
|
|
if !tt.allow && err == nil {
|
|
t.Errorf("Allows(%q) = nil, want deny", tt.url)
|
|
}
|
|
if !tt.allow && err != nil && !errors.Is(err, ErrDenied) {
|
|
t.Errorf("Allows(%q) denial not ErrDenied: %v", tt.url, err)
|
|
}
|
|
}
|
|
}
|