# PRD 0061: Gitleaks inline suppression supervision

- **Status:** Active
- **Author:** codex
- **Created:** 2026-06-10
- **Issue:** #208

## Summary

Route every `# gitleaks:allow` inline suppression through the existing
supervisor approval queue before git-gate forwards a push upstream. The
annotation remains available for test fixtures and confirmed false positives,
but it no longer lets an agent silently bypass the gate's secret scan.

## Problem

Gitleaks supports inline suppression comments. With the default behaviour,
a pushed line that contains a secret-like value and `# gitleaks:allow` is
ignored by gitleaks and accepted by git-gate. That is convenient for dummy
credentials in tests, but it also gives an agent a one-line bypass for a real
secret. Once the commit lands, the diff alone cannot prove whether the
suppression was legitimate.

## Goals / Success Criteria

1. Git-gate continues to run the normal gitleaks scan for every incoming ref.
2. After the normal scan passes, git-gate runs a second scan with
   `--ignore-gitleaks-allow` and a JSON report so suppressed findings become
   visible.
3. If that second scan reports no suppressed findings, the push proceeds
   unchanged.
4. If it reports suppressed findings, git-gate creates a `gitleaks-allow`
   supervisor proposal containing the ref, file path, line number, rule,
   commit, and flagged line for each finding.
5. The push proceeds only when the supervisor explicitly approves the
   proposal; rejection, malformed responses, missing supervisor configuration,
   and timeout all refuse the push.
6. The supervisor TUI requires a reason when approving a `gitleaks-allow`
   proposal, so the audit trail records whether the approval was for a test
   fixture or a false positive.

## Non-goals

- Replacing gitleaks or changing the main secret-detection rule set.
- Removing support for `# gitleaks:allow`.
- Automatically classifying fixture files or false positives.
- Adding new supervisor transport or authentication mechanisms.

## Design

### Git-gate flow

`git_gate_render_hook()` emits a `supervise_gitleaks_allow` shell helper.
For each incoming ref, git-gate first runs the existing gitleaks command. If
that scan passes, it runs:

```sh
gitleaks git \
  --log-opts="$log_opts" \
  --no-banner \
  --redact \
  --ignore-gitleaks-allow \
  --report-format=json \
  --report-path="$report_file" \
  --exit-code 0
```

The second pass keeps the push path non-interactive while producing a report
of findings that would otherwise have been hidden by inline suppression.

### Supervisor proposal

When the JSON report contains findings, an embedded Python helper writes a
proposal into `SUPERVISE_QUEUE_DIR` using the existing proposal schema. The
proposal uses:

- `tool: "gitleaks-allow"`
- a text payload with the ref and each finding's file, line, rule, commit,
  and redacted code line
- a justification that tells the operator to approve only dummy test fixtures
  or confirmed false positives

Git-gate then waits for `<proposal-id>.response.json` for
`SUPERVISE_GITLEAKS_ALLOW_TIMEOUT_SECONDS`, defaulting to 300 seconds.
`approved` and `modified` responses allow the push; `rejected`, invalid
responses, invalid timeout configuration, or timeout refuse it.

### Supervisor UI

`TOOL_GITLEAKS_ALLOW` is added to the supervisor tool registry. The curses
supervisor renders the proposal as text and allows approval or rejection.
Modification is unavailable for this proposal type because there is no file
patch to apply. Approval from the TUI prompts for a non-empty reason and
writes that reason to the response/audit path.

### Tests

Unit tests assert that the rendered git-gate hook includes the second gitleaks
pass, supervisor queue fields, and fail-closed messages. Supervisor tests cover
the new tool constant, proposal archiving, and the required TUI approval
reason.
