FORGE-WP-ADHOC-2026-10-27: allow filename content digests in the receive scanner
The pinned Gitleaks profile now ignores one generic-api-key match: a text-extension filename whose value is exactly 64 lowercase hex digits. A bare keyword assignment of that digest still blocks. The installed Forgejo adapter is unchanged. Assistant: grok Assistant-Model: grok-4.7 Assistant-Session: 01a17f2b-8cc3-76f0-9bce-3b3444b9a5e9
This commit is contained in:
parent
2afdff68b1
commit
67f1cd044f
5 changed files with 75 additions and 4 deletions
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
| Kind | ID | Status | Lane | Source |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| workplan | FORGE-WP-ADHOC-2026-10-27 | active | — | workplans/ADHOC-2026-10-27.md |
|
||||
| workplan | FORGE-WP-0006 | finished | — | workplans/FORGE-WP-0006-independent-secret-detection.md |
|
||||
| workplan | FORGE-WP-0007 | finished | — | workplans/FORGE-WP-0007-reuse-private-reader-contract-review.md |
|
||||
| workplan | FORGE-WP-0008 | proposed | — | workplans/FORGE-WP-0008-secret-detection-residual-coverage.md |
|
||||
|
|
@ -15,6 +16,8 @@
|
|||
| workplan | FORGE-WP-0010 | finished | — | workplans/FORGE-WP-0010-receive-scanner-budget.md |
|
||||
| workplan | FORGE-WP-0011 | active | — | workplans/FORGE-WP-0011-forgejo-resource-requests.md |
|
||||
| workplan | RFORGE-WP-0002 | finished | — | workplans/RFORGE-WP-0002-forge-resource-economics-evidence.md |
|
||||
| task | FORGE-WP-ADHOC-2026-10-27-T01 | done | — | workplans/ADHOC-2026-10-27.md |
|
||||
| task | FORGE-WP-ADHOC-2026-10-27-T02 | wait | — | workplans/ADHOC-2026-10-27.md |
|
||||
| task | FORGE-WP-0006-T01 | done | — | workplans/FORGE-WP-0006-independent-secret-detection.md |
|
||||
| task | FORGE-WP-0006-T02 | done | — | workplans/FORGE-WP-0006-independent-secret-detection.md |
|
||||
| task | FORGE-WP-0006-T03 | cancel | — | workplans/FORGE-WP-0006-independent-secret-detection.md |
|
||||
|
|
|
|||
|
|
@ -56,8 +56,14 @@ 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.
|
||||
implicit baseline/ignore or override switch. Repository configuration cannot
|
||||
disable detection. The adapter's private config adds one reviewed
|
||||
`generic-api-key` match allowlist: a filename with a normal text extension
|
||||
(`.md`, `.markdown`, `.txt`, `.yaml`, `.yml`, `.json`, `.py`, or `.toml`) whose
|
||||
value is exactly 64 lowercase hex digits. A bare keyword assignment of that
|
||||
same digest still blocks. Any wider exception needs a separate reviewed
|
||||
mechanism with exact scope, approval and expiry. Keep this profile identical
|
||||
to Repo Manager's staged-scan config.
|
||||
|
||||
## Bounds and remaining coverage
|
||||
|
||||
|
|
|
|||
|
|
@ -159,9 +159,14 @@ func TestSmallInputParity(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
config := t.TempDir()
|
||||
write(t, filepath.Join(config, "scanner.toml"), []byte("[extend]\nuseDefault = true\n"))
|
||||
write(t, filepath.Join(config, "scanner.toml"), []byte(scannerConfig))
|
||||
write(t, filepath.Join(config, "ignore"), nil)
|
||||
privateKey := key(t)
|
||||
var digestRaw [32]byte
|
||||
if _, err := rand.Read(digestRaw[:]); err != nil {
|
||||
t.Fatal("cannot generate synthetic digest")
|
||||
}
|
||||
digest := hex.EncodeToString(digestRaw[:])
|
||||
var random [32]byte
|
||||
if _, err := rand.Read(random[:]); err != nil {
|
||||
t.Fatal("cannot generate synthetic token")
|
||||
|
|
@ -181,6 +186,8 @@ func TestSmallInputParity(t *testing.T) {
|
|||
{"token", canary, true},
|
||||
{"binary-token", append([]byte{0, 0}, canary...), true},
|
||||
{"inline-suppression", append([]byte("# gitleaks:allow\n"), privateKey...), true},
|
||||
{"filename-digest", []byte(" cards/access.md: " + digest + "\n"), false},
|
||||
{"bare-keyword-digest", []byte("access: " + digest + "\n"), true},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
stdin, e := scan(context.Background(), f, config, tc.input)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,21 @@ import (
|
|||
|
||||
const scannerHash = "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509"
|
||||
const scannerVersion = "8.30.1"
|
||||
|
||||
// scannerConfig extends the pinned scanner defaults with one reviewed shape.
|
||||
// Keep it identical to repo-manager secret_scan.CONFIG. The allowlist targets
|
||||
// the rule match, which is the filename stem through the digest: a trailing
|
||||
// newline is sometimes included in the line slice and then a line anchor misses.
|
||||
// A bare keyword assignment of the same digest still matches generic-api-key.
|
||||
const scannerConfig = `[extend]
|
||||
useDefault = true
|
||||
|
||||
[[allowlists]]
|
||||
description = "Filename key whose value is a lowercase 64-hex content digest"
|
||||
targetRules = ["generic-api-key"]
|
||||
regexTarget = "match"
|
||||
regexes = ['^[\w.-]+\.(?:md|markdown|txt|yaml|yml|json|py|toml):[ \t]+[a-f0-9]{64}$']
|
||||
`
|
||||
const maxInput = 512 * 1024
|
||||
const maxObject = 8 * 1024 * 1024
|
||||
const maxTotal = 64 * 1024 * 1024
|
||||
|
|
@ -333,7 +348,7 @@ func check(ctx context.Context, updates []update, binary string) (out result) {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
if os.WriteFile(filepath.Join(dir, "scanner.toml"), []byte("[extend]\nuseDefault = true\n"), 0600) != nil || os.WriteFile(filepath.Join(dir, "ignore"), nil, 0600) != nil {
|
||||
if os.WriteFile(filepath.Join(dir, "scanner.toml"), []byte(scannerConfig), 0600) != nil || os.WriteFile(filepath.Join(dir, "ignore"), nil, 0600) != nil {
|
||||
return fail(errors.New("scanner-config-unavailable"))
|
||||
}
|
||||
// Ref names can themselves contain recognized tokens. Never echo them.
|
||||
|
|
|
|||
40
workplans/ADHOC-2026-10-27.md
Normal file
40
workplans/ADHOC-2026-10-27.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
id: FORGE-WP-ADHOC-2026-10-27
|
||||
type: workplan
|
||||
title: "Allow filename content digests in the receive scanner"
|
||||
domain: financials
|
||||
repo: railiance-forge
|
||||
status: active
|
||||
owner: grok
|
||||
created: "2026-10-27"
|
||||
updated: "2026-10-27"
|
||||
---
|
||||
|
||||
## Allow a text-filename digest through generic-api-key
|
||||
|
||||
```task
|
||||
id: FORGE-WP-ADHOC-2026-10-27-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
The pinned Gitleaks 8.30.1 profile now allowlists one `generic-api-key` match:
|
||||
a filename with extension `.md`, `.markdown`, `.txt`, `.yaml`, `.yml`, `.json`,
|
||||
`.py`, or `.toml`, whose value is exactly 64 lowercase hex digits. A bare
|
||||
keyword assignment of the same digest still blocks, as do uppercase, short,
|
||||
quoted, and suffixed values. The profile is `scannerConfig` in
|
||||
`tools/receive-secret-scan` and matches Repo Manager's staged-scan config.
|
||||
Repository `.gitleaks.toml` files and inline allow comments remain ignored.
|
||||
|
||||
## Admit the updated adapter on the forge
|
||||
|
||||
```task
|
||||
id: FORGE-WP-ADHOC-2026-10-27-T02
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Source and the disposable scanner test do not change the installed pre-receive
|
||||
adapter. The running Forgejo hook keeps the previous profile until this
|
||||
executable is rebuilt and admitted. The emergent-security 0.2.7 push stays
|
||||
blocked until that admission.
|
||||
Loading…
Add table
Add a link
Reference in a new issue