claudewatcher scrubber misses https://user:secret@host — the git credential-store shape passes every rule #142
Closed
opened 2026-09-30 11:24:16 +00:00 by mathias
·
1 comment
No Branch/Tag Specified
main
fix/pipeline-writes-158
fix/edit-path-io-192
fix/create-regular-188
fix/guard-special-179
fix/linkcheck-keeps-write-178
fix/write-dir-redirect-173
fix/tunnel-detect-174
fix/pipeline-root-157
w3/154-mcp-auth-wiring
fix/tunnel-traversal
test/in-brain-targets-171
fix/brain-scan-root-157
fix/graph-wiki-root-157
fix/vectorstore-root-157
fix/notes-root-157
fix/search-root-157
fix/brainwide-root-157
fix/raw-symlinks-153
feat/symlink-tripwire-157
fix/rest-auth-149
fix/123-brain-edit-envelope-check
feat/106-page-aware-extraction
fix/105-source-drift-on-main
fix/104-surface-merge-conflicts-on-main
agent/b1-compile-on-ingest
agent/104-surface-merge-conflicts
agent/105-source-drift
agent/107-loud-unconfigured-alerting
feat/brain-read-edit
fix/capture-i1-refusal-names-target
renovate/configure
feat/consolidate-single-harness
icebox/cmd-routing
feat/brain-pending-promote
fix/close-session-classification-refresh
fix/capture-summarywriter
v0.24.0
v0.23.1
v0.23.0
v0.22.0
v0.21.1
v0.21.0
v0.20.1
v0.20.0
v0.19.1
v0.19.0
v0.18.4
v0.18.3
v0.18.2
v0.18.1
v0.18.0
v0.17.0
v0.16.1
v0.16.0
v0.15.0
v0.14.0
v0.13.2
v0.13.1
v0.13.0
v0.12.1
v0.12.0
v0.11.2
icebox/cmd-routing-2026-07-01
v0.11.1
v0.11.0
v0.10.0
v0.9.0
v0.8.0
v0.7.0
v0.6.0
v0.5.0
v0.4.1
v0.4.0
v0.3.0
v0.2.0
v0.1.0
Labels
Clear labels
agent/ready
blocked/client
blocked/supplier
blocked/upstream
risk/high
risk/low
risk/medium
size/l
size/m
size/s
size/xs
status:dispatch
status:dispatched
status:review
status:reviewing
type/chore
type/decision
type/docs
type/feat
type/fix
type/ops
type/research
type/test
Meets the agent-ready contract; authorizes unattended execution
Waiting on the client
Waiting on a supplier
Waiting on an upstream project or vendor
Irreversible, security-relevant, or control plane
Reversible, local, test-covered
Touches shared state, deploys or data
Too big - split it
Under two days
Under half a day
Under an hour
Maintenance, no behaviour change
Human decision required; never agent/ready
Documentation only
New capability
Corrects broken behaviour
Infrastructure, deploy, runtime
Investigate; output is a finding, not code
Test coverage only
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mathias/hyperguild#142
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The gap
ingestion/internal/claudewatcher/scrubber.gohas a rule for Postgres URIs with embedded auth:There is no equivalent rule for the generic
scheme://user:secret@hostform, and that is exactly the shape a git credential store holds:Verified against all 13
DefaultRulesusing an invented value (https://mathias:0123…4567@git.d-ma.be, not a real credential): it matches none of them. Notauthorization-header, notbearer-token, and nothomelab-env-token— that last one requires a_TOKEN=/_PASSWORD=style prefix, which a bare URL does not have.So a turn containing a git credential URL is not dropped. It is ingested.
Why it matters here specifically
~/.claude/projectsis claudewatcher's input. A sweep of that tree today finds 12 occurrences of 2 distinct credential-shaped URLs forgit.d-ma.be, across 4 transcript files in 3 different sessions — so this is not a single incident, it is a shape that has been passing the scrubber for a while.The scrubber's own doc comment states the intent it is not currently meeting:
It fails closed for the shapes it names. This shape it does not name, so it fails open.
Fix
Add a rule for credentials embedded in any URL userinfo, not just Postgres:
Notes on the shape:
postgres-uri-with-passwordshould stay for the more specific log name, asauthorization-headerdoes beforebearer-token{8,}floor on the secret half avoids matchinghttp://user:@hostand short non-secret userinfohttps://host:443/path— the[^:\s/@]+:+@anchoring handles that, but it needs a testA test with a table of shapes that must and must not scrub, including:
https://u:<40hex>@git.d-ma.be,https://oauth2:<tok>@host,git+ssh://u:p@hosthttps://git.d-ma.be:443/mathias/infra.git,postgres://localhost:5432/db,https://host/path?a=b:cRelated, and the reason this was found
A test suite executing a script that configures git ran with the operator's real
$HOME, so it read koala's live~/.git-credentialsand printed two live values into a session transcript (infra#466). Two credentials need rotation as a result — tracked separately, since that is a secret write.The generalisable lesson is not "don't read the credential file". It is that the scrubber is the last line of defence for exactly this class of mistake, and this class walks straight through it. The mistake will recur; the scrubber is the control that is supposed to make it survivable.
Acceptance
https://user:<secret>@hostis dropped, with the rule named in the warn logscrubber_test.go, both directionsCreated via git-mcp on behalf of @mathias
Fixed in
3c70b6f, tagged v0.18.4, pushed to main.url-userinfo-credentialadded afterpostgres-uri-with-password, so a DSN still reports the more specific name (same convention asauthorization-headerbeforebearer-token):Verified in both directions by
TestScrub_URLUserinfoCredential, written red before the rule existed — 7 must-scrub cases failed, 7 must-not-scrub passed, then the rule turned the first 7 green without moving the second 7.Must scrub (all secrets invented): git credential store with
mathias:andoauth2:users, the in-context payload line, a clone URL in argv,http://,git+ssh://,redis://.Must NOT scrub:
https://git.d-ma.be:443/...,postgres://localhost:5432/brain, a colon in a query string, a plain issue URL,https://user:@host, the literal printf format stringhttps://oauth2:%s@%s, and a bare00:03:22duration.Confirmed which rule fires rather than assuming the suite going green meant the new one did it: the credential-store shape reports
url-userinfo-credential, andpostgres://user:s3cret@...still reportspostgres-uri-with-password.task checkexit 0,go vetclean, gitleaks "no leaks found" over 1.92 MB.Not closed by this: the two already-leaked credentials. Rotation is tracked on mathias/infra#466 and is Mathias's action, since the 12 occurrences already sitting in
~/.claude/projectspredate this rule — the fix stops new ones being ingested, it does not retract old ones. Per his decision the transcripts stay intact as audit records and rotation is the containment.Created via git-mcp on behalf of @mathias