Refresh checkpoint with CSP and navigation guards

This commit is contained in:
Avi 2026-08-21 13:54:52 -05:00
commit ac614072fe

View file

@ -1,4 +1,4 @@
# Checkpoint — Upload pick tokens (2026-08-21)
# Checkpoint — Header-based CSP + navigation guards (2026-08-21)
A stopping point you can return to if this session is closed. Everything below was
verified green at the moment this file was written.
@ -6,12 +6,12 @@ verified green at the moment this file was written.
## Where things are
- Project: `/home/avi/Projects/0_Nostr`
- Git repo: `master` @ `6e627a3` ("Replace upload file paths with single-use pick tokens").
Before it: `4bde395` ("Restrict renderer IPC to an explicit method allowlist"), then
`dafed33` (checkpoint refresh), on top of contact-aware feed (`a1445d1`) etc.
- Git repo: `master` @ `4d4dfde` ("Enforce header-based CSP and block window open/navigation").
Before it: `6e627a3` (upload pick tokens), `4bde395` (IPC allowlist), then checkpoint
commits, on top of contact-aware feed (`a1445d1`) etc.
- Working tree is **clean** apart from this checkpoint update, which is committed right after.
## What was completed: security hardening items #1 and #2
## What was completed: security hardening items #1, #2 and #3
Fixes from the 2026-08-21 security audit.
@ -39,8 +39,25 @@ page could read+publish arbitrary local files to nostr.build. Now:
- Fake backend mirrors the contract: `upload_image` without a non-empty `token` throws an
`unknown_token` error, so tests exercise the same protocol rule.
**#3 Header-based CSP + navigation/window guards (`4d4dfde`):**
- The static CSP meta tag was **removed** from `frontend/index.html` and replaced with
response headers stamped by `main.ts` (`onHeadersReceived`, mainFrame only):
- `CSP_PROD` for `app://` pages: `script-src 'self'`**no `'unsafe-inline'`**, so an
injected `<script>` cannot execute. `connect-src 'self'` (all real network goes through
the backend/main process); img-src keeps `https:` for remote feed/preview images.
- `CSP_DEV` when `NOSTR_GUI_DEV_URL` matches: keeps `'unsafe-inline'` (Vite's React-refresh
preamble is an inline script) but pins `connect-src` to localhost instead of the old
wide-open `ws:` — HMR still works.
- The meta had to go because it would also have blocked the dev preamble; headers let us
ship strict-prod / workable-dev from one HTML file.
- `will-navigate`: any navigation away from app:// or the dev origin is blocked; http(s)
targets open in the system browser via `shell.openExternal`.
- `setWindowOpenHandler`: all `target="_blank"` popups are denied; external links go to the
system browser. Unknown schemes are denied without opening anything.
## Commits
- `4d4dfde` "Enforce header-based CSP and block window open/navigation" — main.ts + index.html.
- `6e627a3` "Replace upload file paths with single-use pick tokens" — main.ts, api.ts,
types.ts, AppProvider.tsx, ComposeScreen.tsx, fakeBackend.ts (+62/27).
- `4bde395` "Restrict renderer IPC to an explicit method allowlist" — main.ts (+49 lines).
@ -65,6 +82,8 @@ Manual protocol checks worth doing once:
- DevTools console: `window.backend.request('not_a_method')``unknown_method` envelope (#1).
- DevTools console: `window.backend.request('upload_image', {path:'/etc/passwd'})`
`unknown_token` error; no file is read (#2). Real attachment flow works unchanged.
- DevTools console: `document.cookie` / injected `<script>` does not run; Application tab
shows the CSP header on the app:// document (#3). Link previews open in the system browser.
## How to resume
@ -78,13 +97,10 @@ Manual protocol checks worth doing once:
Remaining audit items in priority order:
1. **CSP** — drop `'unsafe-inline'` from `script-src` in `frontend/index.html`; add
`setWindowOpenHandler(() => ({action:'deny'}))` + route external links through
`shell.openExternal` in `main.ts`.
2. **Legacy vault perms** — repo-root `profiles_vault.json` is group-readable (0664);
1. **Legacy vault perms** — repo-root `profiles_vault.json` is group-readable (0664);
chmod 600 / delete after confirming migration; auto-tighten during migration in `src/vault.rs`.
3. **Write-race fixes** — create files with mode 0600 at creation time in `vault.rs`
2. **Write-race fixes** — create files with mode 0600 at creation time in `vault.rs`
(`write_restricted`, `backup_file`).
4. **Zeroize** — wipe decrypted key material via the `zeroize` crate.
5. Smaller: link-preview SSRF guard (block loopback/private IPs), signer pending-cap,
3. **Zeroize** — wipe decrypted key material via the `zeroize` crate.
4. Smaller: link-preview SSRF guard (block loopback/private IPs), signer pending-cap,
NIP-46 secret echo check, backend request timeout.