feat(pwa): manifest + service worker backend — #86 (backend half)#200
Merged
Conversation
…alf) Serving layer for the installable PWA (Issue #86), built as new files to avoid colliding with the active frontend churn. Security lane owns this surface because its load-bearing properties are security ones. - django_admin_react/pwa.py — ManifestView (`<mount>/web.manifest`) + ServiceWorkerView (`<mount>/sw.js`). * Manifest: anonymous (install prompt is pre-login), request-time computed, NO per-user data — only mount-/header-derived + static fields; theme colours from `Sec-CH-Prefers-Color-Scheme` with a `Vary` header. * SW: served with `Service-Worker-Allowed: <mount>` (scope = mount, never sibling Django views) + `Cache-Control: no-cache` (revalidate so a deploy ships a new SW; not no-store, which would break SW update checks). - templates/admin_react/sw.js — hand-rolled SW (no Workbox / no new npm dep) honoring the contract: scope ≤ mount (pass-through otherwise), never cache non-GET (mutation safety), never cache a `no-store` response (the package's API reads stay uncached), and a `dar:purge` message handler for cache-on-logout (read payloads can't outlive the session). Mount injected via `escapejs` (crafted-path safe). - conf.py — optional PWA_NAME / PWA_SHORT_NAME / PWA_ICONS (sane zero-config defaults). - urls.py — `web.manifest` + `sw.js` routes before the SPA catch-all. Tests (tests/test_pwa.py, 8): manifest anonymous + no-per-user-data + mount + theme-hint + short_name override; SW scope header + no-cache + embeds mount + no-store/mutation/purge guards. All pass. Frontend follow-up (needs browser verification): SW registration in main.tsx + the install-prompt affordance + the logout `dar:purge` postMessage. The cache-on-logout *contract* is enforced server-side here via the SW; the SPA hook that fires it is the frontend slot. Tier 5 — adds top-level URL patterns + conf defaults. Human review welcome. Backend half of #86; read/UX contract is docs/ux/pwa.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
f86c8da to
6c235b4
Compare
| }); | ||
|
|
||
| // --- cache purge on logout (contract §5) ---------------------------------- | ||
| self.addEventListener('message', (event) => { |
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
… js/missing-origin-check) (#208) The PWA service worker's `message` handler (the `dar:purge` cache-on-logout hook, #200) processed messages without verifying the sender origin — CodeQL `js/missing-origin-check` (medium). A cross-origin frame must never be able to drive the SW cache. Add `if (event.origin && event.origin !== self.location.origin) return;` so only same-origin clients (the SPA pages this worker controls) can trigger a purge. Same-origin internal `client.postMessage` (empty origin) is still accepted; anything cross-origin is dropped. This is the one open CodeQL alert on main (the other 10 are fixed via #191/#193). Clears it → 0 open. Test asserts the served SW embeds the origin check. Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez
pushed a commit
that referenced
this pull request
May 26, 2026
Ships the batch merged since 0.2.0a2: - SECURITY: SW message-handler origin check (#208, CodeQL js/missing-origin-check) — the artifact's service worker now rejects cross-origin cache-control messages. - PWA backend serving (#200), React login form end-to-end (#190), create/Add form completing CRUD (#199), autocomplete FK widget (#207), date_hierarchy drill-down (#205), action-label + list cosmetics fixes (#203/#204), lint cleanup (#202). 368 tests pass. Tier 6 — version bump; publish via the Security deploy-gate under the standing "deploy regularly if secure" directive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
Ships the batch merged since 0.2.0a2: - SECURITY: SW message-handler origin check (#208, CodeQL js/missing-origin-check) — the artifact's service worker now rejects cross-origin cache-control messages. - PWA backend serving (#200), React login form end-to-end (#190), create/Add form completing CRUD (#199), autocomplete FK widget (#207), date_hierarchy drill-down (#205), action-label + list cosmetics fixes (#203/#204), lint cleanup (#202). 368 tests pass. Tier 6 — version bump; publish via the Security deploy-gate under the standing "deploy regularly if secure" directive. Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
…ce the original pass (#216) docs/threat-model.md §4 STRIDE'd only the original 6 endpoint groups (registry/list/detail/create-update/delete/shell) with pre-merge "lands in PR #N" language. The surface has grown a lot since; this adds STRIDE coverage (§4.7–4.16) for every endpoint added after, citing the real mitigations + their tests: - 4.7 login/logout (#168/#190) — generic-403 no-enumeration, CSRF, policy-before-session, session-fixation, no password logging. - 4.8 autocomplete (#97) — target-model view gate, no cross-model leak. - 4.9 actions runner (#101) — whitelist via get_actions, never getattr. - 4.10 bulk PATCH (#103) — per-row perms, cap, per-row LogEntry. - 4.11 history (#158/#162) — object-view gate, change-message field-name note, the LogEntry get-queryset-rule exception. - 4.12 delete-preview (#164) — delete-perm gate, counts-only (< HTML admin). - 4.13 inline writes (#183) — per-row-state gates, atomic rollback, deny-by-default unknown inline, generic-400 (no str(exc)). - 4.14 panel hook (#111) — declared-panels-only resolution. - 4.15 schema (#108) — staff-gated static envelope schema. - 4.16 PWA manifest+SW (#86/#200/#208) — no per-user manifest data, scope ≤ mount, no-store honored, cache-on-logout, origin check. Keeps the security doc current with the shipped wire surface — an audit-readiness must for a public security-sensitive package. Docs-only (Tier 1). Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 26, 2026
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
…86 frontend) (#219) The PWA backend (manifest + hand-rolled SW serving) shipped in #200, but nothing linked the manifest or registered the SW, so the browser never offered "Install" and the SW never activated. This wires the frontend half. - `index.html` template: `<link rel="manifest" href="{{ mount_point }}web.manifest">` + `theme-color` / `apple-mobile-web-app-*` meta so the browser's install heuristics fire. - `main.tsx`: register `<mount>sw.js` scoped to the mount on `load`. Best-effort (offline/install is progressive enhancement); the SW itself honors `no-store` so authenticated reads are never cached, and the mount scope is permitted by the backend's `Service-Worker-Allowed: <mount>` header. Install-prompt affordance + logout cache-purge (`dar:purge`) are the remaining follow-ups on #86. Test: `test_shell_links_pwa_manifest` asserts the shell links the manifest + theme-color. Typecheck green (7 pkgs); build ok; prettier + ruff + black clean; 15 spa-index tests pass. Tier 4 (web) + the template (Tier 3-ish, no logic). Self-merging under the repo-owner's full-tier authorization. Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
* chore(release): bump to v0.2.0a4 — sixth PyPI alpha Ships the parity batch merged since 0.2.0a3: - Inline editing in the SPA (#223) + inline field-type metadata (#220). - PWA end-to-end: SW registration + manifest link + install affordance (#219, #222) on top of the backend serving (#200). - Navigable ForeignKey links in list + detail (#215, #217) — `to` emitted only for registered models (anti-adjacency-leak, #89 posture). - List column customizer (#214), portal Modal (#221), threat-model refresh (#216). Security review of the batch (issue #225): code is secure + documented; one low-risk incomplete control (cache-purge-on-logout not yet wired frontend-side — moot under the no-store read posture) is tracked. CodeQL 0, Dependabot 0, 374 tests pass. Tier 6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(build): sync pnpm-lock.yaml with apps/web package.json (react-dom/@types/react-dom) A recent PR (#221/#222) added `react-dom` + `@types/react-dom` to `frontend/apps/web/package.json` but didn't regenerate the lockfile, so `pnpm install --frozen-lockfile` (the build + CI path) fails on `main`: specifiers in the lockfile (...) don't match specs in package.json Regenerated with `pnpm install --lockfile-only` (no version changes — only adds the already-declared specifiers to the lock). Frozen install now passes. Release-blocking build-integrity fix found while cutting 0.2.0a4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 26, 2026
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.
Serving layer for the installable PWA (#86), built as new files (low collision with the frontend churn). Security lane owns it: anonymous manifest with no per-user data, SW served with
Service-Worker-Allowed: <mount>(scope ≤ mount), hand-rolled SW (no Workbox) honoringno-store+ mutation-safety + cache-on-logout (dar:purge). 8 tests pass. Frontend SW registration + install affordance are the follow-up (browser-verified). Tier 5.