fix: 404 asset-looking paths instead of the text/html loader shell#3
Closed
MichaelTaylor3d wants to merge 1 commit into
Closed
fix: 404 asset-looking paths instead of the text/html loader shell#3MichaelTaylor3d wants to merge 1 commit into
MichaelTaylor3d wants to merge 1 commit into
Conversation
A site served via *.on.dig.net that ships its own service-worker.js (e.g.
chatfish.on.dig.net) could not register it: the browser rejected it with
`SecurityError: The script has an unsupported MIME type ('text/html')`.
Root cause: a browser's service-worker REGISTRATION fetch (and ES-module
imports) BYPASS the page's controlling loader service worker and hit the
resolver origin directly. The resolver's catch-all served the branded loader
shell (`text/html`) for EVERY path, so `/service-worker.js` came back as an
HTML page — the wrong MIME. The loader SW's own `contentType()` map already
serves `.js`/`.mjs` as `text/javascript`; the defect was purely the Lambda's
SPA-fallback masquerading an asset path as the HTML shell.
Fix: the resolver now classifies a request whose final path segment carries a
known non-HTML file extension as a static asset and answers it with an honest
404 (`text/plain`, `nosniff`, `no-store`) — never the loader shell. The
SPA-fallback shell is served only for navigation/HTML routes (no extension, or
`.html`/`.htm`), so an SPA client-side deep-link still boots the loader, while a
route that merely contains a dot (`/user/john.doe`) stays a navigation route.
The 404 fails SW registration cleanly WITHOUT evicting the loader SW (a browser
installs a SW only from a 2xx script), so store content keeps decrypting.
The resolver cannot decrypt an encrypted store asset, so it cannot serve a
site's real service-worker.js bytes; and the loader SW already owns scope `/`
(it IS the content-decrypting mechanism), so a site-provided service worker is
structurally unsupported on *.on.dig.net. A clean 404 replaces the misleading
SecurityError. Assets the loader SW does serve (in-store subresources on a
controlled page) keep their correct extension-derived Content-Type.
Ordering: the `/__dig/*`, `/__dig_sw.js`, and `/__dig/config.json` internal
routes are matched before the asset-404 branch, so config.json (`.json`) and
the baked JS/WASM assets are unaffected.
Tests: `is_static_asset_path` classifies service-worker.js / sw.js / .mjs /
css / json / wasm / images as assets and navigation routes (incl. dotted SPA
routes) as non-assets; the sw.js `contentType()` regression asserts
service-worker.js / sw.js / app.min.mjs resolve to a JS MIME, never text/html.
Refs DIG-Network/dig_ecosystem#144
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What / why
A site served via
*.on.dig.netthat ships its ownservice-worker.js(live case:chatfish.on.dig.net, a dotnet SPA) could not register it — the browser rejected it with:Root cause (SPA-fallback, not the MIME map). A browser's service-worker registration fetch (and ES-module
imports) bypass the page's controlling loader service worker and hit the resolver origin directly. The resolver's catch-all served the branded loader shell (text/html) for every path, so/service-worker.jscame back as an HTML page → wrong MIME. The loader SW's owncontentType()map already serves.js/.mjsastext/javascript; the defect was purely the Lambda masquerading an asset path as the HTML shell. (FixesDIG-Network/dig_ecosystem#144.)Fix
The resolver now classifies a request whose final path segment carries a known non-HTML file extension (
js mjs css json wasm map svg png … woff2 …) as a static asset and answers it with an honest 404 (text/plain,nosniff,no-store) — never the loader shell. The SPA-fallback shell stays for navigation/HTML routes only (no extension, or.html/.htm), so:/user/john.doe) stays a navigation route (only KNOWN asset extensions match);/service-worker.jsgets a clean 404 that fails registration without evicting the loader SW (a browser installs a SW only from a 2xx script) — so store content keeps decrypting, and the misleadingSecurityErroris gone.The resolver cannot decrypt an encrypted store asset (and the loader SW already owns scope
/— it is the content-decrypting mechanism), so a site-provided service worker is structurally unsupported on*.on.dig.net; a clean 404 is the honest answer. Assets the loader SW does serve (in-store subresources on a controlled page) keep their correct extension-derived Content-Type.Ordering: the
/__dig/*,/__dig_sw.js, and/__dig/config.jsoninternal routes are matched before the asset-404 branch, soconfig.json(.json) and the baked JS/WASM assets are unaffected.Tests (regression, TDD)
is_static_asset_path(Rust lib):service-worker.js/sw.js/firebase-messaging-sw.js/.mjs/.css/.json/.wasm/ images → assets (→ 404);/,/about,/index.html, dotted SPA routes (/user/john.doe,/v1.2) → navigation (→ shell). 44 lib tests pass.sw.jscontentType()(Node):service-worker.js/sw.js/app.min.mjsresolve to a JS MIME, nevertext/html. 20 JS tests pass.Verification
cargo fmt --check,cargo clippy --all-targets -- -D warnings,cargo clippy --bin bootstrap --features aws -- -D warnings,cargo test --all-targets(44 pass),node --test test/sw.test.mjs(20 pass) — all green locally.Blast radius
is_static_asset_pathis a new pure function; the only caller is the resolverhandle()catch-all inbootstrap.rs, inserted after the internal/__dig/*+config.jsonroutes. No cross-repo contract changes: the sharedcontentType()map (byte-identical with hubembed-core.ts/dig-embed.js) is unchanged; SYSTEM.md's exposed on.dig.net asset routes are unchanged; docs.dig.net documents only the on.dig.net handle model, not asset MIME serving.Security posture
Unchanged. NO WAF, verify-then-decrypt fail-closed, and the loader/static-page CSPs are untouched. The change only converts a mislabeled
text/htmlasset response into an honest,nosniff404 — it never weakens content isolation.Version
0.3.0→0.3.1(patch — a backwards-compatible bug fix, no API/behaviour change for navigation routes).