Skip to content

fix(cors): let the SDK's own requests and responses cross an origin - #84

Merged
42-v merged 3 commits into
mainfrom
fix/cors-sdk-headers
Aug 26, 2026
Merged

fix(cors): let the SDK's own requests and responses cross an origin#84
42-v merged 3 commits into
mainfrom
fix/cors-sdk-headers

Conversation

@42-v

@42-v 42-v commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Two halves of the same defect. Together they made the documented cross-origin deployment mode not work at all — not degrade, not error usefully, simply not work.

Requests never left the browser. Access-Control-Allow-Headers listed Content-Type, Authorization, DPoP. The Vue SDK sends X-Requested-With on every request and X-Blob-Checksum / X-Blob-Label on blob writes. None of the three is CORS-safelisted, so the browser refused at preflight and the request never reached vault42 — which is exactly why no server-side test could see it, and why the whole suite, being same-origin, passed.

Responses came back blank. There was no Access-Control-Expose-Headers at all. Only safelisted response headers reach a cross-origin caller and none of vault42's are on that list, so the blob helpers — which hand the checksum and label back to their caller — returned null cross-origin while working perfectly same-origin. Retry-After is exposed with them: the server already sets it on the rate-limited paths (ratelimit.go:291,340) and a client cannot honor a 429 it cannot read.

Two headers from the original report are deliberately absent, having checked rather than assumed:

Header Why not
X-Vault-App proxy-set (model.go:608), so no browser ever asks for it
X-Custom, X-Only, X-Fingerprint test fixtures, not anything the SDK sends

The new test states the SDK's needs separately from the middleware, so the two lists cannot drift apart quietly — if either moves without the other, one of them is wrong and this fails instead of a deployment doing it.

Two existing subtests had to change, and were wrong in an instructive way. Both are named allow-headers includes X and both compared the entire header string for equality, so any legitimate addition broke them while proving nothing their names claimed. One already carried the message "should include DPoP". They now assert containment.

Verified: go test -race ./internal/middleware/ passes; golangci-lint 0 issues on a cleaned cache; misspell clean. A third test confirms widening the allow-list did not widen the origin check.

fix(cors): let the SDK's own requests and responses cross an origin

42-v added 3 commits August 27, 2026 00:05
Two halves of the same defect, and together they made the documented
cross-origin deployment mode not work at all.

Access-Control-Allow-Headers listed Content-Type, Authorization and DPoP. The
Vue SDK sends X-Requested-With on every request and X-Blob-Checksum and
X-Blob-Label on blob writes. None of the three is CORS-safelisted, so the
browser refused the request at preflight and it never reached vault42 -- which
is why no server-side test could see it, and why every test in the suite, being
same-origin, passed.

There was no Access-Control-Expose-Headers at all. Only the safelisted response
headers reach a cross-origin caller and none of the ones vault42 sets is on that
list, so the blob helpers -- which hand the checksum and the label back to their
caller -- returned null cross-origin while working perfectly same-origin.
Retry-After is exposed with them: the server already sets it on the
rate-limited paths and a client cannot honor a 429 it cannot read.

Two headers named in the original report are deliberately absent. X-Vault-App is
proxy-set, so no browser ever asks for it. X-Custom, X-Only and X-Fingerprint
turned out to be test fixtures rather than anything the SDK sends. The list is
what the SDK actually uses, and the new test states that separately from the
middleware so the two cannot drift apart quietly.

Two existing subtests had to change and were wrong in an instructive way. Both
are named "allow-headers includes X" and both compared the entire header string
for equality, so any legitimate addition broke them while proving nothing their
names claimed. One of them already carried the message "should include DPoP".
They now assert containment.
…he value

The Allow-Headers row cited the whole statement including its value, so widening
the header list orphaned the anchor and failed the register gate. That gate did
its job: it asked whether the evidence for the row still exists or whether the
row now claims something the code no longer does.

It still exists. ASVS asks that sensitive functionality cannot be called by a
request which skips the CORS preflight. The set is still closed and still has no
wildcard, and every header added to it is itself non-safelisted, so a request
carrying one preflights exactly as before. Authorization is what actually
carries the argument and it has not moved.

The anchor now stops before the value, the way the workflow-action anchors were
changed to stop at the @ rather than quote a SHA. The notes named a set that is
no longer the set, so they name the real one, and the two line references in
them were stale by seven and sixteen lines.
@42-v
42-v enabled auto-merge (squash) August 26, 2026 22:05
@42-v
42-v force-pushed the fix/cors-sdk-headers branch from d2165bf to 769b8bb Compare August 26, 2026 22:05
@42-v
42-v merged commit 3de630b into main Aug 26, 2026
38 checks passed
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.

1 participant