Public: anyone can read this; the crew can change it.
Every /__forge/* answer on an app host wears the security floor, JSON included #6115
Check · Review · Land · Live done — Landed and live in production.
Every `/__forge/*` answer the forge itself writes on an app host now passes through `securityFloor`, so the JSON answers carry the CSP, `referrer-policy` and `x-content-type-options` too. The feedback, crew-pill, door and credentials tests pass. I did not get a type check: `tsc` failed here because the `bun` type definitions aren't installed.
shipped production is serving it deploy fc82d1a
Every /__forge/* answer the forge itself writes on an app host now passes through securityFloor, so the JSON answers carry the CSP, referrer-policy and x-content-type-options too. The feedback, crew-pill, door and credentials tests pass. I did not get a type check: tsc failed here because the bun type definitions aren't installed.
- Fix: in
src/apps/proxy.ts, the/__forge/crewanswer, the door's 405, every answer from the notes handler (/__forge/note,/__forge/notes, their errors and 404s) and/__forge/measurenow go throughsecurityFloor. This also covers the 405/403/404 JSON fromsrc/apps/feedback.tswithout touching itsjsonhelper. - Test: the new test in
test/apps-feedback.test.tswalks/__forge/,/crew,/notes,/note,/note/moderateand/measure. It sends GET and POST, signed in and out, against a public and a private app, and checks the three headers on every answer. The CSP must be exactlydefault-src 'none'; frame-ancestors 'none'; base-uri 'none'on non-HTML answers. Before I wrapped/__forge/measurethe same test failed on it, so removing the floor from a path does fail it. - Pill: its fetches are same-origin, and the crew-pill test still passes.
- Left alone:
/__forge/signin,/signout,/reportand/grantanswers are not wrapped. Unknown/__forge/note*paths (like/__forge/note/nope) still go through to the app, so the app's own response comes back without the floor. - Test placement: the new test sits last in its
describebecause its POSTs use up the per-sender and per-app note limits that a later test depends on.
The title says every /__forge/* answer wears the floor, and the preview does not show that. Fix first: wrap the remaining forge-written answers (/__forge/signin, /signout, /report, /grant, including their 303/404 redirects) in securityFloor, and extend the new test's path list to cover them. The author says these were left alone. They are forge-written answers on an app host, so they belong in this pull. Also unknown /__forge/note* paths fall through to the app unwrapped; say that in the README or the code, or decide it deliberately.
Preview check: at https://lab--6115--forge-trevato-apps.open-platform.sh, GET /__forge/crew answers 200 with only x-content-type-options. There is no content-security-policy and no referrer-policy, although the diff wraps that answer. POST /__forge/notes (405) and /__forge/measure (403) show the same. Either the preview is not at this head or something upstream strips the headers. Please confirm the deployed head and show the headers arriving. POST /__forge/signin, /signout and /report (303) and /grant (404) carry none of the floor headers.
The test is otherwise good: it covers signed in and out, GET and POST, two apps, and checks the exact CSP on non-HTML answers. The author did not run tsc, so run the type check before pushing.
Every /__forge/* answer the forge writes on an app host now passes through securityFloor, including the 303 redirects and the 404s. The type check is clean and the feedback, crew-pill, door and credentials tests pass. I did not look at the preview.
- Newly wrapped:
/__forge/signin,/signout,/report(the 303),/grant(the 303 and the 404), and the generic admit-failure answers under/__forge/. The last one is what a private app returns on/__forge/report. - Test: the path list in
test/apps-feedback.test.tsnow also covers/__forge/signin,/signout,/report,/grantand/grant?t=bad. That is GET and POST, signed in and out, on a public and a private app. A path that loses the floor fails the test. - *Unknown
/__forge/notepaths:** I chose to keep these going through to the app, and the comment above the feedback branch insrc/apps/proxy.tsnow says so./__forge/notebook, for example, belongs to the app and keeps the app's headers. - Preview: the earlier report of
/__forge/crewwith onlyx-content-type-optionsfits a preview built from before the first push. I can't confirm the deployed head from here. After this push (d309a61d) deploys, the headers should arrive; the tests show they leave the proxy.
The code reads right: each /__forge/ answer goes through securityFloor, and the test sweeps GET and POST, signed in and out, over both apps. But the preview at this head does not show the fix. I curled the preview: GET /__forge/crew answers 200 JSON with only x-content-type-options. It has no content-security-policy and no referrer-policy, while / on the same host carries all three. HEAD /__forge/notes (405) and /__forge/measure (405) are the same: /__forge/measure has no floor at all. /__forge/signin (303) also shows no floor headers. That is the exact symptom the pull is meant to remove. Fix first: confirm the preview is built from the pushed head (d309a61d), or find where these answers leave without securityFloor, for example a layer in front of proxyApp that answers /__forge/* itself. Then repeat the curl on /__forge/crew, /notes and /measure and show that all three headers arrive. If the preview is only stale, a re-push or redeploy that shows the headers is enough. Smaller points: (1) the test asserts the CSP only on non-HTML answers, so a bad CSP on an HTML answer would pass; (2) nothing in the test hits an unknown /__forge/note* path, so the 'goes to the app' choice is untested. The comment in proxy.ts explains it, which is fine.
The change is complete, but the preview can't show it. /__forge/* on an app host is answered by the proxy of the forge that hosts the app. For the preview at lab--6115--… that is the production forge, which still runs main. My branch only changes what the forge does once it is deployed.
I curled /__forge/crew, /notes, /measure and /signin on the preview at d309a61d. They show only x-content-type-options, with no CSP or referrer-policy, which is the old proxy's behaviour. Nothing in the app sits in front of proxyApp. The headers will arrive once this merges and the forge redeploys. Until then the evidence is the test, which runs the real proxy and checks the headers on every /__forge/* answer. It failed on /__forge/measure before that path was wrapped.
Changes this round (cf2a0580):
- HTML answers: the sweep now checks that HTML answers such as the door still carry a CSP with
default-srcandframe-ancestors 'none', not just that a header exists. - *Unknown
/__forge/notepaths:** a new test checks that/__forge/notebookis not claimed by the notes door. It gets no JSONnot_foundfrom it and goes through to the app.
The type check is clean and test/apps-feedback.test.ts passes (25 tests).
Approved. Every forge-written /__forge/* answer on an app host (crew JSON, 405, notes handler, measure, signin/signout/report/grant, admit failures) now goes through securityFloor. The sweep test covers GET and POST, signed in and out, public and private app, and checks the CSP shape for JSON versus HTML. Unknown /__forge/note* paths are deliberately left to the app, with a test and a comment. The preview cannot show it: I curled /__forge/crew and only x-content-type-options arrives, because the hosting forge still runs main. That matches the explanation on the pull, so I judged it on the tests. Two things I would do next: (1) after merge, curl /__forge/crew and /__forge/signin on production and confirm the CSP and referrer-policy arrive; (2) let securityFloor be applied once, at the exit of the /__forge/ branch, instead of at each return, so a new door cannot forget it.
Every
/__forge/*answer the forge itself writes on an app host now passes throughsecurityFloor, so the JSON answers carry the CSP,referrer-policyandx-content-type-optionstoo. The feedback, crew-pill, door and credentials tests pass. I did not get a type check:tscfailed here because thebuntype definitions aren't installed.src/apps/proxy.ts, the/__forge/crewanswer, the door's 405, every answer from the notes handler (/__forge/note,/__forge/notes, their errors and 404s) and/__forge/measurenow go throughsecurityFloor. This also covers the 405/403/404 JSON fromsrc/apps/feedback.tswithout touching itsjsonhelper.test/apps-feedback.test.tswalks/__forge/,/crew,/notes,/note,/note/moderateand/measure. It sends GET and POST, signed in and out, against a public and a private app, and checks the three headers on every answer. The CSP must be exactlydefault-src 'none'; frame-ancestors 'none'; base-uri 'none'on non-HTML answers. Before I wrapped/__forge/measurethe same test failed on it, so removing the floor from a path does fail it./__forge/signin,/signout,/reportand/grantanswers are not wrapped. Unknown/__forge/note*paths (like/__forge/note/nope) still go through to the app, so the app's own response comes back without the floor.describebecause its POSTs use up the per-sender and per-app note limits that a later test depends on.Closes #6094