Tencent/WeKnora · error · ErrUnsafeOutboundURL

ErrUnsafeOutboundURL

ErrUnsafeOutboundURL

Error message

%w: empty URL

What it means

OutboundURLPolicy.Validate is the first-line guard for agent-initiated outbound HTTP. An empty (or whitespace-only) URL cannot be checked, so it is rejected immediately with ErrUnsafeOutboundURL to fail closed rather than allowing an unvalidated request.

Source

Thrown at internal/sandbox/url_guard.go:80

func SafeDialControl(network string, address string, conn syscall.RawConn) error {
	return DefaultOutboundURLPolicy().DialControl(network, address, conn)
}

func SafeDialControlForPolicy(policy OutboundURLPolicy) func(string, string, syscall.RawConn) error {
	return func(network string, address string, conn syscall.RawConn) error {
		return policy.DialControl(network, address, conn)
	}
}

// Validate reports whether raw is an acceptable tenant-supplied endpoint. It
// rejects non-HTTP schemes and any host that resolves to a forbidden address.
//
// Callers must ALSO install DialControl on the dialer they use; Validate alone
// cannot close the DNS-rebinding window.
func (p OutboundURLPolicy) Validate(raw string) error {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return fmt.Errorf("%w: empty URL", ErrUnsafeOutboundURL)
	}
	parsed, err := url.Parse(raw)
	if err != nil {
		return fmt.Errorf("%w: %v", ErrUnsafeOutboundURL, err)
	}
	switch strings.ToLower(parsed.Scheme) {
	case "http", "https":
	default:
		return fmt.Errorf("%w: scheme %q is not allowed", ErrUnsafeOutboundURL, parsed.Scheme)
	}

	host := parsed.Hostname()
	if host == "" {
		return fmt.Errorf("%w: missing host", ErrUnsafeOutboundURL)
	}
	// ".local" is mDNS; "localhost" is only acceptable under the opt-in.
	lower := strings.ToLower(host)
	if strings.HasSuffix(lower, ".local") {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Supply a non-empty http(s) URL before calling, e.g. from config or flags.
  2. Check for empty input at startup and fail configuration loading early with a named field in the message.
  3. If the URL is legitimately optional, skip the call instead of validating an empty string.

Example fix

// before
var endpoint string // never set
err := policy.Validate(endpoint)
// after
endpoint := os.Getenv("WEBHOOK_URL")
if endpoint == "" {
    return errors.New("WEBHOOK_URL must be set")
}
err := policy.Validate(endpoint)
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(rawURL) == "" {
    return errors.New("outbound URL is required")
}

Type guard

func nonEmptyURL(s string) bool { return strings.TrimSpace(s) != "" }

Try / catch

err := policy.Validate(raw)
if errors.Is(err, sandbox.ErrUnsafeOutboundURL) {
    return fmt.Errorf("outbound URL rejected: %w", err)
}

Prevention

When it happens

Trigger: Calling Validate or ValidateOutboundURLWithPolicy with "" or a string that is only whitespace after TrimSpace — typically an unset config field or a variable that was never populated.

Common situations: Missing environment variable / config key for a webhook or API base URL; template placeholder not substituted ("{{url}}" would hit scheme check, truly empty hits this); optional URL fields left blank.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/f75828e875676845. Report an issue: GitHub.