FORGE-WP-ADHOC-2026-10-27: allow filename content digests in the receive scanner
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

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:
tegwick 2026-10-27 22:30:34 +01:00
parent 2afdff68b1
commit 67f1cd044f
5 changed files with 75 additions and 4 deletions

View file

@ -8,6 +8,7 @@
| Kind | ID | Status | Lane | Source | | 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-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-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 | | 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-0010 | finished | — | workplans/FORGE-WP-0010-receive-scanner-budget.md |
| workplan | FORGE-WP-0011 | active | — | workplans/FORGE-WP-0011-forgejo-resource-requests.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 | | 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-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-T02 | done | — | workplans/FORGE-WP-0006-independent-secret-detection.md |
| task | FORGE-WP-0006-T03 | cancel | — | workplans/FORGE-WP-0006-independent-secret-detection.md | | task | FORGE-WP-0006-T03 | cancel | — | workplans/FORGE-WP-0006-independent-secret-detection.md |

View file

@ -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 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 `unverified`. Both nonzero statuses must refuse the complete push. There is no
implicit baseline/ignore or override switch. A false-positive exception needs a implicit baseline/ignore or override switch. Repository configuration cannot
separate reviewed mechanism with exact scope, approval and expiry. 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 ## Bounds and remaining coverage

View file

@ -159,9 +159,14 @@ func TestSmallInputParity(t *testing.T) {
} }
}() }()
config := t.TempDir() 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) write(t, filepath.Join(config, "ignore"), nil)
privateKey := key(t) 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 var random [32]byte
if _, err := rand.Read(random[:]); err != nil { if _, err := rand.Read(random[:]); err != nil {
t.Fatal("cannot generate synthetic token") t.Fatal("cannot generate synthetic token")
@ -181,6 +186,8 @@ func TestSmallInputParity(t *testing.T) {
{"token", canary, true}, {"token", canary, true},
{"binary-token", append([]byte{0, 0}, canary...), true}, {"binary-token", append([]byte{0, 0}, canary...), true},
{"inline-suppression", append([]byte("# gitleaks:allow\n"), privateKey...), 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) { t.Run(tc.name, func(t *testing.T) {
stdin, e := scan(context.Background(), f, config, tc.input) stdin, e := scan(context.Background(), f, config, tc.input)

View file

@ -23,6 +23,21 @@ import (
const scannerHash = "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509" const scannerHash = "88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509"
const scannerVersion = "8.30.1" 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 maxInput = 512 * 1024
const maxObject = 8 * 1024 * 1024 const maxObject = 8 * 1024 * 1024
const maxTotal = 64 * 1024 * 1024 const maxTotal = 64 * 1024 * 1024
@ -333,7 +348,7 @@ func check(ctx context.Context, updates []update, binary string) (out result) {
} }
return nil 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")) return fail(errors.New("scanner-config-unavailable"))
} }
// Ref names can themselves contain recognized tokens. Never echo them. // Ref names can themselves contain recognized tokens. Never echo them.

View 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.