From 8dd367805a0ada4feed6b85cbe7fa4c7f9b1d688 Mon Sep 17 00:00:00 2001 From: Avi Date: Sat, 22 Aug 2026 18:43:05 -0500 Subject: [PATCH] Refresh checkpoint: profile metadata publishing and pictures --- CHECKPOINT-encryption.md | 230 +++++++++++++-------------------------- 1 file changed, 75 insertions(+), 155 deletions(-) diff --git a/CHECKPOINT-encryption.md b/CHECKPOINT-encryption.md index 6c5c5b5..671af43 100644 --- a/CHECKPOINT-encryption.md +++ b/CHECKPOINT-encryption.md @@ -1,4 +1,4 @@ -# Checkpoint — All audit items closed (2026-08-21) +# Checkpoint — Profile metadata publishing + profile pictures (2026-08-22) 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,177 +6,97 @@ verified green at the moment this file was written. ## Where things are - Project: `/home/avi/Projects/0_Nostr` -- Git repo: `master` @ `f7db29e` ("Add SSRF guard, signer queue cap, secret echo, request - timeout"). Before it: `130d7e2` (zeroization), `4461307` (owner-only writes), `4bd7660` - (legacy vault perms), `4d4dfde` (CSP + navigation guards), `6e627a3` (upload tokens), - `4bde395` (IPC allowlist). +- Git repo: `master` @ `a6329d5` ("Publish profile metadata (name + picture) so external + clients show it"). Before it: `db81f8d` (profile deletion with undo), `9a8f334` + (audit checkpoint refresh), and the 2026-08-21 audit-fix commits (`f7db29e`, `d90b6e5`, + `130d7e2`). - Working tree is **clean** apart from this checkpoint update, which is committed right after. -## What was completed: the full 2026-08-21 security audit remediation +## What was completed -All ten findings from the 2026-08-21 security audit are fixed. +**Problem:** profiles created in the app never published a Nostr kind 0 metadata event, +so other clients showed generated petnames ("evil iguana", "homeless leech") or a +truncated npub instead of the user's chosen name. -**#1 IPC method allowlist (`4bde395`, `main.ts` only):** -- `RENDERER_METHODS` set in the Electron main process: the three native methods - (`pick_image`, `link_preview`, `upload_image`) plus every Rust-backend method used by - `frontend/src/lib/api.ts`. -- `isAllowedMethod()` gate at the top of the `backend:request` handler; unknown methods get - `{status:'error', code:'unknown_method'}`, are logged, and never reach the backend. -- No behaviour change for the real UI (only `api.ts` calls `window.backend.request`). +1. **Automatic metadata on creation** — `create_profile` now publishes a kind 0 event + with the label as `name`/`display_name` to all enabled relays (best-effort; relay + failures never block creation). +2. **"Publish name" action for existing profiles** — new button on every Profiles-screen + card plus CLI `publish-name `. Returns a per-relay report shown in the UI. +3. **Profile pictures end-to-end** — + - Vault: optional `picture: Option` per profile (serde default → old vaults + load unchanged). + - Backend: `set_profile_picture` validates http(s) URLs only, stores the URL, and + publishes kind 0 including `picture`; clearing supported (`None`). + - GUI: "Picture" button opens a modal — paste a URL, upload a file via the existing + nostr.build pipeline, or remove; avatar shows the picture everywhere in-app. + - CLI: `set-picture `. +4. **Nested-runtime safety** — metadata publishing runs on a dedicated OS thread with its + own tokio runtime, so both sync (CLI) and async (IPC server) callers are safe. +5. **Finished prior session's delete/undo work** — exposed `undo_history` in + `AppStateView`, fixed invalid Button variants / missing icon / null-safety errors so + the frontend typechecks again. +6. **Test hygiene fix** — `Settings::default()` points at real relays and tests were + silently publishing events to them (one got rate-limited by damus.io). All test suites + now use offline settings; test time dropped from ~126 s to ~3 s. -**#2 Upload pick tokens (`6e627a3`):** -The renderer used to send raw filesystem paths back to main for upload, so a compromised -page could read+publish arbitrary local files to nostr.build. Now: -- `pickImage()` (`main.ts`) mints a random 32-hex-char token per picked file via - `crypto.randomBytes`; tokens map to the file record in `pickedTokens` (main-process memory - only). The renderer receives `{token, name, mime}` — it never sees any path. -- `upload_image` accepts only a valid `token`; the token is consumed (deleted) before the - upload starts, so each pick authorises exactly one upload. Unknown/used/expired tokens - return `{status:'error', code:'unknown_token', message:'That image selection has expired. - Please attach it again.'}` — the user just re-picks the image. -- Renderer updates: `PickedImage.path` → `PickedImage.token` (`types.ts`), `api.uploadImage(token)` - (`api.ts`), context type + callback (`AppProvider.tsx`), `ComposeScreen.onAttach` uses - `image.token`. -- Fake backend mirrors the contract: `upload_image` without a non-empty `token` throws an - `unknown_token` error, so tests exercise the same protocol rule. +## Commits added in this session -**#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 `