Skip to content

feat(config): reject over-broad Origin allowlist entries (#23)#36

Merged
JumpTechCode merged 1 commit into
mainfrom
feat/config-validate-origins
Jun 17, 2026
Merged

feat(config): reject over-broad Origin allowlist entries (#23)#36
JumpTechCode merged 1 commit into
mainfrom
feat/config-validate-origins

Conversation

@JumpTechCode

Copy link
Copy Markdown
Collaborator

What

Validate allowed_origins at the fail-fast config gate and reject entries that defeat the edge's DNS-rebinding guard (design §8, issue #23).

The guard (internal/edge/guard.go) is a pure prefix/exact matcher — it faithfully honors whatever it is given and cannot judge whether an entry is too broad. That judgement belongs in the validation layer, where the gateway already rejects bad configuration before request time (§7). This keeps the guard a pure matcher.

Config.Validate now rejects, with an actionable message:

  • a bare "*" (empty prefix → matches every Origin, allow-all);
  • a wildcard not preceded by a scheme delimiter ("localhost*", "://*");
  • a wildcard over the whole host of a network scheme ("http://*", "https://*", "ws://*", "wss://*"). Opaque schemes keep the scheme itself as the boundary ("vscode-webview://*", "app://*") and still load;
  • an exact entry that is not a bare origin — no scheme, or carrying a path/query/fragment (a real Origin header carries none, so such an entry is dead config that never matches).

The check is part of Validate(), so it guards both initial load (config.Load) and SIGHUP reload (app.go).

Acceptance criteria (issue #23)

  • Config load rejects an over-broad Origin allowlist entry with a clear, actionable error message.
  • Tests cover the rejected forms and confirm legitimate entries (http://localhost, vscode-webview://*) still load.

Testing

  • New table cases in TestValidateRejects for every rejected form; new TestValidateAcceptsLegitimateOrigins for the allowed forms.
  • make verify locally: build, vet, -race tests, coverage 95.3% (gate 80%), golangci-lint v2.12.2 clean, gofmt clean.
  • The shipped example config (config/portcullis.example.yaml) still validates (existing app test).

Closes #23

🤖 Generated with Claude Code

The edge request guard (internal/edge/guard.go) is a pure matcher: an
allowed_origins entry matches exactly, or — when it ends in "*" — by the
prefix before the "*". It faithfully honors whatever it is given, so it
cannot tell an intentional scope (vscode-webview://*) from operator
misconfiguration that defeats the rebinding guard (a bare "*" matches every
Origin; "http://*" matches any http origin).

Put that judgement at the fail-fast config gate, where the gateway already
rejects bad configuration (§7), keeping the guard a pure matcher. Validate
rejects, with an actionable message:

  - a bare "*" (allow-all);
  - a wildcard not preceded by a scheme delimiter ("localhost*", "://*");
  - a wildcard over the whole host of a network scheme ("http://*",
    "https://*", "ws://*", "wss://*"), while opaque schemes keep the scheme
    as the boundary ("vscode-webview://*", "app://*") and still load;
  - an exact entry that is not a bare origin (no scheme, or carrying a path,
    query, or fragment — a real Origin header carries none, so it is dead
    config).

The check lives in Validate(), so it guards both initial load and SIGHUP
reload. Legitimate forms (http://localhost, vscode-webview://*) are covered
by a positive test.

Closes #23

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JumpTechCode
JumpTechCode merged commit d5d47dc into main Jun 17, 2026
5 checks passed
@JumpTechCode
JumpTechCode deleted the feat/config-validate-origins branch June 17, 2026 04:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

config: validate Origin allowlist entries (reject over-broad wildcards)

1 participant