Engineering & roadmap

Implementation review: PB-006, PR 250 (and 445)

Development documentation. This guide follows a reviewed snapshot of main and may describe capabilities not yet in Stable. Check Preview labels and release notes. Some executable names and paths remain unchanged for compatibility. Legacy names explained ↗
Field Value
Backlog item PB-006 Content Security Policy for the main window (feature FEAT-SEC-02; hardening HD-020)
Pull request #250, merged as 29f76070 (head 5c3fc95f; work in ec311614, f8972ae0), set the CSP and moved notebook HTML output to the nbout: frame. #445, merged as aef0d50f (head 38af2939), added the browser test that runs the UI under that CSP.
Package None (no docs/product/pr-ready/PB-006.md; built as HD-020 of the hardening backlog)
Reviewer Engineering quality review thread. It didn't write #250 or #445.
Date 2026-10-27
Environment Linux container, main 6159e507, Python venv for the engine, Playwright Chromium. No tenant, no Windows, no second PC.

Outcome#

Accepted with Documented Limitations (recommended).

The CSP is set, the UI and CSP tests pass, and they fail when the CSP is removed. Notebook HTML output runs in a sandboxed frame with an opaque origin and its own policy with no connect-src. A probe in Chromium found it can't reach the parent page, the IPC endpoint or the network. Two things are open. The WebView2 part is only checked by reading the code. And docs/security.md says the frame "never receives the shell's invoke key", but the Tauri source this build uses puts the invoke key in every frame on Windows (finding 1). The boundary still holds, but on other layers than the one the docs name.

Recommended by: Engineering quality review. Decided by (product owner): pending.

Acceptance criteria#

# Criterion (copied from the backlog item) Result Evidence
1 A CSP is set and the UI test suite passes Met app/src-tauri/tauri.conf.json:21-33: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src ipc: http://ipc.localhost; frame-src nbout: http://nbout.localhost https://nbout.localhost; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'. node --test app/tests/ui.test.cjs: 297 tests, 297 pass. app/tests/browser/csp.test.cjs (#445) serves the UI with this policy: 11/11 pass (inline script blocked and reported; home, create, monitor, settings, notebooks, workspaces, optimize, profile, the notebook editor and the SQL editor open with no violations).
2 Notebook HTML output can't call Tauri commands Met (code reading and Chromium); WebView2 not checked htmlFrame (app/ui/notebook.js:1030-1035) builds <iframe sandbox="allow-scripts"> with no allow-same-origin, loaded from nbout (main.rs:1998-2025, registered at main.rs:2116, 403 for any webview other than main). The frame page's header policy (main.rs:2000-2002) has default-src 'none' and no connect-src. All four HTML paths go through it: type: "html" (notebook.js:983), Altair (1062), Plotly (1087), and the ipywidgets HTML widget (ipywidgets.js:55). The app page's only message listener reads a number for the frame height (notebook.js:1092-1097). Reviewer probe (below): from the frame, parent.document, parent.__TAURI__ and parent.rpc throw SecurityError, self.origin is "null", fetch("http://ipc.localhost/rpc") and a fetch to another host are blocked (no request leaves the page), and top.location = … throws SecurityError. In Tauri 2.12.2, an IPC request with Origin null is refused (tauri ipc/protocol.rs:486-494).

Checks#

Check Result Evidence or note
Scope matches the item Met It matches the item's plan: HTML output moved to another origin (nbout:), then a CSP was set. withGlobalTauri is still true (tauri.conf.json:9). The item doesn't ask to remove it, and security.md:35 says a script that did run in the app's page could call the API.
New tests fail without the change, pass with it Met Copy of main with csp: null and the pre-#250 srcdoc htmlFrame: ui.test.cjs test 100 ("the app has a strict CSP and notebook HTML output stays off the app's origin") fails with "tauri.conf.json must set app.security.csp", and csp.test.cjs fails 11/11 (test 1: the inline script ran, true !== false). The copy had no docs/, so 59 docs-reading ui tests also failed there; those don't bear on this item. With main's files, all pass (above).
Full suites pass (pytest, UI, cargo) Partly ui.test.cjs 297/297. Browser tests that cover HTML output: altair 1/1, plotly 1/1, ipywidgets 1/1, notebook-runs 3/3, csp 11/11. cargo isn't buildable here. The Rust test notebook_output_frame_has_no_network_and_only_opens_in_the_app (main.rs:2248-2257) checks that the frame policy has no connect-src or http source and gives the Fabric view a 403; CI runs cargo test on windows-latest (.github/workflows/ci.yml:281). Not run here. No engine change.
Reachable where docs say; Preview behavior right; packaged into the engine Met No new UI; notebook HTML, Altair, Plotly and widget HTML render in the frame (browser tests above).
OneLake data safety n/a UI and shell only.
Authentication and distribution Met with a note No new scope or command. Tauri treats a registered custom protocol host (nbout.localhost on Windows) as a local origin (tauri webview/mod.rs:1961-2000), and the app has no app ACL manifest (build.rs), so any IPC that did arrive from the frame would be allowed to call rpc. What stops it: the frame's opaque origin (Origin null is refused), the frame policy (no connect-src, so the fetch to ipc.localhost never leaves), and WebView2 sending an iframe's chrome.webview.postMessage to the frame's own event, which wry 0.57 doesn't subscribe to (wry webview2/mod.rs:956 listens on CoreWebView2.WebMessageReceived only). The last one is WebView2 behavior and isn't checked here.
No unproven claim; docs honest about limits Partly security.md says plainly that WebRTC can still leak a notebook's own output. Its claim that the frame "never receives the shell's invoke key" doesn't match Tauri on Windows (finding 1).
Docs match the code; linked; strict docs build passes Partly security.md "Content Security Policy" quotes the policy exactly as tauri.conf.json has it. The invoke key sentence is wrong for Windows (finding 1). The strict docs build wasn't run here.
Accessibility of new controls n/a No new controls; the frame keeps title="HTML output".
Inventory and backlog updated; product_data.py consistent Partly FEAT-SEC-02 is Implemented / Partially Verified with "check under WebView2 on Windows" (FEATURE_INVENTORY.md:284, 1850-1862). docs/hardening/backlog.md:130 says the Windows check was "checked by hand and recorded in the PR"; there was no GitHub access here to see that record. PB-006 is still "In review" (PRODUCT_BACKLOG.md:1386).

