security: add bounded receive-path secret detection adapter
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a12d13-27f2-7163-b8c0-f682af02772c
This commit is contained in:
parent
e395e5e9c1
commit
6d5c9e2abf
6 changed files with 1035 additions and 0 deletions
107
docs/receive-secret-detection.md
Normal file
107
docs/receive-secret-detection.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Forge receive-path secret detection
|
||||
|
||||
FORGE-WP-0006 implements the forge-owned part of RMGR-WP-0021-T03 and
|
||||
Custodian CUST-WP-0080 G07. The local Repo Manager scanner remains separate.
|
||||
The source adapter is not evidence of installed server enforcement.
|
||||
|
||||
## Boundary and ownership
|
||||
|
||||
The forge-owned, standalone Linux adapter composes with Forgejo's generated
|
||||
pre-receive dispatcher. It uses pinned Gitleaks 8.30.1 Linux amd64
|
||||
(SHA256 `88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509`).
|
||||
It requires `/usr/bin/git` and `/proc` but no Python runtime, issuer credential,
|
||||
network access, source checkout, or repository-controlled workflow. The current
|
||||
Forgejo 11.0.3 image has no Python interpreter; this keeps the runtime change to
|
||||
two reviewed executables and a hook fragment, subject to native admission.
|
||||
|
||||
The forge owns tool admission, hook installation and recovery. Repo Manager
|
||||
coordinates repository-control coverage; enablement owns reusable CI templates;
|
||||
source repositories own their workflows. Credential classification and
|
||||
revocation remain with existing incident/custody owners.
|
||||
|
||||
Do not overwrite generated `hooks/pre-receive` or `pre-receive.d/gitea`, or enable
|
||||
arbitrary user Git-hook editing. A reviewed additional executable in the existing
|
||||
`pre-receive.d/` dispatcher is the proposed integration. Server writers, frontend
|
||||
merge/edit/import paths, SSH and HTTP pushes, hook resynchronization, pod restart
|
||||
and new repository creation all need separate native acceptance. No admission
|
||||
or production coverage is inferred from disposable bare-repository tests.
|
||||
|
||||
## What is checked
|
||||
|
||||
Input is the exact `old-object new-object ref-name` batch from receive-pack.
|
||||
Malformed, duplicate, oversized, inconsistent or unsupported updates refuse.
|
||||
All updated ref names are scanned without echoing them. Ref exclusions are frozen
|
||||
from the pre-push snapshot; the traversal scans complete introduced reachable
|
||||
objects across every new tip and every parent, including annotated tags and
|
||||
direct object tags. A secret added and removed before the tip is still examined.
|
||||
|
||||
Blob data, raw commit/tag contents and tree names are scanned. Symlink contents
|
||||
are read as Git blobs; their filesystem targets are never followed. Changed trees
|
||||
containing external gitlinks refuse because the referenced repository is not
|
||||
covered. Git replacement objects and grafts cannot hide history. Partial/shallow
|
||||
repositories, missing objects and unverified traversal refuse. Receive-pack's
|
||||
quarantine object locations are preserved only for Git reads; the adapter never
|
||||
updates refs or copies objects into the primary store.
|
||||
|
||||
The scanner executable is opened without following a final symlink, checked for
|
||||
ownership/mode/regular-file shape and exact SHA256, and executed through its open
|
||||
file descriptor. Scanner config and ignore files are private temporary files;
|
||||
repository configuration, inline allow comments and ambient scanner settings do
|
||||
not disable detection. Child diagnostics/findings are discarded. Output contains
|
||||
only a fixed status/reason, counts, version and, for an object detection, its ID.
|
||||
Privately review the identified object; never paste its contents into an issue,
|
||||
State Hub, CI logs or chat. Standard Git output may independently display ref
|
||||
names; the adapter does not make Git's transport diagnostics a secret-safe log.
|
||||
|
||||
Exit 0 is `clean` in this declared scope; exit 1 is `blocked`; exit 2 is
|
||||
`unverified`. Both nonzero statuses must refuse the complete push. There is no
|
||||
implicit baseline/ignore or override switch. A false-positive exception needs a
|
||||
separate reviewed mechanism with exact scope, approval and expiry.
|
||||
|
||||
## Bounds and remaining coverage
|
||||
|
||||
Current bounds: 256 updates, 10,000 existing refs, 2,000 introduced objects,
|
||||
8 MiB per object, 64 MiB total, 512 KiB update input and 90 seconds for the check.
|
||||
Scanner calls also use the scanner's 15-second timeout. Object sizes and total
|
||||
bytes are checked before scanning payloads. Limits reject rather
|
||||
than skip. Large imports need an admitted path; do not disable the gate to pass
|
||||
an unreviewed import. Git/scanner versions and limits require native load testing.
|
||||
|
||||
The adapter does not rescan objects already reachable before the push, scan
|
||||
unreachable extra pack objects, LFS payloads, other repositories, runtime logs,
|
||||
artifacts or credentials outside Git. It is not an authorization boundary against
|
||||
a forge administrator or hostile same-UID process. Default scanner rules remain
|
||||
heuristic. Historical discovery and finding-to-response are independent work;
|
||||
removing a source value or rejecting a push does not revoke an exposed credential.
|
||||
CUST-WP-0073-T05's founder-deferred rotation is unchanged.
|
||||
|
||||
The 165 observed on-disk repository directories are a discovery count, not an
|
||||
admitted production-software denominator. The 22 reviewed repositories provide a
|
||||
named rollout starting scope; the owner must reconcile all production repositories,
|
||||
wikis, archives, mirrors and new-repository lifecycle before claiming DSM coverage.
|
||||
|
||||
## Build and disposable acceptance
|
||||
|
||||
Use the admitted Go toolchain (the current candidate uses official Go 1.26.9,
|
||||
verified against the SHA256 from go.dev release metadata). No third-party Go
|
||||
modules are required. Keep binaries and logs outside the repository:
|
||||
|
||||
```sh
|
||||
cd tools/receive-secret-scan
|
||||
FORGE_TEST_GITLEAKS=/reviewed/gitleaks go test -count=1 -v ./...
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false -o /tmp/receive-secret-scan .
|
||||
```
|
||||
|
||||
The root `make test-secret-detection` target requires `FORGE_TEST_GITLEAKS`
|
||||
and runs this module; set `GO` to the admitted compiler path where needed.
|
||||
|
||||
Tests use disposable Git servers, actual receive-pack quarantine and generated
|
||||
keys. Scanner-dependent tests explicitly skip without `FORGE_TEST_GITLEAKS`;
|
||||
skips are not acceptance. Native activation additionally needs exact original
|
||||
hook hashes, tool provenance, dry-run/diff, abort owner, rollback and value-free
|
||||
receipts under the accepted build-mode change rule.
|
||||
|
||||
References: [Git receive-pack and quarantine](https://git-scm.com/docs/git-receive-pack),
|
||||
[Git object traversal](https://git-scm.com/docs/git-rev-list),
|
||||
[Forgejo 11.0.3 generated hooks](https://codeberg.org/forgejo/forgejo/src/tag/v11.0.3/modules/repository/hooks.go),
|
||||
[Gitleaks release](https://github.com/gitleaks/gitleaks/releases/tag/v8.30.1).
|
||||
Loading…
Add table
Add a link
Reference in a new issue