fix(security): harden permissions for local mnemonic database#180
Conversation
The mnemonic stored in ~/.mcli/mcli.db derives the user's Mostro identity and trade keys. Until now both the directory and the database file inherited the process umask, so under a permissive umask they could be left world-readable, exposing the mnemonic to other local users. Additionally, `connect()` (which creates the DB and generates the mnemonic on first run) ran before required config was validated, so a command run with missing MOSTRO_PUBKEY/RELAYS could silently create the sensitive database before failing. Changes (issue #179): - Create ~/.mcli with mode 0700 and mcli.db with mode 0600. The DB file is created atomically via create_new + mode, avoiding any window where it exists world-readable. - Defensively re-tighten an existing directory/DB on every run, so installs created by older versions (or a loose umask) get hardened. - Validate MOSTRO_PUBKEY, RELAYS and (for admin commands) ADMIN_NSEC before calling connect(), so missing config fails without creating any mnemonic material on disk. - connect_nostr returns an error instead of panicking when RELAYS is unset. - Add regression tests for 0700/0600 permissions, idempotent/tightening directory creation, and create_new semantics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XW5NGXu1jYKQGgr7xqMs8c
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughThe PR moves ChangesSecurity hardening
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f22c331c2e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/db.rs`:
- Around line 73-79: The code currently logs a warning and continues execution
when the set_mode call fails to restrict database permissions to 0o600, which is
a security risk. Instead of using eprintln to warn and continuing, convert this
to return the error so the program fails closed if database permissions cannot
be properly tightened. Replace the if let Err(e) block that calls eprintln with
error handling that returns the error from set_mode, ensuring the database is
not used if its permissions remain insecure.
- Around line 97-110: The create_private_db_file function has inconsistent
behavior across platforms: Unix uses create_new(true) to fail on existing files,
while non-Unix uses File::create which silently truncates existing files. Move
the create_new(true) call outside of the platform-specific conditionals so it
applies universally across all platforms, keeping only the Unix-specific
mode(0o600) call within the #[cfg(unix)] block. This ensures consistent
cross-platform behavior where creating an existing file always fails rather than
silently truncating it.
In `@src/util/misc.rs`:
- Around line 28-38: The ensure_private_dir function currently returns success
when create_dir_private fails with AlreadyExists without verifying that the
existing path is actually a directory. If the path exists as a regular file, it
will still attempt to set_mode and return Ok(()). Add metadata checking in the
AlreadyExists error branch to verify that the path is indeed a directory using
std::fs::metadata or similar, and return an appropriate error (such as
InvalidInput with a message indicating the path exists but is not a directory)
if it is not a directory before proceeding to the set_mode call.
In `@src/util/net.rs`:
- Around line 17-18: The RELAYS environment variable is split by comma, but
individual entries are not trimmed of whitespace or filtered for empty values,
which can cause failures in add_relay. After splitting the relays string by
comma, chain additional operations to trim whitespace from each entry using a
map operation and filter out any empty strings using filter before collecting
into the Vec. This ensures only valid, non-empty relay entries are processed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c0322dc5-f862-4b38-837a-bf68f51467ee
📒 Files selected for processing (6)
src/cli.rssrc/db.rssrc/util/misc.rssrc/util/mod.rssrc/util/net.rstests/util_misc.rs
- db.rs: use create_new on all platforms in create_private_db_file so the non-Unix fallback no longer truncates an existing database; only the 0600 mode bit stays Unix-gated. - db.rs: fail closed when an existing mnemonic DB can't be re-tightened to 0600 instead of only warning, so we never keep using a world-readable DB. - misc.rs: ensure_private_dir now verifies the pre-existing path is a directory before chmod-ing it and reporting success. - net.rs: trim relay entries and drop empty ones so stray whitespace or trailing commas don't reach add_relay. - tests: cover ensure_private_dir rejecting a non-directory path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XW5NGXu1jYKQGgr7xqMs8c
Closes #179
Summary
mostro-clistores the mnemonic that derives the user's Mostro identity and trade keys in~/.mcli/mcli.db. Until now the directory and DB file inherited the processumask, so under a permissiveumaskthey could be left world-readable, exposing the mnemonic to other local users. A related side-effect bug meant a command run with missing config could create that sensitive database before failing.Changes
0700) —~/.mcliis created with mode0700.get_mcli_pathnow delegates to a newensure_private_dir, which also re-tightens a pre-existing directory so installs created by an older version (or a looseumask) get hardened on the next run.0600) —mcli.dbis created atomically withcreate_new+mode(0o600), so there is never a window where the file exists world-readable. Existing databases are defensively re-tightened to0600on each run.MOSTRO_PUBKEY,RELAYSand (for admin commands)ADMIN_NSECare now validated ininit_contextbeforeconnect()runs, so a mistaken command with missing config fails without ever writing the mnemonic to disk.RELAYS—connect_nostrreturns a proper error instead ofexpect-panicking.0700/0600permissions, idempotent + tightening directory creation, andcreate_newsemantics (won't truncate an existing DB).All changes are
#[cfg(unix)]-gated for the permission bits, with a plain-create fallback on non-Unix platforms.Verification
Reproduced the issue's scenarios against the built binary with a temporary
HOME:umask 000+ full config~/.mcli→drwx------(0700),mcli.db→-rw-------(0600)MOSTRO_PUBKEY~/.mclior DB createdRELAYS0755/0644)0700/0600on next runcargo test,cargo fmt --checkandcargo clippy --all-targetsall pass.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
Bug Fixes
New Features
.mclidirectory are created with owner-only permissions for improved security.Tests