Reviewer probe: $S/pb006/app/tests/browser/zz-probe.test.cjs (scratchpad copy of main, not in the repo). It opens the app with the shell's CSP (csp: true), serves the frame page with OUTPUT_FRAME and OUTPUT_CSP read from main.rs, and renders {type: "html"} output whose script tries each escape and posts the results back. Output: {"parentDoc":"blocked: SecurityError","parentTauri":"blocked: SecurityError","parentRpc":"blocked: SecurityError","origin":"null","ipcFetch":"blocked: TypeError","netFetch":"blocked: TypeError","topNav":"blocked: SecurityError","ownTauri":"undefined"}. The only request outside the app was the frame page itself. 1 pass. ownTauri says nothing about Windows, because the test harness puts no Tauri script in the frame.

Findings#

# Severity (blocking / should fix / note) Finding Where Status
1 should fix security.md says the output frame "never receives the shell's invoke key, so it can't call the Tauri API". Tauri 2.12.2 marks its IPC and invoke key scripts for_main_frame_only, but wry 0.57 says that on Windows "scripts are always added to subframes regardless of the for_main_frame_only option" (wry lib.rs:1029, webview2/mod.rs:506-508, AddScriptToExecuteOnDocumentCreated). So on WebView2 the frame probably has __TAURI_INTERNALS__ and the key. What actually stops it is the opaque origin, the frame policy with no connect-src (guarded by the Rust test) and WebView2's per-frame message routing. The docs should say that. docs/security.md "Content Security Policy" section Open: docs owner
2 should fix No committed test renders type: "html" output and checks that it can't reach the parent or the IPC endpoint. The ui.test only matches the htmlFrame source text; the Altair and Plotly tests only check that nothing is fetched. A test like the reviewer probe would catch an allow-same-origin, a connect-src added to the frame policy, or a new listener in the app page. app/tests/browser Open
3 should fix On Windows (WebView2), nobody here checked that script in the output frame can't call invoke: the frame does get Tauri's scripts, and nbout.localhost counts as a local origin, so the defence rests on WebView2's frame handling. A short check: a %%html cell with <script>__TAURI_INTERNALS__.invoke("check_components").then(r=>document.body.textContent="REACHED", e=>document.body.textContent="refused "+e)</script>, which should not show REACHED. Windows build Open: Chris, FEAT-SEC-02 next action
4 note withGlobalTauri: true stays, so anything that did run in the app's page could call the API. The CSP (script-src 'self') and escaping are the layers in front of it, as security.md:35 says. Notebook Markdown is still rendered with innerHTML in the app's page (app.js:2580), under that CSP. tauri.conf.json:9 Known; outside this item

Documented limitations (only for Accepted with Documented Limitations)#

Limitation Where users are told Backlog item that tracks it
WebRTC isn't covered by a CSP, so a notebook written to leak its own output could send it out that way. docs/security.md (threat table, Content Security Policy section) FEAT-SEC-02 known limitations
The Tauri API is still a page global in the app's own page. docs/security.md:35 HD-020 follow-ups
Not checked under WebView2 on Windows by this review. FEAT-SEC-02 next action ("check under WebView2 on Windows") FEAT-SEC-02; finding 3

Inventory and backlog changes#

  • Features changed: FEAT-SEC-02: add a known limitation that the frame's protection on Windows rests on the opaque origin and the frame policy, not on the frame lacking the invoke key (finding 1). Keep Partially Verified until the Windows check (finding 3).
  • Backlog item state: PB-006 In review → Accepted with Documented Limitations, on the product owner's decision.

Need a hand?

Find a fix in troubleshooting, or share a reproducible issue with the community.

Get support ↗Improve this guide on GitHub ↗

Search documentation

Start typing to search all 160 guides.

Search runs in your browser esc to close