gstack-redact-prepush: generated vector assets produce ~200 MEDIUM findings per file (SVG path floats are Luhn-valid ~1-in-6 of the time)
- Dominant language
- TypeScript
- Stars
- 133k
- Forks
- 19.9k
- Avg merge
- 18h 46m
- Merged PRs (30d)
- 26
Description
**Version:** gstack 1.81.0.0 · macOS 15.7.9 · bun
## Summary
Committing the output of `/diagram` trips the pre-push guard hard. A single PR adding five diagrams reported:
```
gstack-redact-prepush: 1511 MEDIUM finding(s) in pushed diff (PII/internal). Not blocking.
```
Zero HIGH, nothing sensitive. Every finding is geometry: mermaid emits full-precision floats into SVG path data and excalidraw scene JSON, and long digit runs collide with `pii.cc` and `pii.phone.e164`.
The rules are working as designed. The problem is that a generated vector asset is a dense field of exactly the shape these two rules match, and the pre-push path has no way to exclude it.
## Reproduction
```bash
# any mermaid diagram rendered through /diagram
gstack-redact --from-file docs/diagrams/settlement-mode-routing.svg --json
gstack-redact --from-file docs/diagrams/settlement-mode-routing.excalidraw --json
gstack-redact --from-file docs/diagrams/settlement-mode-routing.mmd --json
```
| File | Bytes | Findings | Rules |
|---|---|---|---|
| `.mmd` (source) | 1,199 | **0** | — |
| `.svg` | 50,484 | **34 MEDIUM** | `pii.cc` ×18, `pii.phone.e164` ×16 |
| `.excalidraw` | 42,579 | **177 MEDIUM** | `pii.phone.e164` ×164, `pii.cc` ×13 |
Five diagrams in one PR → ~1,500. PNGs contribute nothing (git treats them as binary, so no added lines).
## Why `pii.cc` fires 18 times on a diagram
`pii.cc` is correctly Luhn-gated (`validate: (span, match) => !insideUuid(match) && luhnValid(span)`, `lib/redact-patterns.ts:755`). But a random digit run passes Luhn roughly 1 time in 10, and an SVG is full of them:
```
$ # 13-19 digit runs in one 50KB rendered diagram
13-19 digit candidate runs: 103
of which Luhn-valid: 18 (17.5%)
examples: 58985137939453, 5593795776367, 599998474121094
```
Those are `path d="M …"` coordinates at float precision. 18 Luhn-valid runs out of 103 candidates reproduces the tool's 18 `pii.cc` findings exactly. Luhn cannot help here — at this candidate density it is a 1-in-6 filter, not a 1-in-∞ one.
`pii.phone.e164` has no equivalent checksum, which is why it dominates the excalidraw count (164).
## Why this matters
This is the failure mode `redact-patterns.ts:214-220` already names, in the maintainer's own words, about parcel IDs in land/title repos:
> a guardrail that cries wolf on the domain's primary identifier trains people to wave the warning through, which is how a real HIGH finding eventually gets ignored
Closed issue #2304 established the same pressure from the other direction — noise pushes users onto `GSTACK_REDACT_PREPUSH=skip`, which disables **all** scanning including HIGH.
Any repo that commits generated diagrams, charts, or vector art hits this on every such PR. `/diagram` is a gstack skill, so gstack's own output trips gstack's own guard.
## Why it can't be configured away today
`bin/gstack-redact-prepush` deliberately discards file identity while collecting added lines:
```js
// line ~285
if (!inHunk && (line.startsWith("+++") || line.startsWith("---"))) continue;
if (line.startsWith("+")) added.push(line.slice(1));
```
The `+++ b/path` header is parsed and dropped, so by the time the engine runs, there is no way to say "skip generated assets". The only control is `GSTACK_REDACT_PREPUSH=skip`, which is all-or-nothing and gives up HIGH detection — the outcome #2304 was closed to avoid.
`--allowlist` exists on the engine but takes exact spans, so it cannot express "this file is generated" for thousands of coordinates.
## Suggested fixes
**1. Retain per-file context in the pre-push hook (preferred, small).** The `+++ b/` header is already being parsed at that line — keep it instead of discarding it, and scan per file. That enables a default skip-list for generated/derived assets (`*.svg`, `*.excalidraw`, `*.min.js`, `*.map`, lockfiles) and, as a bonus, lets findings name the file they came from, which they currently cannot.
**2. Add a numeric-field context guard, in the spirit of `insideUuid`.** `insideUuid` already sets the precedent: a narrow exemption for a digit run in a known non-PII container. The analogue is a digit run inside SVG path data or a bare numeric JSON array. Narrower than (1) but helps every entry point, not just pre-push.
(1) alone would fix the reported case. (1)+(2) together also cover `--from-file` and `/cso` runs over the same assets.
## What I am *not* suggesting
Lowering `pii.cc`/`pii.phone.e164` severity, or dropping Luhn. Both rules are behaving correctly; the input is the problem.
## Environment
- gstack 1.81.0.0 (`~/.claude/skills/gstack`, git install)
- macOS 15.7.9, bun
- Assets produced by gstack's own `/diagram` skill, rendered `ENGINE=browse`
Contributor guide
Research direction
Start with bin/gstack-redact-prepush around the added-line collection near line 285, then review the relevant matching context in lib/redact-patterns.ts at lines 214-220 and 755. Reproduce with the listed --from-file commands and verify that per-file context can suppress generated assets while HIGH findings remain scanned; the resulting findings should also retain their source file identity.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, typescript
- Domain
- security, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100