attackmap 0.6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. attackmap/__init__.py +2 -0
  2. attackmap/analyzer.py +449 -0
  3. attackmap/analyzer_contracts.py +194 -0
  4. attackmap/analyzers.py +1003 -0
  5. attackmap/anomalies.py +612 -0
  6. attackmap/asset_model.py +245 -0
  7. attackmap/attack_taxonomy.py +133 -0
  8. attackmap/auth_protocol.py +702 -0
  9. attackmap/authz.py +1695 -0
  10. attackmap/bench.py +467 -0
  11. attackmap/cli.py +1474 -0
  12. attackmap/config_scanner.py +389 -0
  13. attackmap/context_pack.py +82 -0
  14. attackmap/contracts.py +201 -0
  15. attackmap/control_model.py +280 -0
  16. attackmap/crossrepo.py +356 -0
  17. attackmap/crypto.py +240 -0
  18. attackmap/cve.py +717 -0
  19. attackmap/defensive_review.py +647 -0
  20. attackmap/detection_opportunities.py +1221 -0
  21. attackmap/detections/__init__.py +491 -0
  22. attackmap/detections/bola.yml +57 -0
  23. attackmap/detections/cloud.yml +609 -0
  24. attackmap/detections/injection.yml +167 -0
  25. attackmap/detections/secrets.yml +48 -0
  26. attackmap/detections/sigma.py +511 -0
  27. attackmap/detections/ssrf.yml +98 -0
  28. attackmap/detections/weakness.yml +62 -0
  29. attackmap/detections/webhook.yml +45 -0
  30. attackmap/detections/workflow.yml +121 -0
  31. attackmap/diagrams.py +237 -0
  32. attackmap/diff.py +301 -0
  33. attackmap/exploitability.py +493 -0
  34. attackmap/fingerprint.py +91 -0
  35. attackmap/fleet.py +370 -0
  36. attackmap/gitref.py +129 -0
  37. attackmap/graph.py +26 -0
  38. attackmap/handler_auth.py +639 -0
  39. attackmap/handler_span.py +62 -0
  40. attackmap/hunt_harness.py +505 -0
  41. attackmap/insights.py +701 -0
  42. attackmap/llm_review.py +846 -0
  43. attackmap/lockfiles.py +521 -0
  44. attackmap/mass_assignment.py +543 -0
  45. attackmap/md.py +175 -0
  46. attackmap/merge.py +333 -0
  47. attackmap/models.py +1284 -0
  48. attackmap/plugins_lock.py +114 -0
  49. attackmap/popular_packages.py +193 -0
  50. attackmap/progress.py +258 -0
  51. attackmap/recon_models.py +31 -0
  52. attackmap/recon_to_analysis.py +149 -0
  53. attackmap/redact.py +220 -0
  54. attackmap/report.py +424 -0
  55. attackmap/review_eval.py +335 -0
  56. attackmap/review_json.py +308 -0
  57. attackmap/review_prompts.py +796 -0
  58. attackmap/route_auth_fusion.py +474 -0
  59. attackmap/safe_fs.py +285 -0
  60. attackmap/sarif.py +373 -0
  61. attackmap/sbom.py +418 -0
  62. attackmap/scanner.py +1379 -0
  63. attackmap/sdk/__init__.py +278 -0
  64. attackmap/sdk/contracts.py +17 -0
  65. attackmap/sdk/fs.py +156 -0
  66. attackmap/sdk/models.py +59 -0
  67. attackmap/secrets_history.py +161 -0
  68. attackmap/security_overlay.py +71 -0
  69. attackmap/srcpaths.py +301 -0
  70. attackmap/suggest.py +394 -0
  71. attackmap/supply_chain.py +688 -0
  72. attackmap/suppress.py +740 -0
  73. attackmap/taint.py +1540 -0
  74. attackmap/taint_flow.py +1419 -0
  75. attackmap/taint_sinks.py +431 -0
  76. attackmap/taint_sinks.yaml +308 -0
  77. attackmap/taint_sources.py +396 -0
  78. attackmap/taxonomy.py +465 -0
  79. attackmap/threat_model.py +3441 -0
  80. attackmap/topology.py +316 -0
  81. attackmap/triage.py +151 -0
  82. attackmap/weaknesses.py +365 -0
  83. attackmap/webhardening.py +479 -0
  84. attackmap/webhook_verification.py +1067 -0
  85. attackmap/workflow_scanner.py +964 -0
  86. attackmap-0.6.4.dist-info/METADATA +226 -0
  87. attackmap-0.6.4.dist-info/RECORD +91 -0
  88. attackmap-0.6.4.dist-info/WHEEL +5 -0
  89. attackmap-0.6.4.dist-info/entry_points.txt +2 -0
  90. attackmap-0.6.4.dist-info/licenses/LICENSE +21 -0
  91. attackmap-0.6.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,308 @@
