diff --git a/BUILD-LOG.md b/BUILD-LOG.md index 4d3187cf5..8f5c53123 100644 --- a/BUILD-LOG.md +++ b/BUILD-LOG.md @@ -5358,3 +5358,5 @@ Gander: after Duckworth-0100 design approval with C1-C5, prepared the two host-s 2026-10-22T23:13:27Z | Gyro Q195 O2 fenton-0007 F7/F8 | Named human-only editor.saveGroupPauseMs=2000 and editor.saveGroupMaxMs=15000 with integer/range/cross-field validation and W3a UI/CLI refusal tests. W3b owns production adoption refusal until W4b; isolated fixture conversion remains testable without request-flag bypass. W4b owns adoption module/API tests and reviewed lifting only with committed-reader/citation readiness and, if Q36 approves, the separate notice row PASS. Q36 unresolved stays closed. Static ten-brief, setting-key/bounds, refusal/lift ownership and two-question checks pass. Docs only; no build, browser/worker run, product/queue/live change. Fenton checks F7/F8 on next push. 2026-10-22T23:13:48Z | Gyro mos-1359 / r3 push guard | D13 records save-group timing as Mos's accepted reading of Q226 F41, not Jason's ruling; both defaults remain human-only settings. Push guard identity gyro/purpose push: exit 0 documented degraded-pass, CANNOT_ASSERT credential-unresolvable; audit gyro-q195-ci-queue-audit.jsonl, no credential fallback or CI-green claim. + +2026-10-22T23:58:36Z | Fenton Q195 O2 S0 (#1798, sage-3058 under mos-1360) | Before: partial inventory b7daf5198, no verdict. After: CONDITIONAL PASS on WordCraft e05b7fe8. Inventory 15 local / 199 normal / 10 build-only / 25 build scripts / 10 proc-macros, 3 git deps by commit. Licenses resolved by a notice bundle (L1: epaint fonts missing from WordCraft NOTICE; self_cell and ryu elect Apache-2.0). Findings F1-F15 (F1 wuff WOFF1 orig_length allocation trap reachable from DOCX open, source only), mandatory mitigations M1-M14 with rejection tests. Browser on the copied artifact: style-src self 1 violation (rfd inline style, removed by host-owned Open), style-hash 0 violations, all functions run; Print blob iframe and cross-origin frame navigation blocked. D8 hash fallback returned to Mos, not recommended. Corrections: style-hash attempt 1 had a harness-made connect-src violation (rerun clean); Print evidence copied from the transcript. No compile, no Console/policy/live change, loopback-only fixtures. Filbert reviews. diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index e032a2071..f8a2d618f 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -1552,3 +1552,4 @@ are never rewritten or removed; corrections are new entries. 2026-10-22T23:03:39Z | Gyro (Codex gpt-6.1-sol, mos-1356) | Q195/Q231/Q233 O2 design rewrite, gyro/editor-o2 from c4f77373f | Canonical queue next gyro: nothing. Ten docs-only briefs; full-feature WordCraft, independent 15-crate/199-package security review, editor-only CSP candidate, instant git DOCX saves and retained Markdown/wiki sources, committed-only DOCX citation gate, no agent story prose. Static structure/source-anchor/scope checks only; no code, build/run, queue or live change. Fenton review requested after push. 2026-10-22T23:09:38Z | Gyro (Codex gpt-6.1-sol, fenton-0006) | Q195 O2 design corrections on gyro/editor-o2 | F1 requirement boundary sent to Mos, adoption gated; F2-F6 source-checked docs fixes prepared. Ten briefs and two open owner items checked statically. No implementation, build/browser/worker run, queue or live action; Fenton re-check after push. 2026-10-22T23:13:27Z | Gyro (Codex gpt-6.1-sol, fenton-0007) | Q195 O2 r3 F7/F8 | Save-group keys/defaults/validation and W3b refusal/W4b lift ownership explicit. Static docs checks only. Q36 remains open; no implementation or gate lift. Direct Fenton/Mos handoff after push. +2026-10-22T23:58:36Z | Fenton (Claude Opus 5.5, sage-3058) | Q195 O2 S0 WordCraft source security and CSP review, fenton/q195-s0 | CONDITIONAL PASS, M1-M14 mandatory, D8 style-src hash held for Mos. Reviews docs only; Filbert review requested. diff --git a/docs/reviews/2026-10-22_q195-editor-csp.md b/docs/reviews/2026-10-22_q195-editor-csp.md new file mode 100644 index 000000000..7b3274d3d --- /dev/null +++ b/docs/reviews/2026-10-22_q195-editor-csp.md @@ -0,0 +1,181 @@ +# Q195 O2 S0: editor frame CSP measurement + +Status: COMPLETE. Feeds the S0 verdict in `2026-10-22_q195-wordcraft-security.md`. + +- Review id `s0-20261022`, Gitea #1798. Owner: Fenton. Reviewer: Filbert. +- Spec: `docs/plans/2026-10-21_q195-writing-editor.md` blob `322ec50f` at e36bce352, D7 and D8. +- Scratch: `/mnt/storage/scratch/q195-csp-review/s0-20261022/` (called R below). Receipts are in + `R/receipts/`. +- Nothing was compiled. No Console code, policy or live stack was touched. The harness is a + loopback-only stand-in server with fixture data, and the browser had no external network use: + every request in every run went to 127.0.0.1. + +## What was measured + +The unmodified WordCraft 0.9.0 web build, framed same-origin under a stand-in shell that sends +the Console shell CSP exactly. Three candidate frame policies differ only in `style-src`. + +### Artifact + +Copied from Louie's build (origin/louie/wordcraft-eval 880ef19e0); each hash matches Louie's receipt. + +| File | SHA-256 | +|---|---| +| index.html (not served; reference only) | `361c9ec8eda95afa7483fef1f6e95270a199cfe59591285096bf22cb06bfef43` | +| wordcraft-fa59e09a3f921e03.ico | `53a23dcf66260f44d87d4d143d633395f3e967f391eda47e6e9cc3a52f820225` | +| wordcraft-web-88f7089f25a3b338.js | `866a1de6a33eff2ded8ae9588fd049aeacfc42557a3863ccd2107b3faf4d691e` | +| wordcraft-web-9bda74d1c7bc6623_bg.wasm | `90dec375924b47b40054eb4ecafb4235b6ed0bd63880b91ec138fdbb6f939e75` | + +### Wrapper and harness (mine, scratch only) + +| File | SHA-256 | +|---|---| +| www/frame.html | `6a8d141fa8b073cff73fa666fdf030a9f91030924e74a6aa0a441d76a5583f82` | +| www/frame-bootstrap.js | `32c8f71acfe14f3bdc4d31534ed3a84609fc4fb1fb8a1a4a96f56eb28f39d3d4` | +| www/frame.css | `629eb32e9cdf6b97f0ce79f841419ab4a80d42f322c10948ec5d23198799111e` | +| www/shell.html | `cf94f1ce359e8b90f86d39cdef98894d7874e97da9cf7eb0808b0f6e038b3bac` | +| www/shell.css | `372716168beb22dd9fbc7e3d45a76bc06c1b3b1d8f59807a187700689d9ff38c` | +| harness/serve.py | `ad34aed5cdf49a77128778f5f6523698d35f5a6eca771e1dbe53941f09fabe80` | +| harness/instrument.js | `028e914a994a518043dab0b834a9917fc67bc35730f9e3725b59ebd8fa8fb0fa` | +| fixtures/s0-full-feature.docx (9985 B) | `b40eb5875518701d10de1a5419931f02e10c378560fbd5ab6a2ed68d19be53f4` | + +The frame wrapper loads the wasm-bindgen module from `frame-bootstrap.js`; it has no inline +script or inline style. The fixture is generated by `harness/make_fixture.py`. It carries +two sections (portrait, then landscape), a header and footer, headings, a table, a small PNG, an +external hyperlink to `https://example.invalid/` and wikilinks. It has no embedded font and no +real writing. + +Browser: Chrome 150.0.0.0, Linux x86_64, driven through DevTools MCP isolated contexts. WebGPU +reported "No available adapters", so wgpu ran on its GL backend. + +## Headers + +Shell (stand-in for `serve.mjs:605`, unchanged, no SSO), sent on `/shell.html`, `/shell.css`, +assets, fixtures and every 404/405: + +``` +default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self' +``` + +Frame, sent only on `/editor/wordcraft/frame`: + +``` +default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src ; connect-src 'self'; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; frame-src 'none'; form-action 'none' +``` + +| Variant | `` | +|---|---| +| style-self | `'self'` | +| style-hash | `'self' 'sha256-9tSqG2Th9gNn6sbn1ZYMSqakJLznxnsamRDeigez2Mo='` | +| style-inline | `'self' 'unsafe-inline'` (defined, not run: the first two answered the question) | + +Every response also carries `x-content-type-options: nosniff`, `referrer-policy: no-referrer` +and `cache-control: no-store`. Routes are fixed (`/shell.html`, `/shell.css`, +`/editor/wordcraft/frame`, five allowlisted asset names, `/fixtures/`). Anything else is a +404, and every non-GET method is a 405. Each request and its exact headers are logged to +`requests-.jsonl`. + +Note on the shell header: the stand-in shell sends `frame-ancestors 'none'` as Console does. +The frame is framed by the shell, not the other way round, so this does not affect the +measurement. The real adapter's frame route sends the frame header above. + +## Results + +| Run | Port | CSP violations | Functions | Receipts | +|---|---|---|---|---| +| style-self full pass | 7391 | **1**: `style-src-elem`, blocked `inline`, js line 93 | all | `full-style-self-events.json`, `requests-style-self.jsonl` | +| style-hash clean pass | 7392 | **0** | all | `clean-style-hash-events.json`, `requests-style-hash.jsonl` | +| frame navigation test | 7392 | 1 in the shell, intended: `frame-src`, enforce | n/a | `browser-console-network.txt` | +| Print probe | 7392 | 1, intended: `frame-src`, blocked `blob` | Print blocked | `print-probe.json` | + +Each full pass made exactly 8 requests: the 8 routes, all from 127.0.0.1, all 200. The fixture +fetch is one of them. No other origin was contacted. + +The one style-self violation fired between the file-picker click and the fixture feed. Source: +rfd 0.17.2 `src/backend/wasm.rs:127-129` appends its `wasm/style.css` as a `