Merge: boolean value-analysis (completes assume_safe_booleans1)

ober

0045080c062358663f283204061750ef0aaeb6f6

diff --git a/lib/semgrep/scan.sls b/lib/semgrep/scan.sls
index 1d35f61..e900db9 100644
--- a/lib/semgrep/scan.sls
+++ b/lib/semgrep/scan.sls
@@ -32166,6 +32166,27 @@
          (and val
               before
               (java-value-numeric? val source-text before 0))))
+  (def (java-boolean-value-expr? text)
+       (let ([t (string-trim text)])
+         (or (text-contains-comparison? t)
+             (string-find-substring t "Boolean.valueOf(")
+             (string-find-substring t "Boolean.parseBoolean(")
+             (string-find-substring t ".equals(")
+             (string-find-substring t ".equalsIgnoreCase(")
+             (string-find-substring t ".contains(")
+             (string-find-substring t ".containsKey(")
+             (string-find-substring t ".isEmpty(")
+             (string-find-substring t ".startsWith(")
+             (string-find-substring t ".endsWith(")
+             (string-find-substring t ".matches(")
+             (string-find-substring t ".isPresent(")
+             (string-find-substring t " instanceof "))))
+  (def (sink-value-boolean? sink-spec source-text)
+       (let* ([sink (sink-finding sink-spec)]
+              [val (and sink
+                        (or (direct-call-argument-text sink source-text)
+                            (finding-text sink source-text)))])
+         (and val (java-boolean-value-expr? val))))
   (def (taint-assume-safe-indexes? rule)
        (rule-option-enabled? rule "taint_assume_safe_indexes"))
   (def (taint-assume-safe-functions? rule)
@@ -33256,16 +33277,22 @@
        (let* ([requires (alist-ref/default sink-spec 'requires #f)]
               [reaching0 (source-states-reaching-sink rule sink-spec sources sanitizers
                            assignment-kills source-text)]
-              [reaching (if (taint-assume-safe-numbers? rule)
-                            (if (sink-value-numeric? sink-spec source-text)
-                                '()
-                                (sg-filter
-                                  (lambda (ss)
-                                    (not (source-state-numeric-typed-param?
-                                           ss
-                                           source-text)))
-                                  reaching0))
-                            reaching0)]
+              [reaching (cond
+                          [(taint-assume-safe-numbers? rule)
+                           (if (sink-value-numeric? sink-spec source-text)
+                               '()
+                               (sg-filter
+                                 (lambda (ss)
+                                   (not (source-state-numeric-typed-param?
+                                          ss
+                                          source-text)))
+                                 reaching0))]
+                          [(and (taint-assume-safe-booleans? rule)
+                                (sink-value-boolean?
+                                  sink-spec
+                                  source-text))
+                           '()]
+                          [else reaching0])]
               [labels (label-set-for-source-states reaching)])
          (if (and (not (null? labels))
                   (requires-satisfied? labels requires))
diff --git a/src/semgrep/scan.ss b/src/semgrep/scan.ss
index afa26b8..f1884da 100644
--- a/src/semgrep/scan.ss
+++ b/src/semgrep/scan.ss
@@ -32086,6 +32086,35 @@
     (and val before
          (java-value-numeric? val source-text before 0))))
 
+;; --- assume_safe_booleans: a value of boolean type carries no taint. A
+;; comparison is already handled by the assume_safe_comparisons gate (which
+;; taint-assume-safe-booleans? subsumes); this adds boolean-returning calls
+;; (`Boolean.valueOf`/`parseBoolean`, `.equals`/`.contains`/`.isEmpty`/...),
+;; `instanceof`, so `sink(Boolean.valueOf(x))` is safe while `sink("a" + x)`
+;; (a String concat) still flags.
+(def (java-boolean-value-expr? text)
+  (let ([t (string-trim text)])
+    (or (text-contains-comparison? t)
+        (string-find-substring t "Boolean.valueOf(")
+        (string-find-substring t "Boolean.parseBoolean(")
+        (string-find-substring t ".equals(")
+        (string-find-substring t ".equalsIgnoreCase(")
+        (string-find-substring t ".contains(")
+        (string-find-substring t ".containsKey(")
+        (string-find-substring t ".isEmpty(")
+        (string-find-substring t ".startsWith(")
+        (string-find-substring t ".endsWith(")
+        (string-find-substring t ".matches(")
+        (string-find-substring t ".isPresent(")
+        (string-find-substring t " instanceof "))))
+
+(def (sink-value-boolean? sink-spec source-text)
+  (let* ([sink (sink-finding sink-spec)]
+         [val (and sink
+                   (or (direct-call-argument-text sink source-text)
+                       (finding-text sink source-text)))])
+    (and val (java-boolean-value-expr? val))))
+
 (def (taint-assume-safe-indexes? rule)
   (rule-option-enabled? rule "taint_assume_safe_indexes"))
 
@@ -33150,14 +33179,18 @@
             assignment-kills
             source-text)]
          [reaching
-          (if (taint-assume-safe-numbers? rule)
-              (if (sink-value-numeric? sink-spec source-text)
-                  '()
-                  (sg-filter
-                    (lambda (ss)
-                      (not (source-state-numeric-typed-param? ss source-text)))
-                    reaching0))
-              reaching0)]
+          (cond
+            [(taint-assume-safe-numbers? rule)
+             (if (sink-value-numeric? sink-spec source-text)
+                 '()
+                 (sg-filter
+                   (lambda (ss)
+                     (not (source-state-numeric-typed-param? ss source-text)))
+                   reaching0))]
+            [(and (taint-assume-safe-booleans? rule)
+                  (sink-value-boolean? sink-spec source-text))
+             '()]
+            [else reaching0])]
          [labels (label-set-for-source-states reaching)])
     (if (and (not (null? labels))
              (requires-satisfied? labels requires))