feat(privacy): fail-closed Git identity/secret/host-path gate over all objects - #52
Conversation
Replace the deprecated list-all subprocess contract with explicit host-owned unbounded output controls and list --all on pm CLI 2026.8.20. Add exact argv and real installed npm/bun acceptance, align manifest/peer/dev floors, update all package fixtures and documentation, preserve complete changelog generation, and record PM evidence plus independent privacy, release-order, and exact-coverage blockers.
Document the pm 2026.8.20 compatibility break, remove unsafe narrowing advice from complete-corpus buffer errors, and route the real installed-CLI envelope through the production decoder. Preserve the pm-changelog release blocker because no published version contains PR #148.
v2kt: release publish ordering fix verified live on main (PR #46). zqad: maintainer-approved history rewrite executed 2026-08-21 - /home/steve -> $HOME across 15 blobs, codex@local -> approved noreply identity, all heads/tags force-pushed after fresh-clone validation (HEAD tree identical, 261/261 tests green). Forward identity/host-path gate remains open on zqad.
…very Git object Implements the forward gate required by pm-github-zqad: - scripts/privacy-gate.ts walks the local object store via git cat-file --batch-all-objects (reachable AND unreachable), checks commit author / committer / annotated-tag tagger emails against a checked-in justified allowlist, and scans every blob for high-confidence credential signatures and absolute personal host paths. Findings name only rule + object id; matched values are never printed. - .github/approved-git-identities.txt: justified allowlist including the maintainer decision to retain stefan@preu.at (2026-07-21) and the pi-agent service identity; codex@local deliberately absent after the 2026-08-21 history rewrite. - test/fixtures/privacy-gate/: synthetic fixtures exempted through a content-addressed manifest (exact git blob OIDs + justification). - Behavioral tests build real throwaway git repositories: every secret rule, host paths, identity roles, exemption correctness, negative control (clean baseline then introduced leak fails), fail-closed on missing allowlist and non-repository paths. - Wired into release:check and CI.
Reviewer's GuideAdds a fail-closed Git privacy gate over the full object store and updates the pm CLI integration contract and release pipeline to enforce it, including tests, fixtures, and compatibility-floor wiring. Sequence diagram for the Git privacy gate repository auditsequenceDiagram
actor CI
participant npm
participant privacy_gate_main as privacy-gate.main
participant runGate as runGate
participant fs as filesystem
participant git
CI->>npm: npm run privacy
npm->>privacy_gate_main: node scripts/privacy-gate.ts
privacy_gate_main->>runGate: runGate(repoRoot)
runGate->>fs: parseAllowlist(repoRoot)
fs-->>runGate: approved email set
runGate->>git: listAllObjects(repoRoot)
git-->>runGate: [oid, type][]
runGate->>fs: loadFixtureExemptions(repoRoot)
fs-->>runGate: exemptions map
loop for each object
alt type is commit or tag
runGate->>git: readObject(repoRoot, type, oid)
git-->>runGate: commit/tag text
runGate->>runGate: extractEmail(header line)
alt email not in allowlist
runGate->>runGate: record Finding(identity:type, oid)
end
else type is blob
runGate->>git: readObject(repoRoot, "blob", oid)
git-->>runGate: blob content
runGate->>runGate: scanBlob(oid, content, exemptions)
alt rule(s) matched
runGate->>runGate: record Finding(rule, oid)
end
end
end
alt findings exist
runGate-->>privacy_gate_main: { exitCode: 1, stderr: summary }
else no findings
runGate-->>privacy_gate_main: { exitCode: 0, stdout: summary }
end
privacy_gate_main->>CI: process exitCode and output
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThe PR adds an all-object privacy gate, integrates it into CI and release validation, and updates the pm CLI complete-corpus contract. The follow-up fixes narrow two prior bypasses but leave both security boundaries incomplete.
Confidence Score: 1/5The PR is not yet safe to merge because crafted identity objects and content-aliased sensitive blobs can still bypass the privacy gate. The attempted fixes do not fully enforce either reported privacy boundary: identity validation begins only after a narrow header-prefix match, and fixture validation grants a global exemption based solely on content OID membership. Files Needing Attention: scripts/privacy-gate.ts and test/privacy-gate.test.ts
|
| Filename | Overview |
|---|---|
| scripts/privacy-gate.ts | Implements the privacy gate, but the malformed-header and fixture-exemption fixes leave two reachable bypasses. |
| test/privacy-gate.test.ts | Adds broad behavioral coverage, though the new regressions omit malformed role delimiters and content aliasing through the fixture directory. |
| .github/workflows/ci.yml | Integrates the privacy audit into the existing CI validation sequence. |
| index.ts | Updates complete-workspace reads to the canonical unbounded pm CLI invocation. |
| package.json | Adds privacy validation to release checks and aligns the pm CLI compatibility floor. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Enumerate local Git objects] --> B{Object type}
B -->|commit or tag| C[Match identity-header prefix]
C -->|matched| D[Parse and allowlist email]
C -->|not matched| E[Identity skipped]
B -->|blob| F{OID in effective fixture exemptions}
F -->|yes| G[Skip all blob rules]
F -->|no| H[Scan secrets and host paths]
Prompt To Fix All With AI
### Issue 1
scripts/privacy-gate.ts:348
**Malformed identity prefixes bypass validation**
When a crafted commit or tag uses an identity line such as `author<unapproved@example.com>` or a tab after the role, the prefix check skips the line before `extractEmail` runs, causing the gate to approve an unverifiable identity. **How this was verified:** The unmatched-prefix branch was traced directly through `continue`, bypassing both parsing and the allowlist check.
### Issue 2
scripts/privacy-gate.ts:180-183
**Fixture aliases exempt real leaks**
When sensitive blob content is also placed under `test/fixtures/privacy-gate/` and its OID is added to the manifest, this intersection admits the content OID and `scanBlob` exempts every identical blob regardless of its path or provenance, causing a real credential or host path elsewhere in history to pass CI and `release:check`. **How this was verified:** The effective exemption was traced from HEAD fixture-tree OID membership through the manifest intersection to the unconditional OID-based return in `scanBlob`.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (2): Last reviewed commit: "fix(privacy): fail closed on unparseable..." | Re-trigger Greptile
…to reviewed fixture blobs Greptile review fixes: - P1: an identity header without a parseable <email> is now itself a violation instead of silently passing the allowlist check. - P1: manifest exemption keys are intersected with the blob object ids actually present under test/fixtures/privacy-gate/ at HEAD, so a manifest entry alone can never silence a leaked blob living elsewhere in history. - P2 (documented, no change): CI audits refs/tags reachable objects; unreachable-object coverage remains the local release:check run's evidence, mirroring pm-rust's documented distinction. Regression tests craft real malformed commit objects (--literally) and an abuse attempt pairing an outside-fixture leak with a manifest key.
…scope scan to header section Greptile round 2 fix (valid P1): the role-prefix check required a literal space after author/committer/tagger, so crafted headers like 'author<email>' or a tab separator skipped verification entirely. The gate now scans only the header section (before the first blank line) and matches the bare role keyword, then fails closed on any header identity that does not parse or is not approved. Message-body lines that begin with an identity keyword are provably not flagged. Refused P1 (fixture aliases): an exempted OID is by construction byte-identical to a reviewed synthetic fixture file; content addressing means a 'real leak elsewhere with the same OID' contains exactly those reviewed synthetic bytes. The residual risk is committing a real secret into the fixtures directory itself, which is visible in the diff review of the manifest + fixture change.
There was a problem hiding this comment.
Greptile has paused reviews on this repository — it used its 100 free open-source review credits for this billing period. Reviews resume automatically on August 27. To continue before then, an organization admin can keep reviews running past the free credits — those bill as normal usage.
|
Thanks for the guide — accurate summary of the gate design. Note Sourcery's own diff review was skipped this week (weekly diff-character limit), so no code findings to address from you; the security review happened in the Greptile threads above, all now resolved. |
|
Correction to this summary's conclusion: both flagged boundaries are closed on head 3c1531a. (1) Malformed/unparseable identity headers now fail closed and header scanning is separator-agnostic (Greptile P1s 3833993306, 3834076989 — fixed with regression tests). (2) The fixture-exemption concern (3834076994) was retracted by Greptile itself: content addressing means an exempted OID is byte-identical to reviewed fixture bytes by definition. No unresolved findings remain. |
|
Acknowledged — no manual review trigger needed. This PR received a full Greptile security review on the current head; all P1 findings are fixed or resolved with regression tests, and Greptile's final thread is closed. Merging as-is. |
Co-authored-by: SteveBot <1153461+unbraind@users.noreply.github.com>
Implements the forward gate required by pm-github-zqad.
Stacks on #48 — merge #48 first; this branch contains it plus the privacy gate.
What
scripts/privacy-gate.ts: enumerates every object in the local store (git cat-file --batch-all-objects, reachable and unreachable), fails closed on unapproved commit author/committer/tagger identities, high-confidence credentials, and absolute personal host paths. Never prints matched values..github/approved-git-identities.txt: checked-in, per-entry-justified allowlist. Records the maintainer's 2026-07-21 decision to retainstefan@preu.atas public authoring identity;codex@localis deliberately absent following the maintainer-approved history rewrite executed 2026-08-21.pm items
Summary by Sourcery
Enforce fail-closed privacy checks across Git history and standardize complete-workspace reads on the current pm CLI contract.
New Features:
Bug Fixes:
Enhancements:
pm --pm-path ... list --allfull-workspace read contract and remove reliance on the deprecatedlist-allalias.CI:
Documentation:
Tests:
Chores:
Summary by cubic
Adds a fail-closed Git privacy gate that audits every local object for unapproved identities, high-confidence credentials, and absolute personal host paths. Previously there was no forward gate; now CI and release fail on violations, findings never print matched values, headers without a parseable email or with nonstandard separators fail, and exemptions bind only to reviewed fixture blobs.
scripts/privacy-gate.tswalksgit cat-file --batch-all-objects(reachable and unreachable), validates commit author/committer/tagger emails against.github/approved-git-identities.txtusing header-only scanning and bare role keyword matching, scans blobs for secrets and absolute host paths, and allows exemptions only via a content-addressed blob-OID manifest intersected with fixture blobs present at HEAD..github/workflows/ci.ymlrunsnpm run privacy. CI audits refs/tags; the local release check additionally covers unreachable objects.pm list-allwithpm list --alland explicit unbounded output controls; raise floors to@unbrained/pm-cli >=2026.8.20(manifest and peer), and update docs..github/approved-git-identities.txt; ensure local environments use@unbrained/pm-cli2026.8.20 or newer.Written for commit 3c1531a. Summary will update on new commits.