Context
DA-03 (#513) fixed every deletion site in public/sw.js to only ever delete a positively-owned CacheStorage entry — never a cache belonging to another app sharing the qnbs.github.io origin.
chatgpt-codex-connector flagged a related but structurally separate gap in the same file, out of DA-03's declared scope (public/sw.js deletion boundary only): the fetch handler's caches.match() calls are not scoped to a specific owned cache name, e.g.:
public/sw.js — JS/CSS Cache-First strategy: caches.match(request)
public/sw.js — navigation fallback: (await caches.match(request)) || (await caches.match(${BASE}index.html))
CacheStorage is origin-scoped, not path-scoped — so on a shared origin like qnbs.github.io (which hosts multiple independent GitHub Pages projects on different paths), a bare caches.match(request) searches every cache on the origin, not just this app's own CACHE_STATIC/CACHE_DYNAMIC/CACHE_IMAGES. In principle a different project's cached Response for a request whose full URL happens to coincide with one WorldScript Studio fetches could be served here.
Why this is deferred, not a release blocker
- Practical exploitability is low: WorldScript Studio's own asset URLs are prefixed with
/WorldScript-Studio/... and content-hashed by Vite; a collision requires another origin-sibling app to fetch/cache the exact same full URL (realistically only possible for identical externally-hosted resources, not same-origin paths).
- Different mechanism than DA-03: DA-03 is about deleting another app's data (destructive, origin-shared-tenant blast radius). This is about reading a cached response — no data loss, at most a stale/wrong-but-harmless read in an already-narrow window.
- Flagged as codex severity P2.
Suggested fix
Scope every caches.match() / cache.match() call in the fetch handler to an explicit owned cache name ({ cacheName: CACHE_STATIC } etc., or open the specific cache via caches.open(CACHE_X) first, matching the pattern already used elsewhere in the same handler for images/locales/dynamic content).
Scope
public/sw.js fetch handler only. Same "positively-proven ownership" invariant as DA-03, applied to reads instead of deletes.
Context
DA-03 (#513) fixed every deletion site in
public/sw.jsto only ever delete a positively-owned CacheStorage entry — never a cache belonging to another app sharing theqnbs.github.ioorigin.chatgpt-codex-connector flagged a related but structurally separate gap in the same file, out of DA-03's declared scope (
public/sw.jsdeletion boundary only): thefetchhandler'scaches.match()calls are not scoped to a specific owned cache name, e.g.:public/sw.js— JS/CSS Cache-First strategy:caches.match(request)public/sw.js— navigation fallback:(await caches.match(request)) || (await caches.match(${BASE}index.html))CacheStorageis origin-scoped, not path-scoped — so on a shared origin likeqnbs.github.io(which hosts multiple independent GitHub Pages projects on different paths), a barecaches.match(request)searches every cache on the origin, not just this app's ownCACHE_STATIC/CACHE_DYNAMIC/CACHE_IMAGES. In principle a different project's cachedResponsefor a request whose full URL happens to coincide with one WorldScript Studio fetches could be served here.Why this is deferred, not a release blocker
/WorldScript-Studio/...and content-hashed by Vite; a collision requires another origin-sibling app to fetch/cache the exact same full URL (realistically only possible for identical externally-hosted resources, not same-origin paths).Suggested fix
Scope every
caches.match()/cache.match()call in thefetchhandler to an explicit owned cache name ({ cacheName: CACHE_STATIC }etc., or open the specific cache viacaches.open(CACHE_X)first, matching the pattern already used elsewhere in the same handler for images/locales/dynamic content).Scope
public/sw.jsfetchhandler only. Same "positively-proven ownership" invariant as DA-03, applied to reads instead of deletes.