mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-26 14:50:55 +02:00
docs(changelog): describe the four fail-open classes as shipped
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9c15f7bf45
commit
2144829381
+19
-18
@@ -2,42 +2,43 @@
|
||||
|
||||
## [1.87.7.0] - 2026-09-22
|
||||
|
||||
**Pre-push credential checks close four bypass paths.**
|
||||
**Every target remote resolves against its own base.**
|
||||
**Pre-push credential checks close four bypass classes completely.**
|
||||
**A remote the hook cannot see is never assumed to already have your history.**
|
||||
|
||||
This release closes four paths where the pre-push credential hook exited 0 on pushed diffs carrying live keys. Pushing to a remote other than origin no longer evaluates diff ranges against origin/main. Unfetched remote tips and boundary-crossing proximity patterns now fail closed or scan with overlap instead of slipping past the hook.
|
||||
This release closes four ways the pre-push credential hook exited 0 on a push that carried a live key. Each is a class rather than a single input: pushing somewhere other than `origin`, a remote tip the local clone does not have, a proximity pattern cut in half by a scan slice, and invisible padding that moved that cut. The gate that ships with it exercises seven shapes across those four classes, each against real git repositories.
|
||||
|
||||
### The four numbers that matter
|
||||
### The numbers that matter
|
||||
|
||||
Source: scenarios in `test/redact-prepush-fail-open.sh`, comparing the baseline scanner from v1.87.5.0 with this release. Run `bash test/redact-prepush-fail-open.sh` or `bun test test/redact-prepush-fail-open.test.ts` to execute the full gate. These are deterministic pre-push protocol checks covering range resolution, boundary slicing, and credential detection.
|
||||
Source: `test/redact-prepush-fail-open.sh`, run against the scanner from v1.87.5.0 and against this release. Deterministic protocol checks against real git repositories, no network.
|
||||
|
||||
| Metric | Before | After | Δ |
|
||||
|---|---:|---:|---:|
|
||||
| Fail-open pre-push scenarios | 4 | 0 | -4 |
|
||||
| Target remotes scoped to push target | No | Yes | Scoped |
|
||||
| Fail-open gate scenarios (exit 0 with a credential in the push) | 7 | 0 | -7 |
|
||||
| Gate scenarios passing | 21/30 | 30/30 | +9 |
|
||||
| Proximity slice overlap | 0 KiB | 16 KiB | +16 KiB |
|
||||
| Fail-open gate scenarios passing | 22/26 | 26/26 | +4 |
|
||||
| Clean first push in a SHA-256 repository | blocked | allowed | fixed |
|
||||
|
||||
Pushing to a secondary remote previously produced an empty diff range when local HEAD matched origin, letting secret-bearing commits ship with exit 0. That probe is now scoped directly to the push target.
|
||||
The seven fail-open scenarios are the four classes above. Pushing to a remote the hook has no tracking refs for used to be judged against `origin`'s tip, so history that only `origin` had seen was treated as already delivered everywhere.
|
||||
|
||||
### What this means for developers
|
||||
|
||||
Your pre-push hook blocks secrets when pushing to mirrors, staging targets, and non-origin remotes. Large minified bundles are sliced with overlap and scanned, reporting the matched rule instead of a generic size failure. Run `bun test test/redact-prepush-fail-open.test.ts` to verify your pre-push hook configuration.
|
||||
Your pre-push hook now blocks a credential on its way to a mirror, a staging remote or a push URL, including a credential that `origin` already has but the destination does not. Force-pushes where the local clone is missing the remote's tip are scanned across the whole reachable history instead of a guessed range — slower on that rare path, and correct. A clean first push in a SHA-256 repository is no longer hard-blocked.
|
||||
|
||||
### Itemized changes
|
||||
|
||||
#### Fixed
|
||||
|
||||
- **Scoping default branch probes to push remote:** Pushing to a non-origin remote when HEAD matched origin/main previously resolved an empty diff range against origin, exiting 0 and allowing commits with credentials to push unscanned. The default branch probe now targets the destination remote and falls back to ref-listing when unresolvable.
|
||||
- **Fail closed on absent remote tips:** Well-shaped 40-character hex tips that do not exist in the local object store previously triggered a merge-base guess that could resolve to an empty diff. The hook now treats absent remote tips as unscannable ranges, blocking the push and instructing the user to run git fetch.
|
||||
- **Slice boundary overlap for proximity rules:** Added lines in large diffs are sliced in 768 KiB chunks. Patterns that require qualifying context within a character window previously missed secrets when the label and value straddled a boundary without overlap. Slices now overlap by 16 KiB.
|
||||
- **Zero-width character stripping on ingest:** Normalization of zero-width characters now happens before slice budgeting so that raw byte counts match what the detection engine inspects. Invisible padding can no longer push proximity pairs across slice boundaries.
|
||||
- **Dynamic empty-tree object resolution:** The fallback diff range used a hardcoded SHA-1 empty-tree object id, which does not exist in a SHA-256 repository and hard-blocked every legitimate first push of a new branch there. The id is now obtained from `git hash-object -t tree --stdin`, which is correct under either hash algorithm.
|
||||
- **Over-budget single line slicing:** Minified files with single lines exceeding the chunk budget are now sliced into overlapping chunks and inspected, identifying the specific credential finding rather than exiting with an uninspected size error.
|
||||
- **Push target scoping:** The default-branch probe and the "already on the remote" exclusion now follow the remote the push actually targets. A push to a configured remote other than `origin` is anchored on that remote. A push to a URL is described by no remote-tracking ref at all, so nothing is excluded and everything reachable is scanned; previously it borrowed `origin`'s base and excluded every commit any remote had seen.
|
||||
- **Absent remote tips:** When git names a remote tip the local object database does not have, the local tracking refs are demonstrably stale for that ref, so no local narrowing is applied and the whole reachable history is scanned. A guessed range is never trusted in that case, whether it comes back empty or not.
|
||||
- **Slice boundary overlap for proximity rules:** Added lines are scanned in 768 KiB slices that now overlap by 16 KiB, so a pattern that needs its label nearby cannot lose it at a seam. A single line longer than the overlap now contributes its suffix to the seam instead of nothing.
|
||||
- **Zero-width characters on ingest:** Zero-width characters are stripped before slices are budgeted, so the budget measures the same text the detection engine inspects and invisible padding cannot move a seam between a label and its secret.
|
||||
- **Empty-tree object resolution:** The fallback range's empty-tree object id is obtained from `git hash-object -t tree --stdin` instead of a hardcoded SHA-1 value that does not exist in a SHA-256 repository.
|
||||
- **Over-budget single lines:** A minified file whose single line exceeds the slice budget is sliced with overlap and actually read, so the finding names the credential instead of reporting an unscanned size error.
|
||||
|
||||
#### Added
|
||||
### For contributors
|
||||
|
||||
- **Pre-push fail-open gate:** Added `test/redact-prepush-fail-open.sh` and `test/redact-prepush-fail-open.test.ts` verifying all 26 pre-push range resolution, fail-closed, and boundary detection invariants.
|
||||
- **Pre-push fail-open gate:** `test/redact-prepush-fail-open.sh` (30 scenarios) and `test/redact-prepush-fail-open.test.ts`, which runs it as part of the free suite.
|
||||
- `test/redact-prepush-scan-range.test.ts`: the URL-push case in the S1 block still asserts the hook does not error on an unconfigured name, and now also asserts the push is blocked, because the fixture's secret really is delivered to that URL.
|
||||
|
||||
## [1.87.5.0] - 2026-09-17
|
||||
|
||||
|
||||
Reference in New Issue
Block a user