diff --git a/internal/webapp/dir_test.go b/internal/webapp/dir_test.go index 9fcb7e2..8a390f0 100644 --- a/internal/webapp/dir_test.go +++ b/internal/webapp/dir_test.go @@ -67,6 +67,35 @@ func TestDirSourceServesFolder(t *testing.T) { } } +// Synced HTML served inline must never run with the hub origin's session: +// the file endpoint sandboxes it (same posture as /s/* shares). Downloads +// are exempt — an attachment never executes in the hub's origin. +func TestInlineHTMLIsSandboxed(t *testing.T) { + h := dirServer(t, map[string]string{ + "page.html": "

hi

", + "pic.svg": "", + "plan.md": "# md", + }) + for path, wantCSP := range map[string]bool{ + "/api/file?path=page.html": true, + "/api/file?path=pic.svg": true, + "/api/file?path=plan.md": false, + "/api/download?path=page.html": false, // attachment, not rendered + } { + rec := get(t, h, path) + if rec.Code != 200 { + t.Fatalf("%s: %d", path, rec.Code) + } + csp := rec.Header().Get("Content-Security-Policy") + if wantCSP && csp != "sandbox allow-scripts" { + t.Errorf("%s: CSP = %q, want sandbox", path, csp) + } + if !wantCSP && csp != "" { + t.Errorf("%s: unexpected CSP %q", path, csp) + } + } +} + // The frontend serves real assets directly but returns the app shell for any // client-side route (a deep file path, /join/), so a deep link or // refresh doesn't 404. Reserved API/auth/share prefixes stay real 404s. diff --git a/internal/webapp/frontend/e2e/browse.spec.ts b/internal/webapp/frontend/e2e/browse.spec.ts index bb07420..3fe7fd7 100644 --- a/internal/webapp/frontend/e2e/browse.spec.ts +++ b/internal/webapp/frontend/e2e/browse.spec.ts @@ -127,3 +127,39 @@ test("upload into the selected folder, then the file opens", async ({ page }) => await expect(page.locator("#content h1")).toHaveText("Dropped"); await expect(page.locator('#tree .row[data-path="notes/dropped.md"]')).toBeVisible(); }); + +test("html file renders as a page in a sandboxed iframe", async ({ page }) => { + await login(page); + const pid = await wikiId(page); + const html = "

Hello from HTML

- + +