-
Notifications
You must be signed in to change notification settings - Fork 0
Origin Policy
type OriginPolicy struct {
AllowedOrigins []string
AllowCredentials bool
}One allowlist of browser origins, declared here so CORS and CSRF read the same configuration and neither module imports the other.
policy := rextension.OriginPolicy{
AllowedOrigins: []string{"https://app.example.com", "https://admin.example.com"},
AllowCredentials: true,
}
if err := policy.Valid(); err != nil {
return err // fail the deployment, not the request
}The two consumers share a list and nothing else. It is worth being explicit about why, because the belief that CORS covers CSRF is widespread and wrong.
A simple request — a form POST with application/x-www-form-urlencoded,
multipart/form-data or text/plain — gets no preflight. The browser
dispatches it, the server receives it, and the server commits the state change.
CORS then prevents the attacker from reading the response, which is no
consolation for a transfer that already happened.
So: CORS answers "may this origin read my responses?". CSRF answers "did this request really come from my own application?". Different questions, one allowlist.
Origins are matched exactly — scheme, host and port:
"https://app.example.com" // right
"app.example.com" // never matches; no scheme
"https://app.example.com:8443" // a different origin than the same host on 443There is no pattern or wildcard-subdomain matching, deliberately. Origin patterns are a classic source of over-permissive policies:
- a naive suffix check for
.example.commatchesevil-example.com - a prefix check for
https://app.examplematcheshttps://app.example.attacker.com
Both have appeared in real advisories. List the origins.
"*" allows any origin, and is valid only without credentials.
This is not convention. The Fetch specification requires that
Access-Control-Allow-Origin echo a concrete origin when
Access-Control-Allow-Credentials is true, and browsers reject the response
otherwise. Allows therefore refuses "*" when AllowCredentials is set,
rather than emitting a combination the browser will silently discard — a failure
that looks like a server bug from the client's side and produces no useful error
anywhere.
Valid() reports the contradiction, and it is checked at startup so a
self-contradictory policy stops a deployment rather than producing responses
nobody can debug.
Allows("") is always false, including under a "*" policy. A same-origin
request carries no Origin header for most methods, and such a request needs no
CORS decision at all — treating "" as allowed would make every allowlist
meaningless.
// The request's Origin header. Deliberately does not fall back to Referer.
func RequestOrigin(r *http.Request) string
// Safe in the RFC 9110 sense: does not change server state.
func SafeMethod(method string) boolReferer is not consulted as a fallback. It is stripped by privacy
settings, by referrer policies and by some proxies — so treating its absence as
"no cross-origin request" would be a bypass, and treating its presence as
authoritative would let a referrer policy weaken the check.
CSRF protection applies only to unsafe methods. That is not a convenience: a
GET that changes state is a bug in its own right, and one CSRF protection cannot
fix, because a browser will issue it from an <img> tag with no way for the
server to distinguish it.
Allows(origin string) bool |
is this origin permitted |
IsWildcard() bool |
does the policy allow any origin |
Valid() error |
is the policy internally consistent, and why not |
Valid returns *OriginPolicyError, which names the offending entry and the
reason.
rextension — the Rex extension contract · MIT · © 2026 Kryovyx · pre-1.0, interfaces may change
Building an extension
Contracts
Reference
Ecosystem