From 67eb83674c9da25587a253c8efa75356e246a671 Mon Sep 17 00:00:00 2001 From: tdurieux Date: Sun, 6 Sep 2026 09:17:52 +0200 Subject: [PATCH] fix: sandbox repository webview documents --- src/server/routes/webview.ts | 1 + test/production-regressions.test.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/server/routes/webview.ts b/src/server/routes/webview.ts index 1f83754..c33326a 100644 --- a/src/server/routes/webview.ts +++ b/src/server/routes/webview.ts @@ -62,6 +62,7 @@ const indexPriority = [ ]; async function webView(req: express.Request, res: express.Response) { + res.header("Content-Security-Policy", "sandbox allow-popups allow-forms allow-modals"); const repo = await getRepo(req, res); if (!repo) return; try { diff --git a/test/production-regressions.test.js b/test/production-regressions.test.js index b9820ea..e7a5ecd 100644 --- a/test/production-regressions.test.js +++ b/test/production-regressions.test.js @@ -152,6 +152,21 @@ describe("production regressions", function () { expect(second.headers.ETag).not.to.equal(first.headers.ETag); expect(second.statusCode).not.to.equal(304); }); + it("sandboxes rendered webview documents", async function () { + const File = require("../src/core/AnonymizedFile").default; + const utils = require("../src/server/routes/route-utils"); + const repo = { options: { terms: [], page: true, pageSource: { path: "/", branch: "main" }, image: true }, model: { source: { branch: "main" } } }; + stub(utils, "getRepo", async () => repo); + let path; + stub(File.prototype, "getFileInfo", async function () { path = this.anonymizedPath; return { name: "my file.html", path: "", size: 10 }; }); + stub(File.prototype, "send", async () => {}); + const handler = require("../src/server/routes/webview").default.stack[0].route.stack[0].handle; + const res = response(); + await handler({ path: "/repo/my file.html", params: { repoId: "repo" }, headers: {} }, res); + expect(path).to.equal("my file.html"); + expect(res.headers["Content-Security-Policy"]).to.include("sandbox"); + expect(res.headers["Content-Security-Policy"]).not.to.include("allow-same-origin"); + }); it("omits an upstream length when later text is rewritten", async function () { const File = require("../src/core/AnonymizedFile").default; stub(config, "STREAMER_ENTRYPOINT", "");