Wymcp. Plugs. OriginCheck
(Wymcp v0.4.0)
View Source
The origin check (docs/glossary.md, wire check): DNS-rebinding
protection for browser-based clients. It reads the Origin header's
cardinality, and rejects a request whose Origin is not on the router's
:origin allowlist when one is configured.
This is the first of the three wire checks on every non-fallthrough
route — the ordering is Wymcp.Router's wire-check invariant. It runs before
anything has validated Origin, which is exactly why this plug carries its
own duplicate arm instead of leaving cardinality to
Wymcp.Plugs.SingletonHeaders.
Origin header | No allowlist (nil or []) | Allowlist configured |
|---|---|---|
| absent | passes | passes — non-browser clients (curl, SDKs) send none |
| one value | passes | passes when on the allowlist, else 403 |
| two or more values | 400 | 400 |
The duplicate arm sits above the allowlist branch, so the two columns
agree on cardinality: a repeated header is the signature of a broken proxy,
and that does not depend on how the mount is configured. RFC 6454 forbids a
user agent from sending more than one Origin header field, and carries
several origins space-separated within one — so the values a single header
line holds are none of this arm's business, and a second line is a proxy
artefact.
The table's rows count what the adapter handed Plug, not what arrived
on the wire. Bandit preserves repeated header lines; Plug.Cowboy folds
them into one comma-joined value before this plug runs, so on Cowboy the
bottom row is unreachable and such a request takes the one value row
instead. README's Supported HTTP adapter section is the full statement
of what wymcp is tested against.
The rejection speaks the route's error dialect, taken from the
:error_dialect init option: the JSON-RPC dialect on POST, the plain-JSON
dialect on GET and DELETE. Its envelope id is always nil, and structurally
so — this plug runs ahead of both body parsing and Wymcp.Plugs.Classify, so
Wymcp.Response.rejection_id/1 has no message kind to read even when the
body does carry an id. The nil is not a judgement about the message kind.