fix(allowlist): stop in-repo .gitsafeignore from disabling scanning
ober
41150d05cebab61c717cecf8ef26e5df2f67cc06
--- a/gitsafe/allowlist.ss +++ b/gitsafe/allowlist.ss @@ -71,8 +71,12 @@ (read-file-lines path)))) ;; --- Check if a file path matches any ignorefile pattern --- + ;; The ignorefile lives in the scanned (hostile) tree, so a match-all glob + ;; ("**") must not skip every file; such globs are refused here. (def (ignored-file? path ignore-patterns) - (any (lambda (glob) (glob-match? glob path)) + (any (lambda (glob) + (and (not (match-all-glob? glob)) + (glob-match? glob path))) ignore-patterns)) ;; --- Check if a finding fingerprint is ignored exactly --- --- a/gitsafe/scanner.ss +++ b/gitsafe/scanner.ss @@ -694,11 +694,13 @@ ;; Round up to the next MB for the warning message. (quotient (+ n (- (* 1024 1024) 1)) (* 1024 1024))) - (def (filter-ignored-findings findings ignore-pats config) + ;; Fingerprint suppression honors only a trusted baseline. Fingerprints from + ;; the in-repo .gitsafeignore are hostile input: a predictable + ;; file:pattern-id:line line would otherwise silence a known finding. + (def (filter-ignored-findings findings config) (filter (lambda (f) - (let ([fp (finding-fingerprint f)]) - (not (or (ignored-fingerprint? fp ignore-pats) - (member fp (gitsafe-config-baseline-fingerprints config)))))) + (not (member (finding-fingerprint f) + (gitsafe-config-baseline-fingerprints config)))) findings)) (def *git-empty-tree* "4b825dc642cb6eb9a060e54bf8d69288fbee4904") @@ -782,7 +784,6 @@ [(list path hunks) (scan-diff-hunks hunks config)])) work-items) - ignore-pats config)))) ;; --- Top-level: scan push range --- @@ -831,7 +832,6 @@ [(list path hunks) (scan-diff-hunks hunks config)])) work-items) - ignore-pats config))))))) (def (entry->string entry) @@ -920,7 +920,6 @@ (scan-content path content config) '()))) to-scan) - ignore-pats config) (length to-scan))))) --- a/test/test-gitsafe.ss +++ b/test/test-gitsafe.ss @@ -328,6 +328,14 @@ "config.env:generic-secret:13" '("config.env:generic-secret:12")))) + (test-case "ignored-file?: match-all glob ** does not skip files" + ;; A hostile .gitsafeignore line "**" must not skip every path. + (check-equal? #f (ignored-file? "src/secret.env" '("**"))) + (check-equal? #f (ignored-file? "a/b/c.txt" '("**"))) + ;; Narrow ignore globs still work. + (check-equal? #t (ignored-file? "vendor/foo.go" '("vendor/**"))) + (check-equal? #f (ignored-file? "src/main.ss" '("vendor/**")))) + )) ;; ============================================================ @@ -742,6 +750,33 @@ findings)]) (check-equal? '() entropy-findings))) + (test-case ".gitsafeignore ** does not skip files (integration)" + ;; A hostile in-repo .gitsafeignore containing "**" must not cause all + ;; files to be skipped; the secret is still scanned and reported. + (let ([ignore-path ".gitsafeignore"]) + (write-test-file! ignore-path "**\n") + (let* ([c (default-config)] + [findings (scan-files '("test/fixtures/fake-secrets.txt") c)]) + (delete-test-file! ignore-path) + (check-predicate findings pair?)))) + + (test-case ".gitsafeignore fingerprint line from scanned tree does not suppress" + ;; Fingerprint suppressions must come from a trusted source; a + ;; predictable file:pattern-id:line entry in the in-repo .gitsafeignore + ;; must not silence the finding. + (let* ([ignore-path ".gitsafeignore"] + [target "test/fixtures/tmp-fp-secret.env"] + [fp (finding-fingerprint-value target 'aws-access-key 1)]) + (write-test-file! target "AKIAIOSFODNN7EXAMPLE\n") + (write-test-file! ignore-path (string-append fp "\n")) + (let* ([c (default-config)] + [findings (scan-files (list target) c)] + [aws (filter (lambda (f) (eq? (finding-pattern-id f) 'aws-access-key)) + findings)]) + (delete-test-file! ignore-path) + (delete-test-file! target) + (check-predicate aws pair?)))) + )) ;; ============================================================