1
+ # Declarative taint sink registry (#240).
2
+ #
3
+ # Loaded once at import by attackmap.taint_sinks and validated there (an
4
+ # invalid entry fails the import for this file; a plugin's invalid entry is
5
+ # skipped with a warning). Analyzer plugins add entries through the
6
+ # `attackmap.taint_sinks` entry-point group (see attackmap.sdk).
7
+ #
8
+ # Entry fields:
9
+ # id unique slug (plugins: prefix with the plugin name)
10
+ # kind a sink kind from `kinds:` below
11
+ # langs languages: python, js, go, php, java, csharp, ruby
12
+ # regex the sink signature (Python `re`; must scale linearly — every
13
+ # pattern is run through tests/test_regex_budget.py)
14
+ # cwe primary CWE id (int) — REQUIRED for every sink
15
+ # gate any — dangerous regardless of the argument; fires on reach
16
+ # tainted — fires only when request input reaches `args`: a
17
+ # request-container token in the call (`token_gate`,
18
+ # default true) or a traced intra-procedural flow (#239)
19
+ # args dangerous argument(s): list of positional indexes (default [0]),
20
+ # "all", "receiver" (`url.openStream()`), or "rest" (the rest of
21
+ # the statement, for keyword/assignment sinks)
22
+ # when only fire when the call's arguments match this regex
23
+ # unless never fire when the call's arguments match this regex
24
+ # receiver_unless
25
+ # never fire when the receiver of the matched `.method(` call
26
+ # matches this regex: the expression before the `.` with call
27
+ # arguments elided and whitespace dropped (`a.b()` for
28
+ # `a.b(x)\n .m(`), or `name=<rhs>` for a bare name and its
29
+ # nearest declarator (`taint_sinks.call_receiver`). Anchor with
30
+ # `$` to test the receiver's own last call.
31
+ # requires only consider files whose content matches this regex
32
+ # flags optional: [ignorecase, dotall]
33
+ # base_score optional exploitability override for this entry
34
+ # note free text
35
+ #
36
+ # Single-quoted YAML scalars keep backslashes literal: write regexes as-is.
37
+
38
+ kinds:
39
+ sql_execute: {label: "database (SQL execute)", cwe: 89, base_score: 35}
40
+ subprocess_shell: {label: "command execution", cwe: 78, base_score: 35}
41
+ eval: {label: "code eval", cwe: 95, base_score: 35}
42
+ exec: {label: "code exec", cwe: 94, base_score: 35}
43
+ dynamic_open: {label: "filesystem open", cwe: 22, base_score: 22, alias_of: path_traversal}
44
+ path_traversal: {label: "filesystem path", cwe: 22, base_score: 22}
45
+ zip_slip: {label: "archive extraction", cwe: 22, base_score: 30}
46
+ unsafe_deserialization: {label: "unsafe deserialization", cwe: 502, base_score: 35}
47
+ ssti: {label: "template injection", cwe: 1336, base_score: 30}
48
+ ssrf: {label: "outbound request (SSRF)", cwe: 918, base_score: 20}
49
+ nosql_injection: {label: "NoSQL query", cwe: 943, base_score: 25}
50
+ open_redirect: {label: "redirect target", cwe: 601, base_score: 15}
51
+ code_injection: {label: "dynamic code / dispatch", cwe: 94, base_score: 35}
52
+ expression_injection: {label: "expression language (SpEL/OGNL/MVEL/script)", cwe: 917, base_score: 35}
53
+ jndi_injection: {label: "JNDI lookup", cwe: 74, base_score: 35}
54
+ ldap_injection: {label: "LDAP filter", cwe: 90, base_score: 25}
55
+ xpath_injection: {label: "XPath expression", cwe: 643, base_score: 22}
56
+ header_injection: {label: "HTTP response header", cwe: 113, base_score: 12}
57
+ regex_injection: {label: "regular expression (ReDoS)", cwe: 1333, base_score: 12}
58
+
59
+ sinks:
60
+ # --- SQL ------------------------------------------------------------------
61
+ - {id: sql-cursor-execute, kind: sql_execute, langs: [python, js, go, php], cwe: 89,
62
+ regex: '\b(?:cursor|conn|connection|db|session|engine|client|pool)\.(?:execute|query|exec_driver_sql)\s*\('}
63
+ - {id: go-sql-query, kind: sql_execute, langs: [python, js, go, php], cwe: 89,
64
+ note: "Go database/sql (#102); title-case methods + db-ish receiver avoid url.Query()",
65
+ regex: '\b(?:db|conn|tx|stmt|dbx|sqlx|pool|database|store|dao)\.(?:Query|QueryRow|Exec)\s*\('}
66
+ - {id: go-sql-query-context, kind: sql_execute, langs: [python, js, go, php], cwe: 89, args: [1],
67
+ regex: '\b(?:db|conn|tx|stmt|dbx|sqlx|pool|database|store|dao)\.(?:QueryContext|QueryRowContext|ExecContext)\s*\('}
68
+ - {id: php-mysqli-query, kind: sql_execute, langs: [python, js, go, php], cwe: 89, args: [1], flags: [ignorecase],
69
+ regex: '\b(?:mysqli_query|pg_query)\s*\('}
70
+ - {id: php-pdo-query, kind: sql_execute, langs: [python, js, go, php], cwe: 89, flags: [ignorecase],
71
+ note: "PDO/db ->query|exec (not ->prepare) and Laravel raw DB:: calls (#103)",
72
+ regex: '\$\w+->(?:query|exec)\s*\(|\bDB::(?:select|statement|insert|update|delete|raw|unprepared)\s*\('}
73
+ - {id: java-jdbc, kind: sql_execute, langs: [java], cwe: 89,
74
+ regex: '\b(?:stmt|statement|st|conn|connection|jdbcTemplate|jdbc|em|entityManager|session)\s*\.\s*(?:executeQuery|executeUpdate|execute|prepareStatement|createQuery|createNativeQuery|query|queryForObject|queryForList|update)\s*\('}
75
+
76
+ # --- OS command execution ------------------------------------------------
77
+ - {id: py-subprocess-shell, kind: subprocess_shell, langs: [python, js, go, php], cwe: 78, flags: [dotall],
78
+ regex: 'subprocess\.(?:run|call|Popen|check_output|check_call)\s*\([^)]*shell\s*=\s*True'}
79
+ - {id: py-os-system, kind: subprocess_shell, langs: [python], cwe: 78,
80
+ regex: '\bos\.(?:system|popen)\s*\('}
81
+ - {id: node-child-process-exec, kind: subprocess_shell, langs: [python, js, go, php], cwe: 78,
82
+ regex: 'child_process\s*\.\s*exec(?:Sync)?\s*\('}
83
+ - {id: node-exec-imported, kind: subprocess_shell, langs: [js], cwe: 78,
84
+ note: "destructured / promisified exec(Sync) in a file that loads child_process",
85
+ requires: '[''"](?:node:)?child_process[''"]',
86
+ regex: '(?<![\w.$])(?:exec|execSync|execAsync)\s*\('}
87
+ - {id: node-spawn-shell, kind: subprocess_shell, langs: [js], cwe: 78, args: all,
88
+ requires: '[''"](?:node:)?child_process[''"]',
89
+ when: '\bshell\s*:\s*true',
90
+ regex: '(?<![\w$])(?:child_process\s*\.\s*)?spawn(?:Sync)?\s*\('}
91
+ - {id: go-exec-command, kind: subprocess_shell, langs: [python, js, go, php], cwe: 78, args: all,
92
+ regex: '\bexec\.Command(?:Context)?\s*\('}
93
+ - {id: php-shell, kind: subprocess_shell, langs: [python, js, go, php], cwe: 78,
94
+ regex: '\b(?:system|shell_exec|passthru|proc_open|popen)\s*\('}
95
+ - {id: php-exec, kind: subprocess_shell, langs: [php], cwe: 78,
96
+ note: "PHP exec() runs a shell command (previously reported as code exec)",
97
+ regex: '(?<![\w>$:])exec\s*\('}
98
+ - {id: java-runtime-exec, kind: subprocess_shell, langs: [java], cwe: 78, args: all,
99
+ regex: '\bgetRuntime\s*\(\s*\)\s*\.\s*exec\s*\(|\bnew\s+ProcessBuilder\s*\('}
100
+ - {id: dotnet-process-start, kind: subprocess_shell, langs: [csharp], cwe: 78, args: all,
101
+ regex: '\bProcess\s*\.\s*Start\s*\(|\bnew\s+ProcessStartInfo\s*\('}
102
+ - {id: ruby-system, kind: subprocess_shell, langs: [ruby], cwe: 78, args: rest,
103
+ note: "the argv form system('cmd', arg, …) bypasses the shell — not flagged",
104
+ unless: '^\s*\(?\s*[''"][^''"#]*[''"]\s*,',
105
+ regex: '(?<![\w.:])(?:system|exec|spawn)\b(?=\s*[(''"])'}
106
+ - {id: ruby-backticks, kind: subprocess_shell, langs: [ruby], cwe: 78, args: rest,
107
+ note: "backtick / %x command strings with interpolation",
108
+ regex: '`[^`\n]*#\{|%x[\[({][^\n]*#\{'}
109
+ - {id: ruby-open3, kind: subprocess_shell, langs: [ruby], cwe: 78, args: all,
110
+ regex: '\bOpen3\s*\.\s*(?:popen3|popen2|popen2e|capture2|capture2e|capture3|pipeline\w*)\b'}
111
+ - {id: ruby-kernel-open, kind: subprocess_shell, langs: [ruby], cwe: 78, gate: tainted,
112
+ note: "Kernel#open with a caller-controlled name spawns a process for '|cmd'",
113
+ regex: '(?<![\w.:])(?:Kernel\s*\.\s*)?open\s*\('}
114
+
115
+ # --- code evaluation / dynamic dispatch ----------------------------------
116
+ - {id: eval, kind: eval, langs: [python, js, go, php, ruby], cwe: 95,
117
+ regex: '(?<![\w.])eval\s*\('}
118
+ - {id: py-exec, kind: exec, langs: [python], cwe: 94,
119
+ regex: '(?<![\w.])exec\s*\('}
120
+ - {id: py-import-module, kind: code_injection, langs: [python], cwe: 94, gate: tainted,
121
+ regex: '\bimportlib\.import_module\s*\(|(?<![\w.])__import__\s*\('}
122
+ - {id: py-getattr-dispatch, kind: code_injection, langs: [python], cwe: 94, gate: tainted, args: [1],
123
+ regex: '(?<![\w.])getattr\s*\('}
124
+ - {id: js-new-function, kind: code_injection, langs: [js], cwe: 94, gate: tainted, args: all,
125
+ regex: '\bnew\s+Function\s*\('}
126
+ - {id: js-vm-run, kind: code_injection, langs: [js], cwe: 94, gate: tainted,
127
+ regex: '\bvm\s*\.\s*(?:runInNewContext|runInThisContext|runInContext|compileFunction)\s*\(|\bnew\s+vm\s*\.\s*Script\s*\('}
128
+ - {id: js-timer-string, kind: code_injection, langs: [js], cwe: 94, gate: tainted,
129
+ note: "setTimeout/setInterval with a string body; function callbacks are ignored",
130
+ unless: '^\s*(?:async\b|function\b|\(|[A-Za-z_$][\w$]*\s*=>)',
131
+ regex: '(?<![\w.])set(?:Timeout|Interval)\s*\('}
132
+ - {id: ruby-send, kind: code_injection, langs: [ruby], cwe: 94, gate: tainted,
133
+ regex: '\.\s*(?:send|public_send|__send__|instance_eval|class_eval|instance_variable_get)\s*\('}
134
+ - {id: ruby-constantize, kind: code_injection, langs: [ruby], cwe: 94,
135
+ regex: '\bparams\s*\[[^\]\n]{1,80}\]\s*\.\s*(?:safe_)?constantize\b'}
136
+
137
+ # --- expression language / JNDI (Java) -----------------------------------
138
+ - {id: java-spel, kind: expression_injection, langs: [java], cwe: 917, gate: tainted,
139
+ regex: '\.\s*parseExpression\s*\('}
140
+ - {id: java-ognl, kind: expression_injection, langs: [java], cwe: 917, gate: tainted,
141
+ regex: '\bOgnl(?:Util)?\s*\.\s*(?:getValue|setValue|parseExpression|compileExpression)\s*\('}
142
+ - {id: java-mvel, kind: expression_injection, langs: [java], cwe: 917, gate: tainted,
143
+ regex: '\bMVEL\s*\.\s*(?:eval|evalToString|compileExpression|executeExpression)\s*\('}
144
+ - {id: java-script-engine, kind: expression_injection, langs: [java], cwe: 917, gate: tainted,
145
+ regex: '\b(?:engine|scriptEngine|jsEngine|nashorn|groovyEngine)\s*\.\s*eval\s*\('}
146
+ - {id: java-jndi-lookup, kind: jndi_injection, langs: [java], cwe: 74, gate: tainted,
147
+ regex: '\b(?:ctx|context|initialContext|initCtx|ic|jndiContext|namingContext)\s*\.\s*lookup\s*\(|\bnew\s+InitialContext\s*\(\s*\)\s*\.\s*lookup\s*\('}
148
+
149
+ # --- LDAP / XPath ----------------------------------------------------------
150
+ - {id: py-ldap-search, kind: ldap_injection, langs: [python], cwe: 90, gate: tainted, args: [2],
151
+ note: "python-ldap search_s(base, scope, filterstr)",
152
+ regex: '\b\w+\s*\.\s*search(?:_s|_st|_ext|_ext_s)\s*\('}
153
+ - {id: py-ldap3-search, kind: ldap_injection, langs: [python], cwe: 90, gate: tainted, args: [1],
154
+ note: "ldap3 Connection.search(search_base, search_filter)",
155
+ regex: '\b(?:conn|connection|ldap_conn|ldap)\s*\.\s*search\s*\('}
156
+ - {id: java-ldap-search, kind: ldap_injection, langs: [java], cwe: 90, gate: tainted, args: [1],
157
+ regex: '\b(?:ctx|context|dirContext|ldapContext|ldapTemplate|ldap)\s*\.\s*search\s*\('}
158
+ - {id: php-ldap-search, kind: ldap_injection, langs: [php], cwe: 90, gate: tainted, args: [2],
159
+ regex: '\bldap_(?:search|list|read)\s*\('}
160
+ - {id: py-lxml-xpath, kind: xpath_injection, langs: [python], cwe: 643, gate: tainted,
161
+ regex: '\.\s*xpath\s*\('}
162
+ - {id: java-xpath, kind: xpath_injection, langs: [java], cwe: 643, gate: tainted,
163
+ regex: '\b(?:xpath|xPath|xp|xpathObj)\s*\.\s*(?:evaluate|compile)\s*\('}
164
+ - {id: js-xpath, kind: xpath_injection, langs: [js], cwe: 643, gate: tainted,
165
+ regex: '\bxpath\s*\.\s*(?:select1?|evaluate|parse)\s*\('}
166
+
167
+ # --- path traversal / file inclusion -------------------------------------
168
+ - {id: py-open, kind: dynamic_open, langs: [python, js, go, php], cwe: 22, gate: tainted,
169
+ regex: '(?<![\w.])open\s*\('}
170
+ - {id: node-fs-read, kind: dynamic_open, langs: [js], cwe: 22, gate: tainted,
171
+ regex: '\bfs\s*\.\s*(?:promises\s*\.\s*)?(?:readFile|readFileSync|createReadStream|writeFile|writeFileSync)\s*\('}
172
+ - {id: go-os-open, kind: dynamic_open, langs: [go], cwe: 22, gate: tainted,
173
+ regex: '\b(?:os\.(?:Open|OpenFile|ReadFile|Create|WriteFile)|ioutil\.ReadFile)\s*\('}
174
+ - {id: php-file-read, kind: dynamic_open, langs: [php], cwe: 22, gate: tainted,
175
+ regex: '\b(?:fopen|file_get_contents|readfile|file_put_contents)\s*\('}
176
+ - {id: java-file, kind: dynamic_open, langs: [java], cwe: 22, gate: tainted, args: all,
177
+ regex: '\bnew\s+(?:File|FileInputStream|FileReader|FileOutputStream|FileWriter|RandomAccessFile)\s*\(|\b(?:Paths\s*\.\s*get|Path\s*\.\s*of)\s*\(|\bFiles\s*\.\s*(?:readAllBytes|readAllLines|readString|newInputStream|newBufferedReader|lines)\s*\('}
178
+ - {id: flask-send-file, kind: path_traversal, langs: [python], cwe: 22, gate: tainted,
179
+ note: "send_from_directory is deliberately absent: it joins with werkzeug safe_join",
180
+ regex: '(?<![\w.])(?:flask\.)?send_file\s*\('}
181
+ - {id: express-send-file, kind: path_traversal, langs: [js], cwe: 22, gate: tainted,
182
+ note: "a `root:` option makes `send` reject ../ — not flagged",
183
+ unless: '\broot\s*:',
184
+ regex: '\bres\s*\.\s*(?:sendFile|download)\s*\('}
185
+ - {id: go-serve-file, kind: path_traversal, langs: [go], cwe: 22, gate: tainted, args: [2],
186
+ regex: '\bhttp\.ServeFile\s*\('}
187
+ - {id: php-include, kind: path_traversal, langs: [php], cwe: 98, gate: tainted, args: rest,
188
+ note: "include/require of a caller-chosen path (LFI/RFI)",
189
+ regex: '\b(?:include|require)(?:_once)?\b'}
190
+
191
+ # --- archive extraction (zip-slip) ----------------------------------------
192
+ - {id: py-tar-extractall, kind: zip_slip, langs: [python], cwe: 22, args: all,
193
+ note: "tarfile without filter= (zipfile.extractall already strips ../)",
194
+ requires: '\btarfile\b',
195
+ unless: '\bfilter\s*=',
196
+ regex: '\b(?:tar|tf|tarball|tar_file|tarf|archive|tar_obj)\s*\.\s*extractall\s*\(|\btarfile\.open\s*\([^)\n]*\)\s*\.\s*extractall\s*\('}
197
+ - {id: node-adm-zip, kind: zip_slip, langs: [js], cwe: 22, args: all,
198
+ regex: '\.\s*extractAllTo(?:Async)?\s*\(|\bunzipper\s*\.\s*Extract\s*\('}
199
+
200
+ # --- unsafe deserialization ------------------------------------------------
201
+ - {id: unserialize, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
202
+ regex: '\bunserialize\s*\('}
203
+ - {id: py-pickle, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
204
+ regex: '\b(?:cPickle|pickle)\.loads?\s*\('}
205
+ - {id: py-yaml-load, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
206
+ note: "yaml.load without a Safe loader in the same call",
207
+ regex: '\byaml\.load\s*\((?![^)]*(?:Safe|Loader\s*=))'}
208
+ - {id: py-yaml-unsafe-load, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
209
+ regex: '\byaml\.unsafe_load\s*\('}
210
+ - {id: py-marshal, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
211
+ regex: '\bmarshal\.loads?\s*\('}
212
+ - {id: marshal-ois-unserialize, kind: unsafe_deserialization, langs: [python, js, go, php], cwe: 502,
213
+ regex: '\bMarshal\.load\s*\(|\bObjectInputStream\b|\bunserialize\s*\('}
214
+ - {id: py-jsonpickle-dill, kind: unsafe_deserialization, langs: [python], cwe: 502,
215
+ regex: '\bjsonpickle\.decode\s*\(|\bdill\.loads?\s*\('}
216
+ - {id: py-shelve-open, kind: unsafe_deserialization, langs: [python], cwe: 502, gate: tainted,
217
+ regex: '\bshelve\.open\s*\('}
218
+ - {id: ruby-marshal, kind: unsafe_deserialization, langs: [ruby], cwe: 502,
219
+ regex: '\bMarshal\.load\s*\('}
220
+ - {id: dotnet-binaryformatter, kind: unsafe_deserialization, langs: [csharp], cwe: 502,
221
+ regex: '\bnew\s+(?:BinaryFormatter|LosFormatter|NetDataContractSerializer|SoapFormatter|ObjectStateFormatter)\s*\('}
222
+ - {id: dotnet-typenamehandling, kind: unsafe_deserialization, langs: [csharp], cwe: 502,
223
+ regex: '\bTypeNameHandling\s*=\s*TypeNameHandling\s*\.\s*(?:All|Auto|Objects|Arrays)\b'}
224
+ - {id: java-ois, kind: unsafe_deserialization, langs: [java], cwe: 502,
225
+ regex: '\bnew\s+ObjectInputStream\s*\('}
226
+ - {id: java-jackson-default-typing, kind: unsafe_deserialization, langs: [java], cwe: 502,
227
+ regex: '\.\s*(?:enableDefaultTyping|activateDefaultTyping)\s*\(|\bJsonTypeInfo\s*\.\s*Id\s*\.\s*CLASS\b'}
228
+ - {id: java-snakeyaml, kind: unsafe_deserialization, langs: [java], cwe: 502, args: all,
229
+ unless: 'SafeConstructor',
230
+ regex: '\bnew\s+Yaml\s*\('}
231
+ - {id: java-xstream, kind: unsafe_deserialization, langs: [java], cwe: 502,
232
+ regex: '\b(?:xstream|xStream|XSTREAM)\s*\.\s*fromXML\s*\('}
233
+
234
+ # --- server-side template injection ---------------------------------------
235
+ - {id: flask-render-template-string, kind: ssti, langs: [python, js, go, php], cwe: 1336, gate: tainted,
236
+ regex: '\brender_template_string\s*\('}
237
+ - {id: jinja-template, kind: ssti, langs: [python, js, go, php], cwe: 1336, gate: tainted,
238
+ regex: '\bTemplate\s*\('}
239
+
240
+ # --- SSRF -------------------------------------------------------------------
241
+ - {id: py-requests, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted,
242
+ regex: '\brequests\.(?:get|post|put|delete|patch|head)\s*\('}
243
+ - {id: py-requests-request, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted, args: [1],
244
+ regex: '\brequests\.request\s*\('}
245
+ - {id: py-httpx, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted,
246
+ regex: '\bhttpx\.(?:get|post|put|delete|patch|head|Client)\s*\('}
247
+ - {id: py-httpx-request, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted, args: [1],
248
+ regex: '\bhttpx\.request\s*\('}
249
+ - {id: py-urlopen, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted,
250
+ regex: '\b(?:urlopen|urlretrieve)\s*\('}
251
+ - {id: js-http-client, kind: ssrf, langs: [python, js, go, php], cwe: 918, gate: tainted,
252
+ regex: '\b(?:axios\s*\.\s*(?:get|post|put|delete|patch|head|request)|fetch|http\.(?:get|request))\s*\('}
253
+ - {id: go-http-get, kind: ssrf, langs: [go], cwe: 918, gate: tainted,
254
+ regex: '\bhttp\.(?:Get|Head|Post|PostForm)\s*\('}
255
+ - {id: go-http-new-request, kind: ssrf, langs: [go], cwe: 918, gate: tainted, args: [1],
256
+ regex: '\bhttp\.NewRequest\s*\('}
257
+ - {id: go-http-new-request-ctx, kind: ssrf, langs: [go], cwe: 918, gate: tainted, args: [2],
258
+ regex: '\bhttp\.NewRequestWithContext\s*\('}
259
+ - {id: php-curl-init, kind: ssrf, langs: [php], cwe: 918, gate: tainted,
260
+ regex: '\bcurl_init\s*\('}
261
+ - {id: php-curl-url, kind: ssrf, langs: [php], cwe: 918, gate: tainted, args: [2],
262
+ regex: '\bcurl_setopt\s*\(\s*\$\w+\s*,\s*CURLOPT_URL\b'}
263
+ - {id: java-http-client, kind: ssrf, langs: [java], cwe: 918, gate: tainted,
264
+ regex: '\bHttpRequest\s*\.\s*newBuilder\s*\(|\brestTemplate\s*\.\s*(?:getForObject|getForEntity|postForObject|postForEntity|exchange)\s*\('}
265
+ - {id: java-url-open, kind: ssrf, langs: [java], cwe: 918, gate: tainted, args: receiver,
266
+ regex: '\.\s*(?:openConnection|openStream)\s*\(\s*\)'}
267
+
268
+ # --- NoSQL ----------------------------------------------------------------
269
+ - {id: mongo-filter-request, kind: nosql_injection, langs: [python, js, go, php], cwe: 943,
270
+ note: "a raw request object as the filter; a bare .find( is overwhelmingly array iteration",
271
+ regex: '\.(?:find|findOne|findOneAndUpdate|update|updateOne|updateMany|remove|deleteOne|deleteMany|aggregate|count|countDocuments)\s*\(\s*(?:req|request|body|query|params|payload|user_input|user_data|form_data)\s*(?:\.\s*\w|\[)',
272
+ # A hash/HMAC/cipher `.update(req.body)` digests the body; it isn't a query (#330).
273
+ # Only when the receiver's last call (past chained `.update()`s) builds the digest.
274
+ receiver_unless: '(?:\bcreate(?:Hmac|Hash|Cipheriv|Decipheriv|Cipher|Decipher|Sign|Verify)|\bhashlib\.\w{1,40}|\bhmac\.new|\bCryptoJS\.[\w.]{1,80}|\bforge\.(?:md|hmac)\.[\w.]{1,80})\(\)(?:\.update\(\)){0,8}$'}
275
+ - {id: mongo-where, kind: nosql_injection, langs: [python, js, go, php], cwe: 943,
276
+ regex: '\$where'}
277
+
278
+ # --- open redirect ----------------------------------------------------------
279
+ - {id: redirect, kind: open_redirect, langs: [python, js, go, php], cwe: 601, gate: tainted,
280
+ regex: '\bredirect\s*\('}
281
+ - {id: express-redirect, kind: open_redirect, langs: [python, js, go, php], cwe: 601, gate: tainted, args: all,
282
+ regex: '\bres\s*\.\s*(?:redirect|location)\s*\('}
283
+
284
+ # --- HTTP response headers (CRLF / header value control) ---------------
285
+ - {id: py-response-header-assign, kind: header_injection, langs: [python], cwe: 113, gate: tainted, args: rest,
286
+ regex: '\b(?:response|resp|res|r)\s*\.\s*headers\s*\[[^\]\n]{1,200}\]\s*=(?!=)'}
287
+ - {id: py-set-header, kind: header_injection, langs: [python], cwe: 113, gate: tainted, args: [1],
288
+ regex: '\.\s*(?:set_header|add_header)\s*\('}
289
+ - {id: express-set-header, kind: header_injection, langs: [js], cwe: 113, gate: tainted, args: [1],
290
+ regex: '\bres\s*\.\s*(?:setHeader|header|set|append)\s*\('}
291
+ - {id: go-header-set, kind: header_injection, langs: [go], cwe: 113, gate: tainted, args: [1],
292
+ regex: '\.\s*Header\s*\(\s*\)\s*\.\s*(?:Set|Add)\s*\('}
293
+ - {id: php-header, kind: header_injection, langs: [php], cwe: 113, gate: tainted,
294
+ regex: '(?<![\w>$:])header\s*\('}
295
+ - {id: java-set-header, kind: header_injection, langs: [java], cwe: 113, gate: tainted, args: [1],
296
+ regex: '\b(?:response|resp|res|httpResponse|servletResponse)\s*\.\s*(?:setHeader|addHeader)\s*\('}
297
+
298
+ # --- user-controlled regular expressions (ReDoS) --------------------------
299
+ - {id: py-re-compile, kind: regex_injection, langs: [python], cwe: 1333, gate: tainted,
300
+ regex: '\bre\.(?:compile|search|match|fullmatch|findall|finditer|split|sub|subn)\s*\('}
301
+ - {id: js-regexp, kind: regex_injection, langs: [js], cwe: 1333, gate: tainted,
302
+ regex: '\bnew\s+RegExp\s*\(|(?<![\w.$])RegExp\s*\('}
303
+ - {id: go-regexp, kind: regex_injection, langs: [go], cwe: 1333, gate: tainted,
304
+ regex: '\bregexp\.(?:Compile|MustCompile|CompilePOSIX|MustCompilePOSIX|MatchString)\s*\('}
305
+ - {id: java-pattern, kind: regex_injection, langs: [java], cwe: 1333, gate: tainted,
306
+ regex: '\bPattern\s*\.\s*(?:compile|matches)\s*\('}
307
+ - {id: php-preg, kind: regex_injection, langs: [php], cwe: 1333, gate: tainted,
308
+ regex: '\bpreg_(?:match|match_all|replace|replace_callback|split|grep)\s*\('}