fix: bring index.html level with the deployed copy #4

Merged
jlxq0 merged 1 commit from fix/index-drift into main 2026-08-26 05:10:40 +00:00
Owner

Eleven of the twelve runtime files here were already byte-identical to oddie-apps/static-sites/sites/www.lindner.earth/. index.html was the twelfth, 8,638 bytes against 9,516 live, and the whole difference was the Plausible tag (252859d, 2026-08-12) and ?v=1 cache-busting (599e711, 2026-07-12). Copying the deployed file back makes it byte-identical, so the hazard is removed rather than documented: #2 put a warning in AGENTS.md, and someone who does not read AGENTS.md was still one cp from silently deleting analytics from production.

Consequences of the port

  • The bundle-loading test asserted src="./js/xq.js" with no query, so the cache-busting broke it. Widened to accept ?v=<n>; it still rejects src/xq.js and _xq_unscrambled.js.
  • index.html now loads a script from stats.oddie.app, which this repository's Caddyfile CSP did not allow. Added to script-src and connect-src. The result is directive-for-directive identical to what www.lindner.earth actually serves, diffed against the live response header.

New test, and the review that fixed it

Every absolute script origin index.html loads must be permitted by the Caddyfile script-src. That is the check that was missing; the drift it catches is exactly the one this PR repairs.

The first version matched substrings. codex exec --sandbox read-only named three ways that is not what CSP means, all three real, all three fixed by matching source-list tokens: 'self' resolves against the canonical URL, https://*.host matches a subdomain and not the bare host, and an allowed origin is compared as an origin.

Proved it can fail, and proved it does not fail where CSP permits

One mutation each, restoring between:

mutation result
<script src='https://evil.example/a.js'> 23/24
<script src = "https://evil.example/a.js"> 23/24
<script src=https://evil.example/a.js> 23/24
script-src carrying only https://stats.oddie.app.evil 23/24, "script-src must allow https://stats.oddie.app"
absolute same-origin https://www.lindner.earth/a.js under 'self' 24/24
https://*.oddie.app covering the loaded stats.oddie.app 24/24

The first three passed against the substring version. They are the regressions the review caught, not hypotheticals.

The connect-src half is not mechanically checked and its failure is silent, so it is in Known Pitfalls instead.

npm test: 24 passing. npm run check: bundle current.

Refs #1

Eleven of the twelve runtime files here were already byte-identical to `oddie-apps/static-sites/sites/www.lindner.earth/`. `index.html` was the twelfth, 8,638 bytes against 9,516 live, and the whole difference was the Plausible tag (`252859d`, 2026-08-12) and `?v=1` cache-busting (`599e711`, 2026-07-12). Copying the deployed file back makes it byte-identical, so the hazard is removed rather than documented: #2 put a warning in `AGENTS.md`, and someone who does not read `AGENTS.md` was still one `cp` from silently deleting analytics from production. ### Consequences of the port - The bundle-loading test asserted `src="./js/xq.js"` with no query, so the cache-busting broke it. Widened to accept `?v=<n>`; it still rejects `src/xq.js` and `_xq_unscrambled.js`. - `index.html` now loads a script from `stats.oddie.app`, which this repository's `Caddyfile` CSP did not allow. Added to `script-src` and `connect-src`. The result is directive-for-directive identical to what `www.lindner.earth` actually serves, diffed against the live response header. ### New test, and the review that fixed it Every absolute script origin `index.html` loads must be permitted by the `Caddyfile` `script-src`. That is the check that was missing; the drift it catches is exactly the one this PR repairs. The first version matched substrings. `codex exec --sandbox read-only` named three ways that is not what CSP means, all three real, all three fixed by matching source-list tokens: `'self'` resolves against the canonical URL, `https://*.host` matches a subdomain and not the bare host, and an allowed origin is compared as an origin. ### Proved it can fail, and proved it does not fail where CSP permits One mutation each, restoring between: | mutation | result | |---|---| | `<script src='https://evil.example/a.js'>` | 23/24 | | `<script src = "https://evil.example/a.js">` | 23/24 | | `<script src=https://evil.example/a.js>` | 23/24 | | `script-src` carrying only `https://stats.oddie.app.evil` | 23/24, "script-src must allow https://stats.oddie.app" | | absolute same-origin `https://www.lindner.earth/a.js` under `'self'` | 24/24 | | `https://*.oddie.app` covering the loaded `stats.oddie.app` | 24/24 | The first three passed against the substring version. They are the regressions the review caught, not hypotheticals. The `connect-src` half is not mechanically checked and its failure is silent, so it is in Known Pitfalls instead. `npm test`: 24 passing. `npm run check`: bundle current. Refs #1
Eleven of the twelve runtime files in this tree were already byte-identical to
`oddie-apps/static-sites/sites/www.lindner.earth/`. `index.html` was the twelfth,
8,638 bytes here against 9,516 live, and the whole of the difference was two
changes made only in `static-sites`: the Plausible tag (`252859d`, 2026-08-12)
and `?v=1` cache-busting on the css and js references (`599e711`, 2026-07-12).

Copying the deployed file back makes it byte-identical, so the hazard is removed
rather than documented. AGENTS.md warned about it; someone who does not read
AGENTS.md was still one `cp` away from silently deleting analytics from
production.

Two consequences of the port:

- The bundle-loading test asserted `src="./js/xq.js"` with no query, so the
  cache-busting broke it. Widened to accept `?v=<n>`; it still rejects
  `src/xq.js` and `_xq_unscrambled.js`.
- index.html now loads a script from `stats.oddie.app`, which this repository's
  Caddyfile CSP did not allow. Added to `script-src` and `connect-src`. The
  resulting policy is directive-for-directive identical to the one
  `www.lindner.earth` actually serves, diffed against the live response header.

New test: every absolute script origin index.html loads must be permitted by the
Caddyfile `script-src`. That is the check that was missing, and the drift it
catches is exactly the one this commit repairs.

The first version of it matched substrings, and `codex exec --sandbox read-only`
named three ways that is not what CSP means. All three were real and all three
are fixed by matching source-list tokens instead: `'self'` resolves against the
canonical URL, `https://*.host` matches a subdomain and not the bare host, and an
allowed origin is compared as an origin rather than as a substring.

Proved it can fail, and proved it does not fail where CSP would permit. One
mutation each, restoring between:

- single-quoted `<script src='https://evil.example/a.js'>`   -> 23/24
- spaced `<script src = "https://evil.example/a.js">`         -> 23/24
- unquoted `<script src=https://evil.example/a.js>`           -> 23/24
- `script-src` carrying only `https://stats.oddie.app.evil`   -> 23/24,
  "script-src must allow https://stats.oddie.app"
- absolute same-origin `https://www.lindner.earth/a.js` under `'self'` -> 24/24
- `https://*.oddie.app` covering the loaded `stats.oddie.app` -> 24/24

The first three all passed against the substring version, so they are the
regressions the review caught rather than hypotheticals.

The `connect-src` half is not mechanically checked and its failure mode is
silent, so it is written into Known Pitfalls instead.

npm test: 24 passing. npm run check: bundle current.

Refs #1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Pv3j5e5WCepnztyVSEcUJ
jlxq0 merged commit 9b65ef38de into main 2026-08-26 05:10:40 +00:00
jlxq0 deleted branch fix/index-drift 2026-08-26 05:10:41 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/lindner_web!4
No description provided.