From a3dfa73fef7873339816021cbb9f8226a7a94d0c Mon Sep 17 00:00:00 2001 From: "Snow Lee (Sungwon)" Date: Tue, 18 Aug 2026 15:08:44 -0700 Subject: [PATCH] Catch a credential when it syncs, not only when you share it (#162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(secrets): lift the share-time credential rules into internal/secrets The rules only ever ran on the rarest path a file takes. Moving them out of internal/webapp is what lets internal/syncer run the same six rules on the path every file takes, without inverting the dependency. Pure move plus one addition: Label(), the six human strings that until now lived only in the frontend's SECRET_LABELS — so 'bdrive share' stops printing a bare rule id where the web dialog says 'an AWS access key'. Rule ids and the rule/line JSON tags are unchanged: Browser.tsx keys off them, so they are a wire contract. * feat(sync): warn when a synced file looks like it holds a credential The six share-time rules now run on the path every file takes. A file with an AWS key in it used to ride a normal sync to the hub, to every teammate's disk and into every future agent's context with no badge and no warning — while the Share dialog one click later blocked that exact file. Warn, never block: the op is journaled and pushed exactly as before. A hold arm would mean a false positive silently parks someone's changes, and it would break the cycle's degrade-to-offline posture. - scan() reads the blob PutBlobFile just wrote (the bytes that were actually journaled), only on the branches that wrote one — an unchanged file is still never re-read. - Findings persist per path in secrets-.json, merged rather than replaced: nearly every cycle scans zero files, and a whole-set rewrite would erase the warning seconds after it appeared. Fixing the file clears it. - bdrive status grows a secrets block; the agent hook appends one advisory sentence. Rule ids and line numbers only, never the matched bytes. - SaveSecrets failing logs and continues: advisory telemetry never gets a veto over convergence. * docs: the credential check now runs on sync, not only on share README, the CLI reference and project-files get the new bdrive status block and the warn-never-block posture, with the three limits stated (checked when it changes, first 1 MiB, writing device only). Diagrams: internal/secrets is a package of its own in the overview, secretLog joins the sync engine, and the share-gate class notes that it no longer owns the rules. * test(sync): assert an unchanged file is never re-read for credentials The check must ride the branch that already reads the file. Clearing the record by hand and cycling proves it: a scan that re-read unchanged files would put the finding back, and the daemon's 3-second tick would pay for it on every file. --- README.md | 25 +++- architecture/cli-sync.md | 14 ++ architecture/overview.md | 4 + architecture/webapp-server.md | 9 +- cmd/bdrive/cmds.go | 36 +++++ cmd/bdrive/hooksync.go | 54 +++++++- cmd/bdrive/hooksync_test.go | 81 ++++++++++++ cmd/bdrive/migrate.go | 6 +- cmd/bdrive/share.go | 6 +- internal/secrets/secrets.go | 99 ++++++++++++++ internal/secrets/secrets_test.go | 78 +++++++++++ internal/store/secrets.go | 57 ++++++++ internal/store/secrets_test.go | 84 ++++++++++++ internal/store/store.go | 19 ++- internal/syncer/secrets.go | 95 ++++++++++++++ internal/syncer/syncer.go | 36 ++++- internal/syncer/syncer_test.go | 124 ++++++++++++++++++ internal/webapp/cli_e2e_test.go | 69 ++++++++++ internal/webapp/secrets.go | 74 ----------- internal/webapp/secrets_test.go | 63 --------- internal/webapp/shares.go | 6 +- internal/webapp/shares_test.go | 12 +- web/docs/src/content/docs/reference/cli.md | 32 ++++- .../content/docs/reference/project-files.md | 4 +- 24 files changed, 913 insertions(+), 174 deletions(-) create mode 100644 internal/secrets/secrets.go create mode 100644 internal/secrets/secrets_test.go create mode 100644 internal/store/secrets.go create mode 100644 internal/store/secrets_test.go create mode 100644 internal/syncer/secrets.go delete mode 100644 internal/webapp/secrets.go delete mode 100644 internal/webapp/secrets_test.go diff --git a/README.md b/README.md index 278f916..55e66b9 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ hub's own storage, never something a syncing client points at directly: | `bdrive sync [folder]` | Run one sync cycle now. `--note ` stamps session context (e.g. an agent session id) onto changes — shown in `bdrive log` and hub history; keeps applying to daemon-committed changes until `--note-ttl` (default 30m) expires. `--prune` also removes from the hub what `.bdriveignore` now excludes (files stay on disk everywhere). `--hook