Repository navigation
Description
Problem
Developer undo history persists whole-file snapshots as JSON in <config>/developer_undo/<workspace-hash>.json. FileHistory::persist creates the directory with std::fs::create_dir_all and writes with std::fs::write, without explicitly protecting the directory or file. With an ordinary 022 umask these become 0755 directories and 0644 files on Unix.
If the containing home/config tree is traversable by another local account, that account can read the snapshots, including text a researcher edited and later removed from the working file. Protecting the session database directory in #445 does not protect this separate snapshot location. This finding concerns confidentiality of app-created undo snapshots; it does not assume root access, compromise of the owner account, or any real participant/patient data.
Controlled reproduction
Use only synthetic content in a temporary test profile:
- Set
BIOROUTER_PATH_ROOTto a temporary profile whose ancestors are traversable; retain the ordinary022umask. - Create a workspace text file containing
SYNTHETIC_UNDO_CANARY. - Edit that file using Developer
text_editorso its prior content is snapshotted. - Inspect permissions of
<temporary-profile>/config/developer_undoand its JSON snapshot. The current writer supplies no explicit mode, so the expected ordinary result is0755/0644. - In a controlled Linux two-UID fixture, try reading the synthetic JSON as a second unprivileged UID. Access should be denied after remediation.
No existing user snapshot content needs to be read to reproduce or verify the problem.
Recommended remediation, preserving convenience
- Keep automatic undo and the existing tool names/workflow. Make privacy a backend storage property, without routine new confirmation dialogs.
- Create/open the app-owned undo directory through a bound directory handle; require the current owner and protect it as
0700on Unix. Refuse links and insecure/non-owned destinations. - Atomically publish new snapshot files with owner-only access (
0600) from the first creation, including temporary publication files. Preserve the private access controls on subsequent replacement. - Secure existing history directory/files before reading or retaining them; do not copy history through a shared temporary folder. Existing legitimate history should remain usable.
- Account for inherited macOS ACLs and Windows protected owner-only DACLs; Unix mode bits alone do not establish ACL parity. Fail closed with an actionable message or private in-memory undo if the filesystem cannot provide the promised protection.
- Keep target-file snapshots/restores through the bounded executor in Anchor file operations to authorized directory handles #452, and retain the latest undo entry when a restore fails.
Acceptance evidence
- Synthetic tests for new storage, migration of pre-existing
0755/0644history, owner mismatch/link refusal, and normal persistent undo across restart. - Deterministic root/path-substitution checks for the history store, with no outside canary effects.
- A controlled Linux two-UID test: the owner retains undo; another UID cannot traverse/read snapshots.
- Platform-specific ACL checks for macOS and Windows, with unsupported cases stated explicitly.
Related: #445 (session-store privacy), #452 (directory-handle-backed file operations). This is a distinct storage location discovered while implementing the defensive audit follow-up.
Activity
Broccolito commentedon Oct 24, 2026
Issue #453: Protect persisted Developer undo snapshots
Issue · Host PR #454 · merged main source
Historical desktop/browser/CLI observations used f227 artifacts, accepted via the reviewed production-equivalence comparison; exact-main CI/build/smoke is attributed separately below.
Problem. Persisted original source snapshots could be readable by another local account even when session history was protected.
Fix and UX. Undo storage uses bounded owner-private directories/files, private atomic replacement, object/owner/link checks and safe owned legacy migration. Restore uses bounded directory/target authority; failed restore retains the undo entry. Unsafe persistence setup falls back to same-session memory with an actionable notice. Later save failures are logged; no universal new UI notice is implied. Ordinary editor/undo commands remain unchanged.
Validation. Exact main rebuilt Linux passed 39/39 records,13 cases × 3,234 assertions, including separate-account snapshot denial and actual daemon restart→resumed Developer undo. Historical f227 native P1 independently passed full app+backend relaunch→old chat→fresh read/write Allow once→undo: snapshot counts 0/0→1/1→1/1 afterrestart→0/1 afterundo. Patch-one restored exactly; patch-two retained its authorized edit. Current macOS CI ran undo privacy/legacy/substitution/fallback units; Windows CI ran legacy enrollment/across-instances units and private-history bounded-file controls. Undo regressions distinguish those software observations from actual process restart.
Limits. Linux session-store legacy repair is not every undo migration path. Mac everyone-ACE checks and Windows primitives are not foreign-UID/GUI acceptance. No same-account/admin resistance, already-open descriptor revocation, exhaustive rollback or arbitrary network-filesystem guarantee is established. Windows handle-move limits remain under #452.
Recommendation. Keep the existing undo API and private atomic publication; retain entries after failed restores and recover unsafe startup persistence through the visible memory fallback.
Merged-main verification. PR454 is merged at
0580110; main Rust CI, frontend CI and related checks finished 20 successes/one skipped (17 push successes plus three landing/deployment checks; these are not security-test counts). Normal main build manifest SHA256:38cc2979c7da104cb623975bac7f6922a085e834a1be5c18e961d7f26ea811fc; shipping receipt:e6e1d8957d8d0b22a9aae814011a1e028c5d279848ffabbf4c6d0c4edf3c0112. Actual main01 smoke separately recorded four pairs—three reads/one typed Deny—and three unchanged canaries; it does not rerun/recount the historical 81-pair cohort.Submission record. Manuscript v1.1 local Supplementary Data S1: Supplementary Data S1 - BioRouter Security Audit - 2026-10-24.zip; manifest SHA256 4f0c0c110ca0b07a29d5281870227f7b36c744a0f37ccb499b8ef1283dea116e. This is a user/submission package, not a claim of public upload. Public PR/source/CI links above provide the public implementation proof.