|
| 1 | +# SPDX-License-Identifier: MPL-2.0 |
| 2 | +# |
| 3 | +# Gitleaks configuration — estate baseline. |
| 4 | +# |
| 5 | +# WHY THIS FILE EXISTS |
| 6 | +# -------------------- |
| 7 | +# Until #500 the estate's gitleaks step carried `continue-on-error: true`, so |
| 8 | +# the primary secret scanner could not fail a PR. #500 made it gate for real |
| 9 | +# (`gitleaks detect --exit-code 1` against a checksum-verified binary). The |
| 10 | +# first honest run then surfaced 15 findings in this repo — every one a false |
| 11 | +# positive: module integrity hashes, package UUIDs, a public GPG fingerprint, |
| 12 | +# a Haskell type *name*, and synthetic `.example` fixtures. |
| 13 | +# |
| 14 | +# That is very likely the trap the `continue-on-error` was papering over: turn |
| 15 | +# the gate on with no allowlist, get a wall of red that is entirely noise, |
| 16 | +# someone reverts, and the estate is back to a scanner that cannot fail. This |
| 17 | +# config is therefore the *unblocker* for re-pinning callers onto the real |
| 18 | +# gate across the estate. |
| 19 | +# |
| 20 | +# DESIGN RULE — KEEP THIS WHEN EDITING |
| 21 | +# ------------------------------------ |
| 22 | +# Allowlist by PATTERN CLASS, anchored to the WHOLE value. Never allowlist a |
| 23 | +# path wholesale because "that file is fine", and never disable a rule. |
| 24 | +# |
| 25 | +# Every `regexes` entry below is anchored `^...$` on purpose. An unanchored |
| 26 | +# entry is a substring match, which silently blinds the scanner: a pattern |
| 27 | +# like `(?i)test|example` would allowlist the real AWS key |
| 28 | +# `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` purely because "EXAMPLE" appears |
| 29 | +# inside it. That was caught by canary-testing an earlier draft of this file — |
| 30 | +# if you add a pattern here, plant a realistic secret and confirm it is still |
| 31 | +# detected before you commit. |
| 32 | +# |
| 33 | +# `paths` entries are limited to formats that cannot legitimately hold a live |
| 34 | +# credential (lockfile integrity hashes, dependency UUIDs). If you want to |
| 35 | +# allowlist any other path wholesale, that is a signal the secret should be |
| 36 | +# removed from the tree instead. |
| 37 | + |
| 38 | +[extend] |
| 39 | +useDefault = true |
| 40 | + |
| 41 | +[allowlist] |
| 42 | +description = "Estate baseline: dependency metadata, anchored placeholder shapes, published test vectors" |
| 43 | + |
| 44 | +paths = [ |
| 45 | + # Lockfiles: the "secrets" are SHA-256 *integrity hashes* of public registry |
| 46 | + # modules. A lockfile has no legitimate reason to contain a credential. |
| 47 | + '''(^|/)deno\.lock$''', |
| 48 | + '''(^|/)package-lock\.json$''', |
| 49 | + '''(^|/)yarn\.lock$''', |
| 50 | + '''(^|/)Cargo\.lock$''', |
| 51 | + # Julia project manifests: entries are public package UUIDs from the General |
| 52 | + # registry, which trip the generic-api-key entropy heuristic. |
| 53 | + '''(^|/)Project\.toml$''', |
| 54 | + '''(^|/)Manifest\.toml$''', |
| 55 | +] |
| 56 | + |
| 57 | +regexes = [ |
| 58 | + # --- documented placeholder shapes (all anchored to the whole value) ------ |
| 59 | + |
| 60 | + # Ellipsis-terminated illustrative values: `abc123def456...` (carrying an |
| 61 | + # explicit "// Would be real token" comment), `1234567890_abc123...` inside a |
| 62 | + # fenced ASCII mockup of terminal output. A real credential is never elided. |
| 63 | + '''^[A-Za-z0-9_\-]{4,}\.\.\.$''', |
| 64 | + |
| 65 | + # Repeated-character masks: a provider prefix followed by a long run of a |
| 66 | + # single filler character (the GitLab, GitHub and OpenAI token prefixes are |
| 67 | + # all commonly masked this way in docs). A real credential never repeats one |
| 68 | + # character 8+ times. |
| 69 | + # |
| 70 | + # Deliberately described rather than shown: writing the masked token out |
| 71 | + # literally here makes THIS file trip the estate's Hypatia secret detector |
| 72 | + # ("Secret found: GitLab PAT"), which is how that was found. Do not add |
| 73 | + # literal example tokens to this file, even inside comments. |
| 74 | + '''^[A-Za-z0-9_\-]*?(x{8,}|X{8,}|y{8,}|z{8,}|0{12,}|\*{6,})[A-Za-z0-9_\-]*$''', |
| 75 | + |
| 76 | + # Monotonic hex walks used as filler: `0123456789abcdef` repeated, |
| 77 | + # `ABCDEF0123456789ABCDEF0123456789`, `0a1b2c3d4e5f6a7b…` (ascending nibble |
| 78 | + # pairs). These are keyboard-walk fillers, never generated key material. |
| 79 | + '''^(?i:0123456789abcdef|abcdef0123456789|0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1|deadbeef|cafebabe)+$''', |
| 80 | + |
| 81 | + # Snake-case self-labelled fixtures, e.g. `sg_test_api_key_abc123`. Anchored |
| 82 | + # and shape-constrained: an all-lowercase snake_case string with an explicit |
| 83 | + # test/demo marker as a bounded segment. Real API keys carry mixed case, |
| 84 | + # digits and symbols and will not match this. |
| 85 | + '''^[a-z]{2,6}_(test|demo|fake|sample|example|dummy|placeholder)_[a-z0-9_]{2,40}$''', |
| 86 | + |
| 87 | + # --- published test vectors ---------------------------------------------- |
| 88 | + # RFC 8032 §7.1 TEST 1 Ed25519 seed and the public key it derives. These |
| 89 | + # exact values are printed in the RFC; checking that one yields the other is |
| 90 | + # the entire point of the interoperability test. |
| 91 | + '''^9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60$''', |
| 92 | + '''^d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a$''', |
| 93 | + |
| 94 | + # --- public-by-definition / non-secret identifiers ------------------------ |
| 95 | + # OpenPGP 40-hex fingerprints. A fingerprint *identifies* a key; it is |
| 96 | + # published deliberately (SECURITY.md, audit records, keyservers) and |
| 97 | + # discloses nothing. Secret key *material* is PEM-armoured and is caught by |
| 98 | + # the dedicated private-key rules, which are NOT allowlisted here. |
| 99 | + '''^[A-F0-9]{40}$''', |
| 100 | + |
| 101 | + # Cryptographic *type names* appearing in source/design docs, e.g. the |
| 102 | + # Haskell declaration `data Ed25519KeyPair = Ed25519KeyPair`. A bare |
| 103 | + # CamelCase identifier with no digits or separators is a type, not a key. |
| 104 | + '''^(Ed25519|X25519|Curve25519|RSA|ECDSA|EdDSA|Secp256k1)[A-Za-z]*$''', |
| 105 | +] |
0 commit comments