Conversation
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).
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
providerConnections.data(accessToken, refreshToken, idToken, apiKey, etc.) was stored as plainJSON.stringifyin SQLite — readable in the clear by anyone with filesystem access to~/.9router/orDATA_DIR.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 insrc/mitm/manager.js), with an optionalDB_ENCRYPTION_KEYenv override documented in.env.examplefor portability across machines.connectionsRepo.js(normal read/write path),db/index.js(exportDb/importDb, DB backup/restore), andmigrate.js(one-time legacydb.json→ SQLite import).enc1:prefix) and parsed directly; every write re-encrypts going forward. No explicit migration step required.Flagged as the top finding in a security review for using 9Router with a company-owned provider API key.
Test plan
tests/unit/secretCol.test.js: round-trip encryption, legacy-plaintext read compatibility, fallback on invalid/null input.tests/__baseline__/known-fails.txtknown-red set) — checked in particulardb-concurrent,db-migration-chain,db-sqlite-vs-lowdb,bulk-add-names,antigravity-cache(all pass; unrelated failures confirmed pre-existing on a clean checkout).datacolumn 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