git-gate: match the bottle's injected known secrets, not just gitleaks patterns #346

Open
opened 2026-07-11 16:54:11 -04:00 by didericis-claude · 0 comments
Collaborator

Problem

There's an asymmetry in how the two exfiltration chokepoints decide what's a secret:

  • Egress (HTTP/DNS) blocks by known literal value — the bottle's injected env secrets are registered as known_secrets and blocked regardless of shape (plus token_patterns for shape-based catches).
  • git-gate (push) blocks only by gitleaks pattern in the pre-receive hook. It has no knowledge of the specific secrets we injected into the bottle.

So a secret that doesn't match any bundled gitleaks rule can be exfiltrated via git push even though the exact same value is blocked on the egress path. gitleaks only reliably flags separator-prefixed, high-entropy tokens (ghp_, xoxb-, glpat-, …) in a bare/keyword-free context; it does not catch arbitrary API keys, a bare hex/base64 blob, the canonical AWS example key (allowlisted), or provider keys that need a nearby keyword (AWS/GCP). A malicious agent can therefore embed a known-injected secret in a committed file in a shape gitleaks ignores and push it out.

Discovered via

The sandbox-escape integration test (tests/integration/test_sandbox_escape.py, attack 5). We had to reshape its planted secrets to gitleaks-pattern-matchable values (github/slack/gitlab) just to get the push actually rejected — which itself demonstrates the gap: the original fixtures (anthropic key, AWS example key, generic hex) were blocked on egress but sailed through git-gate.

Ask

Feed the bottle's known injected secrets into git-gate's pre-receive scan so push is protected symmetrically with egress. Options:

  1. Generate a per-bottle gitleaks config with exact-match rules (or a regexTarget on the literal values) for each known_secret, layered on top of the bundled ruleset. Then a single gitleaks git invocation covers both bundled patterns and the injected values.
  2. Add a known-secret scan to the pre-receive hook (independent of gitleaks) that greps the incoming refs' blobs/diffs for any registered known-secret value and rejects on match — mirroring the egress known_secrets mechanism, ideally sharing the same source of truth.

Either way the goal is one shared definition of "the secrets this bottle holds" enforced at every egress boundary (HTTP, DNS, git).

Notes

  • Keep it value-exact and constant-time-ish where feasible; don't log the secret values.
  • Once git-gate matches injected secrets, the sandbox-escape fixtures could go back to arbitrary shapes (or add a case that's specifically NOT gitleaks-matchable to assert the known-secret path catches it).

Ref: surfaced alongside the sandbox-escape host-key/gitleaks fixture fixes.

## Problem There's an asymmetry in how the two exfiltration chokepoints decide what's a secret: - **Egress (HTTP/DNS)** blocks by **known literal value** — the bottle's injected env secrets are registered as `known_secrets` and blocked regardless of shape (plus `token_patterns` for shape-based catches). - **git-gate (push)** blocks **only by gitleaks pattern** in the pre-receive hook. It has no knowledge of the specific secrets we injected into the bottle. So a secret that doesn't match any bundled gitleaks rule can be exfiltrated via `git push` even though the exact same value is blocked on the egress path. gitleaks only reliably flags separator-prefixed, high-entropy tokens (`ghp_`, `xoxb-`, `glpat-`, …) in a bare/keyword-free context; it does **not** catch arbitrary API keys, a bare hex/base64 blob, the canonical AWS example key (allowlisted), or provider keys that need a nearby keyword (AWS/GCP). A malicious agent can therefore embed a known-injected secret in a committed file in a shape gitleaks ignores and push it out. ## Discovered via The sandbox-escape integration test (`tests/integration/test_sandbox_escape.py`, attack 5). We had to reshape its planted secrets to gitleaks-pattern-matchable values (github/slack/gitlab) just to get the push actually rejected — which itself demonstrates the gap: the *original* fixtures (anthropic key, AWS example key, generic hex) were blocked on egress but sailed through git-gate. ## Ask Feed the bottle's known injected secrets into git-gate's pre-receive scan so push is protected symmetrically with egress. Options: 1. **Generate a per-bottle gitleaks config** with exact-match rules (or a regexTarget on the literal values) for each `known_secret`, layered on top of the bundled ruleset. Then a single `gitleaks git` invocation covers both bundled patterns and the injected values. 2. **Add a known-secret scan** to the pre-receive hook (independent of gitleaks) that greps the incoming refs' blobs/diffs for any registered known-secret value and rejects on match — mirroring the egress `known_secrets` mechanism, ideally sharing the same source of truth. Either way the goal is one shared definition of "the secrets this bottle holds" enforced at every egress boundary (HTTP, DNS, git). ## Notes - Keep it value-exact and constant-time-ish where feasible; don't log the secret values. - Once git-gate matches injected secrets, the sandbox-escape fixtures could go back to arbitrary shapes (or add a case that's specifically NOT gitleaks-matchable to assert the known-secret path catches it). Ref: surfaced alongside the sandbox-escape host-key/gitleaks fixture fixes.
didericis added the Kind/Security label 2026-07-11 19:19:29 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: didericis/bot-bottle#346