fix(server): return empty list + CORS header for untrusted /permission/pending#3880
Merged
fix(server): return empty list + CORS header for untrusted /permission/pending#3880
Conversation
…n/pending
The previous implementation replied `403 Forbidden` to any untrusted
`Origin` on `/permission/pending`, with no `Access-Control-Allow-*`
header. That caused the browser to surface a "CORS header missing"
error in the devtools console for every non-same-origin fetch
(e.g. from a sandboxed iframe whose origin is `null`), even though
the underlying behaviour — hiding live prompt contents from
untrusted callers — was intentional. The console error looked like a
real bug to users (reported on Matrix by Lukas Orsvärn, 2026-04-14)
and made the viewer URL for contract-hosted webapps spew noise.
Instead, always reply `200 OK` with
`Access-Control-Allow-Origin: *` and return an empty `[]` body when
the `Origin` is untrusted. The shell's polling loop silently ignores
an empty list, so legitimate same-origin callers still receive the
real prompt list and untrusted / null / cross-origin callers get a
valid-shape response they can read but which contains no data.
Security posture is unchanged: a cross-origin or DNS-rebinding
attacker still cannot learn prompt contents, and
`/permission/{nonce}/respond` retains its strict `Origin` check
independently. `*` is safe on this endpoint because no credentials
(cookies, auth tokens) are associated with the poll.
Regression tests cover three cases — untrusted origin, `null`
origin, and trusted origin — asserting both body shape and that the
CORS header is present on every branch. The previous
`test_pending_prompts_rejects_untrusted_origin` is replaced by
`test_pending_prompts_untrusted_origin_returns_empty_with_cors`,
which documents the new contract.
[AI-assisted - Claude]
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
Rule Review: No issues foundRules checked: No rule violations detected. Summary of what was verified:
Rule review against |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Lukas Orsvärn reported (Matrix, 2026-04-14) a console error when opening a contract-hosted image viewer:
/permission/pendingwas replying403 Forbiddento any untrustedOriginheader (e.g.nullfrom a sandboxed iframe), but the403carried noAccess-Control-Allow-*header, so the browser surfaced a "CORS header missing" error in devtools for every non-same-origin caller. The underlying behaviour (hiding live prompt contents from untrusted callers) was intentional, but the console noise looked like a real bug and made the contract-hosted webapp URL unusable without errors.Solution
Always reply
200 OKwithAccess-Control-Allow-Origin: *, and return an empty[]body when theOriginis untrusted. The shell's polling loop silently ignores an empty list:Origin→ full prompt list (unchanged — documented threat model).null/ cross-origin → empty[]with CORS header → browser can deliver the body, no console error.Security posture is unchanged: a cross-origin or DNS-rebinding attacker still cannot learn live prompt contents, and
/permission/{nonce}/respondretains its strictOrigincheck independently.*is safe on this endpoint because no credentials (cookies, auth tokens) are associated with the poll.Testing
Three regression tests in
permission_prompts::tests, all asserting both body shape and presence of the CORS header:test_pending_prompts_untrusted_origin_returns_empty_with_cors— replaces the oldtest_pending_prompts_rejects_untrusted_originand documents the new contract.test_pending_prompts_null_origin_returns_empty_with_cors— regression for the reported bug (Origin: nullfrom sandboxed iframes).test_pending_prompts_trusted_origin_returns_list_with_cors— confirms trusted origins still see the real list and get the CORS header.cargo test -p freenet --lib permission_prompts→ 33 tests, all pass.cargo fmt --checkclean.cargo clippy -p freenet --lib -- -D warningsclean.Fixes
Addresses Lukas's Matrix report from 2026-04-14 (no issue number yet).
[AI-assisted - Claude]