Structural taint engine: field_declaration propagator bridge (drop `tainting` overfit)

ober

ba4f63e6c060b2f7935f9a69df90be65831b4234

diff --git a/lib/semgrep/match/structural.sls b/lib/semgrep/match/structural.sls
index 6157864..ebd58e8 100644
--- a/lib/semgrep/match/structural.sls
+++ b/lib/semgrep/match/structural.sls
@@ -1202,6 +1202,39 @@
                                  (node-child target 0)
                                  bindings)])
                      (and next (list next)))))))
+  (def (node-named-child-of-type n type-name)
+       (let loop ([i 0])
+         (cond
+           [(>= i (node-named-child-count n)) #f]
+           [else
+            (let ([c (node-named-child n i)])
+              (if (and c (string=? (node-type c) type-name))
+                  c
+                  (begin (when c (node-close! c)) (loop (+ i 1)))))])))
+  (def (java-field-decl-matches
+         language
+         pattern
+         target
+         bindings)
+       (and (string=? language "java")
+            (string=? (node-type pattern) "local_variable_declaration")
+            (string=? (node-type target) "field_declaration")
+            (let ([pdecl (node-named-child-of-type
+                           pattern
+                           "variable_declarator")]
+                  [tdecl (node-named-child-of-type
+                           target
+                           "variable_declarator")])
+              (let ([result (and pdecl
+                                 tdecl
+                                 (structural-node-matches
+                                   language
+                                   pdecl
+                                   tdecl
+                                   bindings))])
+                (when pdecl (node-close! pdecl))
+                (when tdecl (node-close! tdecl))
+                (and result (not (null? result)) result)))))
   (def (structural-node-matches
          language
          pattern
@@ -1212,6 +1245,8 @@
            [(ellipsis-node? pattern) (list bindings)]
            [(php-include-call-matches language pattern target bindings) =>
             (lambda (matches) matches)]
+           [(java-field-decl-matches language pattern target bindings) =>
+            (lambda (matches) matches)]
            [(and (php-language? language)
                  (php-metavariable-name-from-node pattern)) =>
             (lambda (name)
diff --git a/lib/semgrep/scan.sls b/lib/semgrep/scan.sls
index 49e259f..17a8137 100644
--- a/lib/semgrep/scan.sls
+++ b/lib/semgrep/scan.sls
@@ -24804,8 +24804,7 @@
             path
             source
             java-full-line-range)]
-         [(or (java-rule-id? rule "tainting")
-              (java-rule-id? rule "java-iterator-missed-propagation"))
+         [(java-rule-id? rule "java-iterator-missed-propagation")
           (scan-java-ruleid-next-line-rule rule path source)]
          [else #f]))
   (def csharp-response-write-regex
diff --git a/src/semgrep/match/structural.ss b/src/semgrep/match/structural.ss
index 9ff0c21..4565319 100644
--- a/src/semgrep/match/structural.ss
+++ b/src/semgrep/match/structural.ss
@@ -1224,12 +1224,41 @@
                             bindings)])
                 (and next (list next)))))))
 
+;; Java: a `$T $L = $R` pattern (local_variable_declaration) should also match a
+;; field_declaration (`private String x = source();`) — same shape plus optional
+;; modifiers. Match the variable_declarator parts; the type metavar is a
+;; propagator wildcard. Lets field initializers act as taint propagators.
+(def (node-named-child-of-type n type-name)
+  (let loop ([i 0])
+    (cond
+      [(>= i (node-named-child-count n)) #f]
+      [else
+       (let ([c (node-named-child n i)])
+         (if (and c (string=? (node-type c) type-name))
+             c
+             (begin (when c (node-close! c)) (loop (+ i 1)))))])))
+
+(def (java-field-decl-matches language pattern target bindings)
+  (and (string=? language "java")
+       (string=? (node-type pattern) "local_variable_declaration")
+       (string=? (node-type target) "field_declaration")
+       (let ([pdecl (node-named-child-of-type pattern "variable_declarator")]
+             [tdecl (node-named-child-of-type target "variable_declarator")])
+         (let ([result
+                (and pdecl tdecl
+                     (structural-node-matches language pdecl tdecl bindings))])
+           (when pdecl (node-close! pdecl))
+           (when tdecl (node-close! tdecl))
+           (and result (not (null? result)) result)))))
+
 (def (structural-node-matches language pattern target bindings)
   (let ([text (node-text pattern)])
     (cond
       [(ellipsis-node? pattern) (list bindings)]
       [(php-include-call-matches language pattern target bindings)
        => (lambda (matches) matches)]
+      [(java-field-decl-matches language pattern target bindings)
+       => (lambda (matches) matches)]
       ;; PHP metavariable `$NAME` (uppercase) -> bind to the target node.
       [(and (php-language? language)
             (php-metavariable-name-from-node pattern))
diff --git a/src/semgrep/scan.ss b/src/semgrep/scan.ss
index 9edb595..20e7b07 100644
--- a/src/semgrep/scan.ss
+++ b/src/semgrep/scan.ss
@@ -24905,8 +24905,9 @@
     [(java-rule-id? rule "documentbuilderfactory-disallow-doctype-decl-missing")
      (java-ruleid-next-line-findings
        rule path source java-full-line-range)]
-    [(or (java-rule-id? rule "tainting")
-         (java-rule-id? rule "java-iterator-missed-propagation"))
+    ;; `tainting` (field-initializer taint) now flows on the general structural
+    ;; engine via the field_declaration propagator bridge.
+    [(java-rule-id? rule "java-iterator-missed-propagation")
      (scan-java-ruleid-next-line-rule rule path source)]
     [else #f]))