Audit round 6 (mobile + --lan): access key replaces trust-by-allowlist
Five blind reviewers over 2936d21; 13 confirmed fixes. The design change: the Host allowlist was CSRF armor being asked to do access control. --lan now mints a per-run access key — the printed URLs carry ?k=..., the first visit sets a cookie — required on EVERY request, reads included (shelf photos and pipeline state are private). That closes DNS-rebinding read exfiltration (GETs were exempt from the old guard), closes any-LAN-device mutations via a forged localhost Host, and frees phones from allowlist accuracy — multi-interface machines, DHCP renewals, and failed IP discovery no longer strand writes. A foreign Origin is still refused even with the key. Guard hardening: Host parsed via url.hostname (ports, IPv6 brackets, case) instead of a manual split; refusals now echo one stderr line (they were invisible at log_level=warning) and the LAN 403 names the remedy; startup warns when no LAN IP could be determined instead of printing hostname-only URLs as if verified. Silent failures: the queue page no longer freezes blank forever when a render throws (LAST was recorded before render; one malformed CSV cell would blank all three ledgers and blame the network) — all three change-detection pages record LAST only after a successful render, and the queue null-guards source_photos. Mobile: touch-size the review/ticket/merge buttons the finger-sized rule lost to on specificity. Style: the meta-cell builder is one shared metaLine() helper; the Help page no longer claims localhost-only; dead -webkit prefix dropped; --lan help text in house style. Tests: token gating (reads and writes, cookie handoff, foreign-Origin refusal), the Origin-present + Host-with-port path every real browser mutation takes (was fully uncovered), run_web_review's lan branch via monkeypatched uvicorn, and a lan_hosts test that actually pins the lowercase/non-empty/v4-only invariants the guard depends on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
2936d21f0a
commit
d4e619611a
@@ -90,7 +90,7 @@ Non-secret knobs (`photos_dir`, `data_dir`, the vision model, the rate limit) li
|
||||
uv run bggpipe web # opens http://127.0.0.1:8377/ — the whole app in the browser
|
||||
```
|
||||
|
||||
The app is localhost-only by default. `--lan` also serves it to your local network — handy for proofreading from the couch or snapping shelf photos on your phone and uploading them straight into the Photos page — but mind the trade: there is no login, so anyone on the network can operate the pipeline. Use it on networks you trust (or use a device VPN like Tailscale against the localhost default instead).
|
||||
The app is localhost-only by default. `--lan` also serves it to your local network — handy for proofreading from the couch or snapping shelf photos on your phone and uploading them straight into the Photos page. It prints a link carrying a per-run access key (`?k=...`): open that exact link on the phone once and a cookie remembers it. The key is the only lock — there is no login behind it — so still prefer networks you trust (or use a device VPN like Tailscale against the localhost default instead).
|
||||
|
||||
Six pages in one local app: **Pipeline** (run stages, watch live output), **Photos** (drag-and-drop upload, gallery, reshoot tickets), **Review** (keyboard-first match and edition decisions), **Titles** (every read off your shelves, alphabetized — and where you proofread them: fix misreads, add cues, split multi-copy lines, remove non-games), **Queue** (exactly what upload will do, plus its full log), and **Library** (your enriched collection, browsable once real BGG data lands). The real upload sits behind a confirmation and behind the stub-data lock. Prefer the terminal? Every stage is also a command, and the two interfaces share all state:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user