Make the SQLite data directory and DB files private (0700/0600) - #135
Merged
Conversation
The data dir was created with umask perms (typically 0755) and better-sqlite3 created privacy.db and its WAL/SHM sidecars 0644 — world-readable on multi-user machines, for a file holding the user's full app inventory, notes, and AI provider key. - lib/db.ts: mkdir with mode 0o700, plus tightenDataPermissions() on every open (0700 dir, 0600 db/-wal/-shm). Runs BEFORE the WAL pragma so freshly-created sidecar files inherit 0600 (SQLite copies the main DB file's mode). Best-effort by design: no-op on Windows, never fatal to opening the DB. Existing loose installs are tightened on their next boot. - src-tauri/src/sidecar.rs: cfg(unix) 0700 on the data dir at creation, so the first boot is never world-listable. - ci.yml compose-smoke: the bind-mount assertion becomes `sudo test -f` — the runner user can no longer stat inside a 0700 dir owned by uid 100, which is the intended lockdown. - AGENTS.md: documents the permissions contract. Pinned by tests/app/data-dir-permissions.test.ts (fresh install, loose-install migration, WAL-sidecar inheritance). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The quality job's audit gate fails on every branch since today's advisory batch — none of it introduced by this PR's diff: - next 16.2.9 -> 16.2.11: patches the 2026-07-22 Next.js batch (4 high + 5 moderate, all fixed in 16.2.11) - dompurify 3.4.11 -> 3.4.12: GHSA-c2j3-45gr-mqc4 (low) - sharp forced to ^0.35.0 via the existing pnpm-workspace.yaml overrides block: Next still pins ^0.34.5 transitively, which audit rejects for GHSA-f88m-g3jw-g9cj (libvips CVEs, fixed in 0.35.0). Same pattern as the postcss / @babel/core overrides; drop when Next's own range reaches >=0.35. Verified: pnpm audit --prod clean, typecheck, 438 unit tests, full Playwright suite (33 passed / 1 intentional skip) on a clean production build of next 16.2.11 with sharp 0.35.3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
Author
|
Second commit ( 🤖 Generated with Claude Code |
This was referenced Jul 23, 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.
Summary
The data directory was created with umask permissions (typically
0755) and better-sqlite3 createdprivacy.db+ its WAL/SHM sidecars0644— world-readable on multi-user machines, for a file holding the user's full app inventory, notes, and AI provider key. The repo already sets0600on the backup signing key; this brings the database itself up to the same standard.lib/db.ts— the data dir is created withmode: 0o700, and every open runstightenDataPermissions():0700on the dir,0600onprivacy.db/-wal/-shm. It runs before thejournal_mode=WALpragma so freshly-created sidecar files inherit0600(SQLite copies the main DB file's mode). Best-effort by design: a no-op on Windows, never fatal to opening the DB. Existing loose installs are tightened on their next boot — no migration step needed.src-tauri/src/sidecar.rs—cfg(unix)0700on the data dir at creation, so a desktop first-boot is never world-listable even before the Node side opens the DB..github/workflows/ci.yml— the compose-smoke bind-mount assertion becomessudo test -f data/privacy.db: the runner user can no longer stat inside a0700dir owned by uid 100, which is the intended lockdown, and the assertion must not depend on its absence.AGENTS.md— documents the permissions contract (including that bind-mount hosts now needsudoto inspect./data).Deployment impact
audituser (uid 100) owns/app/dataand keeps full access.sudoto look inside./data. Backups viadocker compose cp web:/app/data ./data-backupare unaffected.next start: files become private to the owning user; no behaviour change for the app itself.Verification
tests/app/data-dir-permissions.test.tspins three paths: fresh install, loose-install migration (chmod back to0755/0644, tighten, re-assert), and WAL-sidecar inheritance after a write. POSIX-only, skipped on Windows.pnpm typecheck+pnpm lintclean;cargo check --no-default-features --lockedclean.compose-smokejob in this PR's own CI run (both volume legs).🤖 Generated with Claude Code