fix(prepush): never-block credential gate — scope to sessions/, auto-redact JSONL, quarantine the rest (0.8.1)#606
Conversation
…/github (0.8.1) Co-Authored-By: SageOx <ox@sageox.ai> SageOx-Session: https://sageox.ai/repo/repo_019c5812-01e9-7b7d-b5b1-321c471c9777/sessions/2026-05-12T23-39-ryan-OxzcCn/view
…ine the rest (0.8.1) Co-Authored-By: SageOx <ox@sageox.ai> SageOx-Session: https://sageox.ai/repo/repo_019c5812-01e9-7b7d-b5b1-321c471c9777/sessions/2026-05-12T23-39-ryan-OxzcCn/view
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughPre-push credential gate redesigned to scope scanning to ChangesPre-push Secret Gate & Redaction Debt
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 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)
Comment |
Summary
The 0.8.0 pre-push secret gate (
cmd/ox/prepush_scan.go, introduced in #597) refused the whole push on any detector hit anywhere in the push range. Two compounding problems shipped together:data/github/**PR/Issue caches. PR titles, bodies, and comments contain text matching credential heuristics — sampleAuthorization: Bearersnippets, the literal phrase "STS session key", and other bytes already public on GitHub.ox doctorreconcile failed on clean machines with "Push refused: 3 credential pattern(s) detected in 2 file(s)" pointing at JSON the user did not author and could not fix viaox session audit/ox session redact(the recovery commands the gate's error message names — neither operates ondata/github/).What this PR changes
origin/main...HEADsessions/**onlydata/github/**writer-side redactorWriteGitHubPR/WriteGitHubIssue)RedactionPasstometa.json, amend holding commit, push proceeds.sageox/cache/quarantine/<session>/<file>, carve path out of holding commit, write a debt marker, push proceeds with the rest of the commitledger-redaction-debtcheck surfaces every quarantined session + next-step recoveryOX_ALLOW_SECRETS=1keeps its short-circuit semantics for explicit "publish as-is" overrides.Pre-push gate flow
flowchart TD A[pushLedger] --> B[runPrePushSecretGate] B --> C{scan sessions/ only} C -- clean --> Z[return nil] C -- findings --> D{OX_ALLOW_SECRETS=1?} D -- yes --> W[warn loudly to stderr] --> Z D -- no --> E[autoRedactSessionFindings: per session run redactFileInPlace, append RedactionPass to meta.json, stage --sparse, amend] E --> F{re-scan} F -- clean --> G[stderr: redacted N files] --> Z F -- findings remain --> H[quarantineUnredactableFindings] H --> H1[move file to .sageox/cache/quarantine/session/] H1 --> H2[git reset HEAD~ -- path, or git rm --cached if no HEAD~] H2 --> H3[git commit --amend] H3 --> H4[write .sageox/cache/redaction-debt/session.json] H4 --> I[stderr: quarantined N files, names listed, points at ox doctor] I --> ZWhy quarantine, not block
The daemon's session-finalize watches
<ledger>/sessions/and<ledger>/.sageox/cache/sessions/(seeinternal/daemon/agentwork/session_finalize.go). The quarantine path<ledger>/.sageox/cache/quarantine/is outside both, so anti-entropy will not re-introduce a quarantined file. Bytes are preserved verbatim on disk for forensic inspection or manual recovery; the holding commit drops only those paths.Why unwire the
data/github/**writer-side redactordata/github/**is a verbatim cache of bytes already published on GitHub. Replicating those bytes into the ledger has the same exposure surface as ingesting the PR/Issue at all. Rewriting them at write time:origin/mainkeep tripping the scanner on every re-touch.The
FieldRedactorplumbing is retained as a no-op pass-through so a future warn-on-detect mode can re-use the same chokepoint without re-plumbing the package boundary.Scope contract
TestFormatPrePushFindings_RecoveryMatchesScannerScopepins the policy: any future widening ofprePushScannerScopePrefixesmust come with a deliberate broadening of the recovery message surface.TestInPrePushScannerScopetable-tests the scope predicate.TestScanPrePushForSecrets_OnlyScansSessionsScopeexercises end-to-end thatdata/github/**,kb/**, andteam-context/**paths are not scanned even when they carry planted canaries.What's deferred
ox-tduv— daemon anti-entropy interactions for sessions quarantined long-term + an optionalox session accept-unredacted <name>command for explicit "I reviewed it, publish as-is" flow. Today the user manually moves the quarantined bytes back tosessions/<name>/and commits, or usesOX_ALLOW_SECRETS=1per-push.Test plan
make lint— 0 issuesgo vet ./...— cleango test ./cmd/ox ./internal/ledger -short -count=1— all greenmake verify-version— 0.8.1 across version.go, marketplace.json, plugin.json, CHANGELOGOX_ALLOW_SECRETS=1still short-circuits recoverydata/github/content:ox doctorreconcile should now pass;ox doctorshould surface any quarantined sessions under "Ledger redaction debt"Files
cmd/ox/prepush_scan.gosessions/;runPrePushSecretGatenow never blocks — auto-redact + quarantine flowcmd/ox/prepush_autoredact.goautoRedactSessionFindings,quarantineUnredactableFindings,redactionDebtRecordcmd/ox/prepush_scan_test.gocmd/ox/prepush_autoredact_test.gocmd/ox/doctor_redaction_debt.goledger-redaction-debtcheckcmd/ox/doctor_redaction_debt_test.gocmd/ox/doctor_check_registry.go+doctor_types.gocmd/ox/redactor_wiring.goledger.SetFieldRedactorinternal/ledger/github_data.goredactPR/redactIssuenow no-op pass-throughinternal/version/version.go,.claude-plugin/marketplace.json,claude-plugin/.claude-plugin/plugin.json,CHANGELOG.md