Skip to content

fix(storage): create config.db owner-only and secure database backups - #1342

Open
Dumbris wants to merge 1 commit into
mainfrom
fix/db-file-mode-0600
Open

Dumbris wants to merge 1 commit into
mainfrom
fix/db-file-mode-0600

Conversation

@Dumbris

@Dumbris Dumbris commented Sep 22, 2026

Copy link
Copy Markdown
Member

What

~/.mcpproxy/config.db stores OAuth access/refresh tokens and DCR client secrets as plain JSON (internal/storage/models.go), but was created with mode 0644 and backed up with 0644. This PR makes the database and its backups owner-only.

  • bbolt.Open now passes 0600.
  • An existing group/world-readable database is tightened on open, clearing only the 0o077 bits so a stricter owner mode is never widened. The mode argument to bbolt.Open applies only at creation and bbolt never chmods an existing file, so this migration is the entire fix for anyone who already has a database.
  • The chmod is best-effort: stat/chmod failures are logged at debug level and never block startup.
  • Backup no longer uses Tx.CopyFile directly. CopyFile opens the destination with O_CREATE|O_TRUNC, so its mode argument is ignored when the file already exists — a chmod afterwards comes too late, since the database bytes would already have landed in whatever permissions a stale backup carried. The copy is now staged in an owner-only temp file next to the destination and renamed into place.

Why

Scoped honestly: ~/.mcpproxy is created 0700 (internal/config/loader.go), so in a default install another local user cannot traverse to the 0644 database. This is defense-in-depth, and the 0700 directory is not an invariant — the MkdirAll is skipped when DataDir still contains an unresolved ${...}, and MkdirAll never tightens an already-existing directory.

Backup currently has no production callers — it is reachable only as an exported method declared on appctx.StorageInterface. The 0644 copy was therefore a latent hazard on a public API, not a live leak.

Out of scope, deliberately:

  • cmd/mcpproxy/db_cmd.go compact/repair propagate the on-disk mode on purpose (info.Mode().Perm(), with a comment explaining bbolt will not chmod an existing file). They become correct by propagation after this change and are untouched.
  • Envelope-encrypting the token fields with a go-keyring-held key is the right follow-up and a larger, separate PR.
  • ~/.mcpproxy/index.bleve/ and the log files (documented as 0644) are not covered here.

How verified

TDD: five new tests in internal/storage/bbolt_file_mode_test.go (fresh create, migration of an existing 0644 database, owner-bit preservation, backup to a new destination, backup over a pre-existing 0644 destination), each confirmed failing before the fix. They are skipped on Windows, where os.Chmod only toggles the read-only bit and os.Stat reports 0666.

Also verified against a real mcpproxy serve instance on a scratch data dir: a fresh config.db lands -rw-------; loosening it to 0644 and restarting migrates it back with Tightened permissions on ... from 0644 to 0600 at debug level; a third start on an already-0600 database logs nothing.

Cross-reviewed with codex gpt-5.6-sol over three rounds; two rounds returned blocking findings on the backup path (both verified against go.etcd.io/bbolt@v1.5.0/tx.go before fixing), the third returned APPROVE.

🤖 Generated with Claude Code

config.db stores OAuth access/refresh tokens and DCR client secrets as
plain JSON, but was created with mode 0644 and backed up with 0644.

- Open the database 0600.
- Tighten an existing group/world-readable database on open, clearing
  only the 0o077 bits so a stricter owner mode is never widened. This is
  best-effort: failures are logged at debug level and never block startup.
- Stage Backup in an owner-only temp file next to the destination and
  rename it into place. bbolt's Tx.CopyFile opens with O_CREATE|O_TRUNC,
  so its mode argument is ignored for an existing destination and the
  database bytes would land in whatever permissions a stale backup carried.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0ca3f08
Status: ✅  Deploy successful!
Preview URL: https://935f6982.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-db-file-mode-0600.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 50.00000% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/storage/bbolt.go 50.00% 10 Missing and 8 partials ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown
Contributor

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/db-file-mode-0600

Available Artifacts

  • archive-darwin-amd64 (30 MB)
  • archive-darwin-arm64 (27 MB)
  • archive-linux-amd64 (18 MB)
  • archive-linux-arm64 (16 MB)
  • archive-windows-amd64 (30 MB)
  • archive-windows-arm64 (26 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (24 MB)
  • installer-dmg-darwin-arm64 (22 MB)
  • smart-mcp-proxymcpproxy-goISI6J9.dockerbuild (0 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 35746068151 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants