Skip to content

fix(db): encrypt provider connection secrets at rest (AES-256-GCM) - #2776

Open
iamrulloh wants to merge 1 commit into
decolua:masterfrom
iamrulloh:fix/encrypt-provider-connections
Open

iamrulloh wants to merge 1 commit into
decolua:masterfrom
iamrulloh:fix/encrypt-provider-connections

Conversation

@iamrulloh

Copy link
Copy Markdown

Summary

  • providerConnections.data (accessToken, refreshToken, idToken, apiKey, etc.) was stored as plain JSON.stringify in SQLite — readable in the clear by anyone with filesystem access to ~/.9router/ or DATA_DIR.
  • Adds src/lib/db/helpers/secretCol.js: AES-256-GCM encryption, keyed by a machine-derived key by default (node-machine-id + salt, same pattern already used for the MITM sudo password in src/mitm/manager.js), with an optional DB_ENCRYPTION_KEY env override documented in .env.example for portability across machines.
  • Wires it into every code path that reads/writes that column: connectionsRepo.js (normal read/write path), db/index.js (exportDb/importDb, DB backup/restore), and migrate.js (one-time legacy db.json → SQLite import).
  • Backward compatible: existing plaintext rows are detected (no enc1: prefix) and parsed directly; every write re-encrypts going forward. No explicit migration step required.
  • DB export/import backup payloads are intentionally kept in plaintext JSON (decrypt-on-export, encrypt-on-import) to preserve the existing human-readable/portable backup format — this matches current behavior, not a new regression.

Flagged as the top finding in a security review for using 9Router with a company-owned provider API key.

Test plan

  • New tests/unit/secretCol.test.js: round-trip encryption, legacy-plaintext read compatibility, fallback on invalid/null input.
  • Verified no regressions against the existing suite (tests/__baseline__/known-fails.txt known-red set) — checked in particular db-concurrent, db-migration-chain, db-sqlite-vs-lowdb, bulk-add-names, antigravity-cache (all pass; unrelated failures confirmed pre-existing on a clean checkout).
  • Manual: add a provider connection, inspect the SQLite data column directly to confirm it's ciphertext, restart the app and confirm the connection still works (round-trip through the running app).

🤖 Generated with Claude Code

Provider access/refresh/id tokens and API keys were stored as plain
JSON in the providerConnections.data SQLite column — readable by
anyone with filesystem access to the data directory.

Adds src/lib/db/helpers/secretCol.js (AES-256-GCM, machine-derived key
by default, optional DB_ENCRYPTION_KEY env override for portability)
and wires it into every read/write path touching that column:
connectionsRepo.js, index.js (exportDb/importDb), migrate.js (legacy
db.json import). Existing plaintext rows keep reading correctly via an
"enc1:" prefix marker and are transparently re-encrypted on next write
— no migration step needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
afandiaziz pushed a commit to afandiaziz/9router that referenced this pull request Aug 9, 2026
29 PR upstream di-cherry-pick (semua masih open upstream per 2026-08-09).
Rincian lengkap + link per PR ada di FORK-CHANGES.md.

P1 skala 2475 koneksi : decolua#2798 decolua#410 decolua#2879 decolua#879 decolua#2997
P2 akurasi token/usage: decolua#2422 decolua#2658 decolua#2762 decolua#2453 decolua#2668 decolua#2361
P3 provider & combo   : decolua#2526 decolua#3125 decolua#1434 decolua#2689 decolua#2439 decolua#2724 decolua#2647 decolua#1805
                        decolua#2909 decolua#2853 decolua#2508 decolua#2928 decolua#2345 decolua#2112 decolua#2786
P4 keamanan           : decolua#1666 decolua#2776

Revert decolua#664: menambah transformRequest kedua di DefaultExecutor sehingga
menimpa yang pertama dan mematikan stream_options/text.format/
injectReasoningContent/stripUnsupportedParams — termasuk PR decolua#3081 yang
sudah dipakai produksi.

Test: 88 gagal / 1783 lulus — nol regresi vs baseline v0.5.50 (88/1656).
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.

1 participant