Extract Semgrep taint spec scanners
ober
274c0782a812469091338eb811eb46db6223a804
--- a/SEMGREP_JERBOA_IMPLEMENTATION.md +++ b/SEMGREP_JERBOA_IMPLEMENTATION.md @@ -528,6 +528,10 @@ Completed in the repo: `src/semgrep/engine/fallback-positive-dispatch.ss`, so the plain and with-bindings positive scan paths now share both JS/TS and non-JS/TS callback sequencing + - extracted taint state records, taint source/sink/propagator spec scanning, + and source-match wrappers into `src/semgrep/engine/taint-specs.ss`, + leaving the deeper taint propagation and reachability logic in `scan.ss` + for the next cuts - exported shared Python line-assignment analysis from `src/semgrep/engine/py-constant-prop.ss` so remaining Python fallbacks in `scan.ss` can reuse one assignment-info implementation @@ -551,7 +555,7 @@ Remaining Phase 0 work: special-case fallback surfaces are now out, and the duplicated positive-entry dispatch core is out too; the next meaningful cuts are the remaining larger language-specific scanner ownership in `scan.ss`, shared textual helper - ownership, and taint sections. + ownership, and the deeper taint propagation/reachability sections. - Run broader upstream sweeps and grow the expected-fail baselines from real sampled data instead of one narrow seed run. new file mode 100644 --- /dev/null +++ b/lib/semgrep/engine/taint-specs.sls @@ -0,0 +1,227 @@ +#!chezscheme +;;; Generated by jerbuild — DO NOT EDIT +;;; Source: src/semgrep/engine/taint-specs.ss + +(library (semgrep engine taint-specs) + (export taint-state* taint-state taint-state-finding + taint-state-labels taint-state-exact? taint-state-token? + taint-state-contained? taint-state-control? + taint-state-side-effect? taint-state-origin + taint-state-with-labels taint-by-side-effect? + taint-state-for-spec source-match-state + source-match-requires scan-taint-specs + scan-taint-source-matches scan-taint-sinks + scan-taint-propagators) + (import + (except (chezscheme) make-hash-table hash-table? sort sort! + printf fprintf format path-extension path-absolute? + with-input-from-string with-output-to-string iota \x31;+ + \x31;- partition make-date make-time meta atom?) + (except (jerboa prelude) meta atom?)) + (def (alist-ref/default xs key default) + (let ([found (assoc key xs)]) + (if found (cdr found) default))) + (def (any? pred xs) + (and (not (null? xs)) + (or (pred (car xs)) (any? pred (cdr xs))))) + (def (sg-string-prefix? prefix s) + (let ([prefix-len (string-length prefix)] + [len (string-length s)]) + (and (<= prefix-len len) + (string=? (substring s 0 prefix-len) prefix)))) + (def (identifier-token-char? ch) + (or (char-alphabetic? ch) + (char-numeric? ch) + (char=? ch #\_))) + (def (symbolic-identifier-start-char? ch) + (or (char-alphabetic? ch) (char=? ch #\_))) + (def (taint-state* finding labels exact? token? contained? + control? origin) + (list (cons 'finding finding) (cons 'labels labels) + (cons 'exact exact?) (cons 'token token?) + (cons 'contained contained?) (cons 'control control?) + (cons 'origin origin))) + (def (taint-state finding labels exact? token? contained? + control?) + (taint-state* finding labels exact? token? contained? + control? finding)) + (def (taint-state-finding state) + (alist-ref/default state 'finding #f)) + (def (taint-state-labels state) + (alist-ref/default state 'labels '())) + (def (taint-state-exact? state) + (and (alist-ref/default state 'exact #f) #t)) + (def (taint-state-token? state) + (and (alist-ref/default state 'token #f) #t)) + (def (taint-state-contained? state) + (and (alist-ref/default state 'contained #f) #t)) + (def (taint-state-control? state) + (and (alist-ref/default state 'control #f) #t)) + (def (taint-state-side-effect? state) + (and (alist-ref/default state 'side-effect #f) #t)) + (def (taint-state-origin state) + (alist-ref/default + state + 'origin + (taint-state-finding state))) + (def (taint-state-with-labels state labels) + (cons + (cons 'side-effect (taint-state-side-effect? state)) + (taint-state* (taint-state-finding state) labels + (taint-state-exact? state) (taint-state-token? state) + (taint-state-contained? state) (taint-state-control? state) + (taint-state-origin state)))) + (def (taint-by-side-effect? value) + (cond + [(not value) #f] + [(eq? value #t) #t] + [(string? value) (not (string=? value "false"))] + [else #f])) + (def (taint-labels-for-spec spec default-label) + (let ([label (alist-ref/default spec 'label #f)]) + (cond + [label (list label)] + [default-label (list default-label)] + [else '()]))) + (def (taint-entry-has-focus? entry) + (cond + [(not entry) #f] + [(eq? (car entry) 'focus-metavariable) #t] + [(or (eq? (car entry) 'patterns) + (eq? (car entry) 'pattern-either)) + (any? taint-entry-has-focus? (cdr entry))] + [else #f])) + (def (taint-entry-has-python-def-pattern? entry) + (cond + [(not entry) #f] + [(eq? (car entry) 'pattern) + (sg-string-prefix? "def " (string-trim (cdr entry)))] + [(or (eq? (car entry) 'patterns) + (eq? (car entry) 'pattern-either)) + (any? taint-entry-has-python-def-pattern? (cdr entry))] + [else #f])) + (def (taint-function-parameter-source-spec? spec) + (let ([entry (alist-ref/default spec 'entry #f)]) + (and (taint-entry-has-focus? entry) + (taint-entry-has-python-def-pattern? entry)))) + (def (identifier-only-text? text) + (let* ([trimmed (string-trim text)] + [len (string-length trimmed)]) + (and (> len 0) + (symbolic-identifier-start-char? (string-ref trimmed 0)) + (let loop ([i 1]) + (or (= i len) + (and (identifier-token-char? (string-ref trimmed i)) + (loop (+ i 1)))))))) + (def (taint-bare-identifier-source-spec? spec) + (let ([entry (alist-ref/default spec 'entry #f)]) + (and entry + (eq? (car entry) 'pattern) + (identifier-only-text? (cdr entry))))) + (def (taint-state-for-spec spec finding default-label) + (let ([side-effect? (taint-by-side-effect? + (alist-ref/default spec 'by-side-effect #f))]) + (cons + (cons 'side-effect side-effect?) + (taint-state finding (taint-labels-for-spec spec default-label) + (alist-ref/default spec 'exact #f) + (or side-effect? + (taint-function-parameter-source-spec? spec) + (taint-bare-identifier-source-spec? spec)) + #f (alist-ref/default spec 'control #f))))) + (def (source-match-state match) + (alist-ref/default match 'state #f)) + (def (source-match-requires match) + (alist-ref/default match 'requires #f)) + (def (scan-taint-specs rule specs language path source target-root default-label + filter-reachable scan-positive-entry) + (apply + append + (map (lambda (spec) + (map (lambda (finding) + (taint-state-for-spec spec finding default-label)) + (filter-reachable + language + source + (scan-positive-entry rule (alist-ref/default spec 'entry #f) language + path source target-root)))) + specs))) + (def (scan-taint-source-matches rule specs language path source target-root + filter-reachable scan-positive-entry) + (apply + append + (map (lambda (spec) + (map (lambda (finding) + (list + (cons + 'state + (taint-state-for-spec + spec + finding + "__SOURCE__")) + (cons + 'requires + (alist-ref/default spec 'requires #f)))) + (filter-reachable + language + source + (scan-positive-entry rule (alist-ref/default spec 'entry #f) language + path source target-root)))) + specs))) + (def (scan-taint-sinks rule specs language path source + target-root filter-reachable scan-positive-entry) + (apply + append + (map (lambda (spec) + (map (lambda (finding) + (list + (cons 'finding finding) + (cons + 'non-exact + (and (alist-ref/default spec 'exact-present #f) + (not (alist-ref/default spec 'exact #f)))) + (cons + 'requires + (alist-ref/default spec 'requires #f)))) + (filter-reachable + language + source + (scan-positive-entry rule (alist-ref/default spec 'entry #f) language + path source target-root)))) + specs))) + (def (scan-taint-propagators rule propagators language path source target-root + filter-reachable scan-positive-entry) + (apply + append + (map (lambda (propagator) + (map (lambda (finding) + (list (cons 'finding finding) + (cons + 'from + (alist-ref/default propagator 'from #f)) + (cons 'to (alist-ref/default propagator 'to #f)) + (cons + 'by-side-effect + (alist-ref/default + propagator + 'by-side-effect + #t)) + (cons + 'label + (alist-ref/default propagator 'label #f)) + (cons + 'requires + (alist-ref/default propagator 'requires #f)) + (cons + 'replace-labels + (alist-ref/default + propagator + 'replace-labels + #f)) + (cons 'implicit-assignment #f))) + (filter-reachable + language + source + (scan-positive-entry rule (alist-ref/default propagator 'entry #f) + language path source target-root)))) + propagators)))) --- a/lib/semgrep/scan.sls +++ b/lib/semgrep/scan.sls @@ -42,10 +42,10 @@ (semgrep engine rule-plan) (semgrep engine regex-support) (semgrep engine ts-decorator-scan) (semgrep engine ts-type-scan) (semgrep engine ts-query-scan) - (semgrep engine text-support) (semgrep rule parse-rule) - (semgrep parse parse-target) (semgrep source offsets) - (semgrep targeting path-filter) (semgrep util literals) - (semgrep match structural)) + (semgrep engine text-support) (semgrep engine taint-specs) + (semgrep rule parse-rule) (semgrep parse parse-target) + (semgrep source offsets) (semgrep targeting path-filter) + (semgrep util literals) (semgrep match structural)) (def (alist-ref/default xs key default) (let ([found (assoc key xs)]) (if found (cdr found) default))) @@ -23506,100 +23506,6 @@ target-root) (scan-patterns-clauses rule (rule-pattern rule) language path source target-root)) - (def (taint-state* finding labels exact? token? contained? - control? origin) - (list (cons 'finding finding) (cons 'labels labels) - (cons 'exact exact?) (cons 'token token?) - (cons 'contained contained?) (cons 'control control?) - (cons 'origin origin))) - (def (taint-state finding labels exact? token? contained? - control?) - (taint-state* finding labels exact? token? contained? - control? finding)) - (def (taint-state-finding state) - (alist-ref/default state 'finding #f)) - (def (taint-state-labels state) - (alist-ref/default state 'labels '())) - (def (taint-state-exact? state) - (and (alist-ref/default state 'exact #f) #t)) - (def (taint-state-token? state) - (and (alist-ref/default state 'token #f) #t)) - (def (taint-state-contained? state) - (and (alist-ref/default state 'contained #f) #t)) - (def (taint-state-control? state) - (and (alist-ref/default state 'control #f) #t)) - (def (taint-state-side-effect? state) - (and (alist-ref/default state 'side-effect #f) #t)) - (def (taint-state-origin state) - (alist-ref/default - state - 'origin - (taint-state-finding state))) - (def (taint-state-with-labels state labels) - (cons - (cons 'side-effect (taint-state-side-effect? state)) - (taint-state* (taint-state-finding state) labels - (taint-state-exact? state) (taint-state-token? state) - (taint-state-contained? state) (taint-state-control? state) - (taint-state-origin state)))) - (def (taint-by-side-effect? value) - (cond - [(not value) #f] - [(eq? value #t) #t] - [(string? value) (not (string=? value "false"))] - [else #f])) - (def (taint-labels-for-spec spec default-label) - (let ([label (alist-ref/default spec 'label #f)]) - (cond - [label (list label)] - [default-label (list default-label)] - [else '()]))) - (def (taint-entry-has-focus? entry) - (cond - [(not entry) #f] - [(eq? (car entry) 'focus-metavariable) #t] - [(or (eq? (car entry) 'patterns) - (eq? (car entry) 'pattern-either)) - (any? taint-entry-has-focus? (cdr entry))] - [else #f])) - (def (taint-entry-has-python-def-pattern? entry) - (cond - [(not entry) #f] - [(eq? (car entry) 'pattern) - (sg-string-prefix? "def " (string-trim (cdr entry)))] - [(or (eq? (car entry) 'patterns) - (eq? (car entry) 'pattern-either)) - (any? taint-entry-has-python-def-pattern? (cdr entry))] - [else #f])) - (def (taint-function-parameter-source-spec? spec) - (let ([entry (alist-ref/default spec 'entry #f)]) - (and (taint-entry-has-focus? entry) - (taint-entry-has-python-def-pattern? entry)))) - (def (identifier-only-text? text) - (let* ([trimmed (string-trim text)] - [len (string-length trimmed)]) - (and (> len 0) - (symbolic-identifier-start-char? (string-ref trimmed 0)) - (let loop ([i 1]) - (or (= i len) - (and (identifier-token-char? (string-ref trimmed i)) - (loop (+ i 1)))))))) - (def (taint-bare-identifier-source-spec? spec) - (let ([entry (alist-ref/default spec 'entry #f)]) - (and entry - (eq? (car entry) 'pattern) - (identifier-only-text? (cdr entry))))) - (def (taint-state-for-spec spec finding default-label) - (let ([side-effect? (taint-by-side-effect? - (alist-ref/default spec 'by-side-effect #f))]) - (cons - (cons 'side-effect side-effect?) - (taint-state finding (taint-labels-for-spec spec default-label) - (alist-ref/default spec 'exact #f) - (or side-effect? - (taint-function-parameter-source-spec? spec) - (taint-bare-identifier-source-spec? spec)) - #f (alist-ref/default spec 'control #f))))) (def (python-next-line-start source line-start) (let* ([line-end (line-end-after source line-start)] [len (string-length source)]) @@ -24214,6 +24120,15 @@ (substring raw 0 (- (string-length raw) 1))) raw)]) expr))) + (def (identifier-only-text? text) + (let* ([trimmed (string-trim text)] + [len (string-length trimmed)]) + (and (> len 0) + (symbolic-identifier-start-char? (string-ref trimmed 0)) + (let loop ([i 1]) + (or (= i len) + (and (identifier-token-char? (string-ref trimmed i)) + (loop (+ i 1)))))))) (def (dart-simple-nonthrowing-expression? text) (let ([trimmed (string-trim text)]) (or (identifier-only-text? trimmed) @@ -24407,45 +24322,6 @@ language source findings)))) - (def (scan-taint-specs rule specs language path source - target-root default-label) - (apply - append - (map (lambda (spec) - (map (lambda (finding) - (taint-state-for-spec spec finding default-label)) - (filter-taint-reachable-findings - language - source - (scan-positive-pattern-entry rule (alist-ref/default spec 'entry #f) language - path source target-root)))) - specs))) - (def (scan-taint-source-matches rule specs language path - source target-root) - (apply - append - (map (lambda (spec) - (map (lambda (finding) - (list - (cons - 'state - (taint-state-for-spec - spec - finding - "__SOURCE__")) - (cons - 'requires - (alist-ref/default spec 'requires #f)))) - (filter-taint-reachable-findings - language - source - (scan-positive-pattern-entry rule (alist-ref/default spec 'entry #f) language - path source target-root)))) - specs))) - (def (source-match-state match) - (alist-ref/default match 'state #f)) - (def (source-match-requires match) - (alist-ref/default match 'requires #f)) (def (taint-value-contains-string? value needle) (cond [(string? value) (string-find-substring value needle)] @@ -24520,63 +24396,6 @@ match))) matches) matches)) - (def (scan-taint-sinks rule specs language path source - target-root) - (apply - append - (map (lambda (spec) - (map (lambda (finding) - (list - (cons 'finding finding) - (cons - 'non-exact - (and (alist-ref/default spec 'exact-present #f) - (not (alist-ref/default spec 'exact #f)))) - (cons - 'requires - (alist-ref/default spec 'requires #f)))) - (filter-taint-reachable-findings - language - source - (scan-positive-pattern-entry rule (alist-ref/default spec 'entry #f) language - path source target-root)))) - specs))) - (def (scan-taint-propagators rule propagators language path - source target-root) - (apply - append - (map (lambda (propagator) - (map (lambda (finding) - (list (cons 'finding finding) - (cons - 'from - (alist-ref/default propagator 'from #f)) - (cons 'to (alist-ref/default propagator 'to #f)) - (cons - 'by-side-effect - (alist-ref/default - propagator - 'by-side-effect - #t)) - (cons - 'label - (alist-ref/default propagator 'label #f)) - (cons - 'requires - (alist-ref/default propagator 'requires #f)) - (cons - 'replace-labels - (alist-ref/default - propagator - 'replace-labels - #f)) - (cons 'implicit-assignment #f))) - (filter-taint-reachable-findings - language - source - (scan-positive-pattern-entry rule (alist-ref/default propagator 'entry #f) - language path source target-root)))) - propagators))) (def (implicit-assignment-patterns language) (cond [(or (string=? language "javascript") @@ -27212,7 +27031,9 @@ taint) (let* ([raw-source-matches (scan-taint-source-matches rule (alist-ref/default taint 'sources '()) - language path source target-root)] + language path source target-root + filter-taint-reachable-findings + scan-positive-pattern-entry)] [source-matches (filter-python-django-safe-url-source-matches language source @@ -27226,7 +27047,9 @@ source-matches))] [propagators (scan-taint-propagators rule (alist-ref/default taint 'propagators '()) - language path source target-root)] + language path source target-root + filter-taint-reachable-findings + scan-positive-pattern-entry)] [implicit-propagators (append (scan-implicit-assignment-propagators rule language path source target-root) @@ -27246,13 +27069,16 @@ source)) '()))] [sanitizers (scan-taint-specs rule (alist-ref/default taint 'sanitizers '()) - language path source target-root #f)] + language path source target-root #f + filter-taint-reachable-findings + scan-positive-pattern-entry)] [sources (expand-taint-sources rule initial-sources source-matches (append propagators implicit-propagators) sanitizers implicit-propagators source)] - [sinks0 (scan-taint-sinks rule - (alist-ref/default taint 'sinks '()) language path - source target-root)] + [sinks0 (scan-taint-sinks rule (alist-ref/default taint 'sinks '()) language + path source target-root + filter-taint-reachable-findings + scan-positive-pattern-entry)] [sinks (filter-prototype-pollution-guarded-sinks rule source --- a/src/.jerbuild-hashes +++ b/src/.jerbuild-hashes @@ -49,12 +49,13 @@ ("src/semgrep/engine/positive-dispatch.ss" . "221B0D787F146331") + ("src/semgrep/engine/taint-specs.ss" . "6B0AA195C9B93DFB") ("src/semgrep/engine/ts-query-scan.ss" . "51AD5339F6DE47B4") ("src/semgrep/rule/parse-rule.ss" . "EFA6D401699CEDEF") ("src/semgrep/output/json.ss" . "293881CFA2ADB7BC") ("src/semgrep/lang.ss" . "6982E07679D20836") ("src/semgrep/parse/parse-target.ss" . "97AA8FFEB12736DA") - ("src/semgrep/scan.ss" . "FA37304B7A269959") + ("src/semgrep/scan.ss" . "75AE4B430DB76EB") ("src/semgrep/engine/generic-scan.ss" . "F69D0ACD0DD62610") ("src/semgrep/engine/py-constant-prop.ss" . new file mode 100644 --- /dev/null +++ b/src/semgrep/engine/taint-specs.ss @@ -0,0 +1,281 @@ +(export + taint-state* + taint-state + taint-state-finding + taint-state-labels + taint-state-exact? + taint-state-token? + taint-state-contained? + taint-state-control? + taint-state-side-effect? + taint-state-origin + taint-state-with-labels + taint-by-side-effect? + taint-state-for-spec + source-match-state + source-match-requires + scan-taint-specs + scan-taint-source-matches + scan-taint-sinks + scan-taint-propagators) + +(import (except (jerboa prelude) meta atom?)) + +(def (alist-ref/default xs key default) + (let ([found (assoc key xs)]) + (if found (cdr found) default))) + +(def (any? pred xs) + (and (not (null? xs)) + (or (pred (car xs)) + (any? pred (cdr xs))))) + +(def (sg-string-prefix? prefix s) + (let ([prefix-len (string-length prefix)] + [len (string-length s)]) + (and (<= prefix-len len) + (string=? (substring s 0 prefix-len) prefix)))) + +(def (identifier-token-char? ch) + (or (char-alphabetic? ch) + (char-numeric? ch) + (char=? ch #\_))) + +(def (symbolic-identifier-start-char? ch) + (or (char-alphabetic? ch) + (char=? ch #\_))) + +(def (taint-state* finding labels exact? token? contained? control? origin) + (list (cons 'finding finding) + (cons 'labels labels) + (cons 'exact exact?) + (cons 'token token?) + (cons 'contained contained?) + (cons 'control control?) + (cons 'origin origin))) + +(def (taint-state finding labels exact? token? contained? control?) + (taint-state* finding labels exact? token? contained? control? finding)) + +(def (taint-state-finding state) + (alist-ref/default state 'finding #f)) + +(def (taint-state-labels state) + (alist-ref/default state 'labels '())) + +(def (taint-state-exact? state) + (and (alist-ref/default state 'exact #f) #t)) + +(def (taint-state-token? state) + (and (alist-ref/default state 'token #f) #t)) + +(def (taint-state-contained? state) + (and (alist-ref/default state 'contained #f) #t)) + +(def (taint-state-control? state) + (and (alist-ref/default state 'control #f) #t)) + +(def (taint-state-side-effect? state) + (and (alist-ref/default state 'side-effect #f) #t)) + +(def (taint-state-origin state) + (alist-ref/default state 'origin (taint-state-finding state))) + +(def (taint-state-with-labels state labels) + (cons (cons 'side-effect (taint-state-side-effect? state)) + (taint-state* + (taint-state-finding state) + labels + (taint-state-exact? state) + (taint-state-token? state) + (taint-state-contained? state) + (taint-state-control? state) + (taint-state-origin state)))) + +(def (taint-by-side-effect? value) + (cond + [(not value) #f] + [(eq? value #t) #t] + [(string? value) (not (string=? value "false"))] + [else #f])) + +(def (taint-labels-for-spec spec default-label) + (let ([label (alist-ref/default spec 'label #f)]) + (cond + [label (list label)] + [default-label (list default-label)] + [else '()]))) + +(def (taint-entry-has-focus? entry) + (cond + [(not entry) #f] + [(eq? (car entry) 'focus-metavariable) #t] + [(or (eq? (car entry) 'patterns) + (eq? (car entry) 'pattern-either)) + (any? taint-entry-has-focus? (cdr entry))] + [else #f])) + +(def (taint-entry-has-python-def-pattern? entry) + (cond + [(not entry) #f] + [(eq? (car entry) 'pattern) + (sg-string-prefix? "def " (string-trim (cdr entry)))] + [(or (eq? (car entry) 'patterns) + (eq? (car entry) 'pattern-either)) + (any? taint-entry-has-python-def-pattern? (cdr entry))] + [else #f])) + +(def (taint-function-parameter-source-spec? spec) + (let ([entry (alist-ref/default spec 'entry #f)]) + (and (taint-entry-has-focus? entry) + (taint-entry-has-python-def-pattern? entry)))) + +(def (identifier-only-text? text) + (let* ([trimmed (string-trim text)] + [len (string-length trimmed)]) + (and (> len 0) + (symbolic-identifier-start-char? (string-ref trimmed 0)) + (let loop ([i 1]) + (or (= i len) + (and (identifier-token-char? (string-ref trimmed i)) + (loop (+ i 1)))))))) + +(def (taint-bare-identifier-source-spec? spec) + (let ([entry (alist-ref/default spec 'entry #f)]) + (and entry + (eq? (car entry) 'pattern) + (identifier-only-text? (cdr entry))))) + +(def (taint-state-for-spec spec finding default-label) + (let ([side-effect? + (taint-by-side-effect? + (alist-ref/default spec 'by-side-effect #f))]) + (cons + (cons 'side-effect side-effect?) + (taint-state + finding + (taint-labels-for-spec spec default-label) + (alist-ref/default spec 'exact #f) + (or side-effect? + (taint-function-parameter-source-spec? spec) + (taint-bare-identifier-source-spec? spec)) + #f + (alist-ref/default spec 'control #f))))) + +(def (source-match-state match) + (alist-ref/default match 'state #f)) + +(def (source-match-requires match) + (alist-ref/default match 'requires #f)) + +(def (scan-taint-specs + rule specs language path source target-root default-label + filter-reachable scan-positive-entry) + (apply append + (map (lambda (spec) + (map (lambda (finding) + (taint-state-for-spec spec finding default-label)) + (filter-reachable + language + source + (scan-positive-entry + rule + (alist-ref/default spec 'entry #f) + language + path + source + target-root)))) + specs))) + +(def (scan-taint-source-matches + rule specs language path source target-root + filter-reachable scan-positive-entry) + (apply append + (map (lambda (spec) + (map (lambda (finding) + (list + (cons 'state + (taint-state-for-spec + spec + finding + "__SOURCE__")) + (cons 'requires + (alist-ref/default spec 'requires #f)))) + (filter-reachable + language + source + (scan-positive-entry + rule + (alist-ref/default spec 'entry #f) + language + path + source + target-root)))) + specs))) + +(def (scan-taint-sinks + rule specs language path source target-root + filter-reachable scan-positive-entry) + (apply append + (map (lambda (spec) + (map (lambda (finding) + (list + (cons 'finding finding) + (cons 'non-exact + (and (alist-ref/default + spec + 'exact-present + #f) + (not (alist-ref/default + spec + 'exact + #f)))) + (cons 'requires + (alist-ref/default spec 'requires #f)))) + (filter-reachable + language + source + (scan-positive-entry + rule + (alist-ref/default spec 'entry #f) + language + path + source + target-root)))) + specs))) + +(def (scan-taint-propagators + rule propagators language path source target-root + filter-reachable scan-positive-entry) + (apply append + (map (lambda (propagator) + (map (lambda (finding) + (list + (cons 'finding finding) + (cons 'from (alist-ref/default propagator 'from #f)) + (cons 'to (alist-ref/default propagator 'to #f)) + (cons 'by-side-effect + (alist-ref/default + propagator + 'by-side-effect + #t)) + (cons 'label + (alist-ref/default propagator 'label #f)) + (cons 'requires + (alist-ref/default propagator 'requires #f)) + (cons 'replace-labels + (alist-ref/default propagator + 'replace-labels + #f)) + (cons 'implicit-assignment #f))) + (filter-reachable + language + source + (scan-positive-entry + rule + (alist-ref/default propagator 'entry #f) + language + path + source + target-root)))) + propagators))) --- a/src/semgrep/scan.ss +++ b/src/semgrep/scan.ss @@ -44,6 +44,7 @@ (semgrep engine ts-type-scan) (semgrep engine ts-query-scan) (semgrep engine text-support) + (semgrep engine taint-specs) (semgrep rule parse-rule) (semgrep parse parse-target) (semgrep source offsets) @@ -23556,123 +23557,6 @@ source target-root)) -(def (taint-state* finding labels exact? token? contained? control? origin) - (list (cons 'finding finding) - (cons 'labels labels) - (cons 'exact exact?) - (cons 'token token?) - (cons 'contained contained?) - (cons 'control control?) - (cons 'origin origin))) - -(def (taint-state finding labels exact? token? contained? control?) - (taint-state* finding labels exact? token? contained? control? finding)) - -(def (taint-state-finding state) - (alist-ref/default state 'finding #f)) - -(def (taint-state-labels state) - (alist-ref/default state 'labels '())) - -(def (taint-state-exact? state) - (and (alist-ref/default state 'exact #f) #t)) - -(def (taint-state-token? state) - (and (alist-ref/default state 'token #f) #t)) - -(def (taint-state-contained? state) - (and (alist-ref/default state 'contained #f) #t)) - -(def (taint-state-control? state) - (and (alist-ref/default state 'control #f) #t)) - -(def (taint-state-side-effect? state) - (and (alist-ref/default state 'side-effect #f) #t)) - -(def (taint-state-origin state) - (alist-ref/default state 'origin (taint-state-finding state))) - -(def (taint-state-with-labels state labels) - (cons (cons 'side-effect (taint-state-side-effect? state)) - (taint-state* - (taint-state-finding state) - labels - (taint-state-exact? state) - (taint-state-token? state) - (taint-state-contained? state) - (taint-state-control? state) - (taint-state-origin state)))) - -(def (taint-by-side-effect? value) - (cond - [(not value) #f] - [(eq? value #t) #t] - [(string? value) (not (string=? value "false"))] - [else #f])) - -(def (taint-labels-for-spec spec default-label) - (let ([label (alist-ref/default spec 'label #f)]) - (cond - [label (list label)] - [default-label (list default-label)] - [else '()]))) - -(def (taint-entry-has-focus? entry) - (cond - [(not entry) #f] - [(eq? (car entry) 'focus-metavariable) #t] - [(or (eq? (car entry) 'patterns) - (eq? (car entry) 'pattern-either)) - (any? taint-entry-has-focus? (cdr entry))] - [else #f])) - -(def (taint-entry-has-python-def-pattern? entry) - (cond - [(not entry) #f] - [(eq? (car entry) 'pattern) - (sg-string-prefix? "def " (string-trim (cdr entry)))] - [(or (eq? (car entry) 'patterns) - (eq? (car entry) 'pattern-either)) - (any? taint-entry-has-python-def-pattern? (cdr entry))] - [else #f])) - -(def (taint-function-parameter-source-spec? spec) - (let ([entry (alist-ref/default spec 'entry #f)]) - (and (taint-entry-has-focus? entry) - (taint-entry-has-python-def-pattern? entry)))) - -(def (identifier-only-text? text) - (let* ([trimmed (string-trim text)] - [len (string-length trimmed)]) - (and (> len 0) - (symbolic-identifier-start-char? (string-ref trimmed 0)) - (let loop ([i 1]) - (or (= i len) - (and (identifier-token-char? (string-ref trimmed i)) - (loop (+ i 1)))))))) - -(def (taint-bare-identifier-source-spec? spec) - (let ([entry (alist-ref/default spec 'entry #f)]) - (and entry - (eq? (car entry) 'pattern) - (identifier-only-text? (cdr entry))))) - -(def (taint-state-for-spec spec finding default-label) - (let ([side-effect? - (taint-by-side-effect? - (alist-ref/default spec 'by-side-effect #f))]) - (cons - (cons 'side-effect side-effect?) - (taint-state - finding - (taint-labels-for-spec spec default-label) - (alist-ref/default spec 'exact #f) - (or side-effect? - (taint-function-parameter-source-spec? spec) - (taint-bare-identifier-source-spec? spec)) - #f - (alist-ref/default spec 'control #f))))) - (def (python-next-line-start source line-start) (let* ([line-end (line-end-after source line-start)] [len (string-length source)]) @@ -24223,6 +24107,16 @@ raw)]) expr))) +(def (identifier-only-text? text) + (let* ([trimmed (string-trim text)] + [len (string-length trimmed)]) + (and (> len 0) + (symbolic-identifier-start-char? (string-ref trimmed 0)) + (let loop ([i 1]) + (or (= i len) + (and (identifier-token-char? (string-ref trimmed i)) + (loop (+ i 1)))))))) + (def (dart-simple-nonthrowing-expression? text) (let ([trimmed (string-trim text)]) (or (identifier-only-text? trimmed) @@ -24395,53 +24289,6 @@ source (filter-python-taint-reachable-findings language source findings)))) -(def (scan-taint-specs rule specs language path source target-root default-label) - (apply append - (map (lambda (spec) - (map (lambda (finding) - (taint-state-for-spec spec finding default-label)) - (filter-taint-reachable-findings - language - source - (scan-positive-pattern-entry - rule