Ignore Git object IDs in entropy scan
ober
6ad5cd8240a0f415c75af5c966449736ba424b7c
--- a/gitsafe/scanner.ss +++ b/gitsafe/scanner.ss @@ -194,9 +194,9 @@ ;; --- Context detection for noisy entropy patterns --- ;; Pattern IDs whose matches should be suppressed in non-secret contexts: - ;; URL paths, OCI content digests, and Dockerfile FROM lines. These are - ;; the broad entropy patterns; precise patterns (AWS keys, GitHub PATs, - ;; etc.) are intentionally excluded so credentials embedded in URLs or + ;; URL paths, OCI content digests, Git object IDs, and Dockerfile FROM lines. + ;; These are the broad entropy patterns; precise patterns (AWS keys, GitHub + ;; PATs, etc.) are intentionally excluded so credentials embedded in URLs or ;; image refs are still caught. (def *url-sensitive-patterns* '(high-entropy-base64 high-entropy-hex)) @@ -217,6 +217,21 @@ #t] [else (loop (+ i 1))])))) + (def *git-sha-prefix-re* + (re "(?i:(^|[^A-Za-z0-9_])(?:old\\s+head|new\\s+head|head|commit|revision|rev|object|tree|parent|merge|merged|checkout|reset|rebase|tag)[^A-Za-z0-9_]{0,24}$)")) + + (def (git-object-id-length? s) + (let ([n (string-length s)]) + (or (= n 40) (= n 64)))) + + ;; Returns #t when a high-entropy hex match is in a Git object-id context, + ;; e.g. "OLD HEAD: <sha1>", "commit <sha1>", or "parent <sha1>". + (def (in-git-sha-context? line match-start matched) + (and (git-object-id-length? matched) + (let* ([prefix-start (max 0 (- match-start 96))] + [prefix (substring line prefix-start match-start)]) + (and (re-search *git-sha-prefix-re* prefix) #t)))) + ;; Returns #t when the match at match-start is the value of a content ;; digest reference like "sha256:<hex>", "sha384:<hex>", or "sha512:<hex>". ;; These appear in OCI/Docker image refs (e.g. image@sha256:abc…), @@ -311,10 +326,12 @@ (if (not matched) (loop (cdr pats) results) ;; For high-noise entropy patterns, suppress matches inside - ;; URLs, OCI content digests, and Dockerfile FROM lines. + ;; URLs, OCI content digests, Git object IDs, and Dockerfile + ;; FROM lines. (if (and (member (secret-pattern-id pat) *url-sensitive-patterns*) (or (in-url-context? line (re-match-start m)) (in-digest-context? line (re-match-start m)) + (in-git-sha-context? line (re-match-start m) matched) (in-dockerfile-from? line))) (loop (cdr pats) results) ;; Run validator if present --- a/test/test-gitsafe.ss +++ b/test/test-gitsafe.ss @@ -320,6 +320,19 @@ c)]) (check-equal? '() findings))) + (test-case "scan-content: git shas are not flagged as entropy secrets" + (let* ([c (default-config)] + [findings (scan-content "rebase.log" + (string-append + "OLD HEAD: 89ca49128bc2f2f38480be0e697d02a3a5339121\n" + "commit 89ca49128bc2f2f38480be0e697d02a3a5339121\n" + "parent 89ca49128bc2f2f38480be0e697d02a3a5339121\n") + c)]) + (check-equal? '() (filter (lambda (f) + (member (finding-pattern-id f) + '(high-entropy-base64 high-entropy-hex))) + findings)))) + (test-case "scan-content: URLs in comments are not flagged as entropy secrets" ;; Long URL paths match [A-Za-z0-9/]{40+} but are not secrets. (let* ([c (default-config)]