Summary
Plan U8 specifies "filters on both the local ref name AND the remote ref name." Implementation enforces the dual-ref check on the private-ref guard but not on the public path. git push origin <branch>:refs/notes/prompts (local ref is a branch, remote ref is the public notes ref) skips the gate entirely; arbitrary content can be written to the public notes ref unscrubbed.
Severity
P1 — secret-leak path bypassing the second line of defense. Confidence 95.
Evidence
crates/prov-cli/src/commands/hook.rs (handle_pre_push): the public-side filter currently checks only local_ref != NOTES_REF_PUBLIC (around line 1038). Should also fire when remote_ref == NOTES_REF_PUBLIC regardless of local ref name.
Suggested fix
Mirror the private-ref guard pattern: trigger the gate when either side names the public notes ref.
Acceptance criteria
Reference
/tmp/compound-engineering/ce-code-review/20260502-075634-3af3d047/security.json (sec-003).
Summary
Plan U8 specifies "filters on both the local ref name AND the remote ref name." Implementation enforces the dual-ref check on the private-ref guard but not on the public path.
git push origin <branch>:refs/notes/prompts(local ref is a branch, remote ref is the public notes ref) skips the gate entirely; arbitrary content can be written to the public notes ref unscrubbed.Severity
P1 — secret-leak path bypassing the second line of defense. Confidence 95.
Evidence
crates/prov-cli/src/commands/hook.rs(handle_pre_push): the public-side filter currently checks onlylocal_ref != NOTES_REF_PUBLIC(around line 1038). Should also fire whenremote_ref == NOTES_REF_PUBLICregardless of local ref name.Suggested fix
Mirror the private-ref guard pattern: trigger the gate when either side names the public notes ref.
Acceptance criteria
refs/heads/mainand remote ref isrefs/notes/prompts— gate fires the redactor.Reference
/tmp/compound-engineering/ce-code-review/20260502-075634-3af3d047/security.json(sec-003).