@clear-capabilities/agentic-security-scanner 0.77.0 → 0.78.0

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 (83) hide show
  1. package/bin/.agentic-security/findings.json +1907 -0
  2. package/bin/.agentic-security/last-scan.json +1907 -0
  3. package/bin/.agentic-security/last-scan.json.sig +1 -0
  4. package/bin/.agentic-security/scan-history.json +115 -0
  5. package/bin/.agentic-security/streak.json +20 -0
  6. package/bin/agentic-security.js +33 -2
  7. package/dist/178.index.js +1 -1
  8. package/dist/384.index.js +1 -1
  9. package/dist/637.index.js +1 -1
  10. package/dist/718.index.js +106 -0
  11. package/dist/824.index.js +126 -0
  12. package/dist/838.index.js +1 -1
  13. package/dist/agentic-security.mjs +32 -32
  14. package/dist/agentic-security.mjs.sha256 +1 -1
  15. package/package.json +3 -3
  16. package/src/.agentic-security/findings.json +82642 -0
  17. package/src/.agentic-security/last-scan.json +82642 -0
  18. package/src/.agentic-security/last-scan.json.sig +1 -0
  19. package/src/.agentic-security/scan-history.json +10054 -0
  20. package/src/.agentic-security/streak.json +21 -0
  21. package/src/dataflow/.agentic-security/findings.json +3515 -0
  22. package/src/dataflow/.agentic-security/last-scan.json +3515 -0
  23. package/src/dataflow/.agentic-security/last-scan.json.sig +1 -0
  24. package/src/dataflow/.agentic-security/scan-history.json +702 -0
  25. package/src/dataflow/.agentic-security/streak.json +22 -0
  26. package/src/dataflow/async-sequencing.js +16 -7
  27. package/src/dataflow/builtin-summaries.js +131 -0
  28. package/src/dataflow/catalog.js +107 -0
  29. package/src/dataflow/cross-repo.js +75 -1
  30. package/src/dataflow/engine.js +129 -0
  31. package/src/dataflow/implicit-flow.js +24 -6
  32. package/src/dataflow/stub-aware-filter.js +69 -11
  33. package/src/dataflow/summaries.js +28 -3
  34. package/src/engine-parallel.js +70 -0
  35. package/src/engine.js +165 -15
  36. package/src/ir/.agentic-security/findings.json +3777 -0
  37. package/src/ir/.agentic-security/last-scan.json +3777 -0
  38. package/src/ir/.agentic-security/last-scan.json.sig +1 -0
  39. package/src/ir/.agentic-security/scan-history.json +771 -0
  40. package/src/ir/.agentic-security/streak.json +21 -0
  41. package/src/ir/index.js +22 -1
  42. package/src/ir/parser-go.js +403 -0
  43. package/src/ir/parser-js.js +2 -0
  44. package/src/ir/parser-php.js +330 -0
  45. package/src/ir/parser-py.helper.py +137 -11
  46. package/src/ir/parser-rb.js +309 -0
  47. package/src/posture/.agentic-security/findings.json +51562 -0
  48. package/src/posture/.agentic-security/last-scan.json +51562 -0
  49. package/src/posture/.agentic-security/last-scan.json.sig +1 -0
  50. package/src/posture/.agentic-security/scan-history.json +650 -0
  51. package/src/posture/.agentic-security/streak.json +20 -0
  52. package/src/posture/calibration.js +14 -0
  53. package/src/posture/triage.js +13 -0
  54. package/src/report/.agentic-security/findings.json +80 -0
  55. package/src/report/.agentic-security/last-scan.json +80 -0
  56. package/src/report/.agentic-security/last-scan.json.sig +1 -0
  57. package/src/report/.agentic-security/scan-history.json +35 -0
  58. package/src/report/.agentic-security/streak.json +22 -0
  59. package/src/report/index.js +23 -2
  60. package/src/sast/.agentic-security/findings.json +5190 -0
  61. package/src/sast/.agentic-security/last-scan.json +5190 -0
  62. package/src/sast/.agentic-security/last-scan.json.sig +1 -0
  63. package/src/sast/.agentic-security/scan-history.json +408 -0
  64. package/src/sast/.agentic-security/streak.json +20 -0
  65. package/src/sast/cache-poisoning.js +77 -0
  66. package/src/sast/comparison-safety.js +73 -0
  67. package/src/sast/db-taint.js +54 -0
  68. package/src/sast/graphql.js +127 -0
  69. package/src/sast/llm-stored-prompt.js +57 -0
  70. package/src/sast/mutation-xss.js +43 -0
  71. package/src/sast/nosql-injection.js +5 -0
  72. package/src/sast/null-byte-injection.js +76 -0
  73. package/src/sast/redos-nfa.js +338 -0
  74. package/src/sast/sensitive-data-logging.js +73 -0
  75. package/src/sast/weak-password-hash.js +77 -0
  76. package/src/sast/weak-randomness.js +100 -0
  77. package/src/sca/.agentic-security/findings.json +1587 -0
  78. package/src/sca/.agentic-security/last-scan.json +1587 -0
  79. package/src/sca/.agentic-security/last-scan.json.sig +1 -0
  80. package/src/sca/.agentic-security/scan-history.json +36 -0
  81. package/src/sca/.agentic-security/streak.json +21 -0
  82. package/src/sca/llm-function-extract.js +107 -0
  83. package/src/sca/vendor-detect.js +91 -0
@@ -0,0 +1 @@
1
+ 411dd662c96624bcc85746b90c2d1a487fd15e7da71fe116cb3d8e548071a588
@@ -0,0 +1,408 @@
1
+ [
2
+ {
3
+ "timestamp": "2026-05-26T16:30:07.351Z",
4
+ "label": "scan",
5
+ "total": 17,
6
+ "critical": 0,
7
+ "high": 0,
8
+ "medium": 2,
9
+ "low": 15,
10
+ "kev": 0,
11
+ "ids": [
12
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
13
+ "client-side:CLIENT_EVAL:client-side.js:135",
14
+ "client-side:CLIENT_EVAL:client-side.js:139",
15
+ "client-side:CLIENT_EVAL:client-side.js:140",
16
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
17
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
18
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
19
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
20
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
21
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
22
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
23
+ "ssrf-meta-hardcoded:go-extended.js:39",
24
+ "ssrf-meta-hardcoded:python-sinks.js:186",
25
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
26
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
27
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
28
+ "zip-slip:zip-slip.js:192:node-entry"
29
+ ]
30
+ },
31
+ {
32
+ "timestamp": "2026-05-26T16:34:28.797Z",
33
+ "label": "scan",
34
+ "total": 17,
35
+ "critical": 0,
36
+ "high": 0,
37
+ "medium": 2,
38
+ "low": 15,
39
+ "kev": 0,
40
+ "ids": [
41
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
42
+ "client-side:CLIENT_EVAL:client-side.js:135",
43
+ "client-side:CLIENT_EVAL:client-side.js:139",
44
+ "client-side:CLIENT_EVAL:client-side.js:140",
45
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
46
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
47
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
48
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
49
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
50
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
51
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
52
+ "ssrf-meta-hardcoded:go-extended.js:39",
53
+ "ssrf-meta-hardcoded:python-sinks.js:186",
54
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
55
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
56
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
57
+ "zip-slip:zip-slip.js:192:node-entry"
58
+ ]
59
+ },
60
+ {
61
+ "timestamp": "2026-05-27T01:10:20.082Z",
62
+ "label": "scan",
63
+ "total": 17,
64
+ "critical": 0,
65
+ "high": 0,
66
+ "medium": 2,
67
+ "low": 15,
68
+ "kev": 0,
69
+ "ids": [
70
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
71
+ "client-side:CLIENT_EVAL:client-side.js:135",
72
+ "client-side:CLIENT_EVAL:client-side.js:139",
73
+ "client-side:CLIENT_EVAL:client-side.js:140",
74
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
75
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
76
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
77
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
78
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
79
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
80
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
81
+ "ssrf-meta-hardcoded:go-extended.js:39",
82
+ "ssrf-meta-hardcoded:python-sinks.js:186",
83
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
84
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
85
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
86
+ "zip-slip:zip-slip.js:192:node-entry"
87
+ ]
88
+ },
89
+ {
90
+ "timestamp": "2026-05-27T03:05:16.971Z",
91
+ "label": "scan",
92
+ "total": 17,
93
+ "critical": 0,
94
+ "high": 0,
95
+ "medium": 2,
96
+ "low": 15,
97
+ "kev": 0,
98
+ "ids": [
99
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
100
+ "client-side:CLIENT_EVAL:client-side.js:135",
101
+ "client-side:CLIENT_EVAL:client-side.js:139",
102
+ "client-side:CLIENT_EVAL:client-side.js:140",
103
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
104
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
105
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
106
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
107
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
108
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
109
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
110
+ "ssrf-meta-hardcoded:go-extended.js:39",
111
+ "ssrf-meta-hardcoded:python-sinks.js:186",
112
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
113
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
114
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
115
+ "zip-slip:zip-slip.js:192:node-entry"
116
+ ]
117
+ },
118
+ {
119
+ "timestamp": "2026-05-27T03:18:22.550Z",
120
+ "label": "scan",
121
+ "total": 17,
122
+ "critical": 0,
123
+ "high": 0,
124
+ "medium": 2,
125
+ "low": 15,
126
+ "kev": 0,
127
+ "ids": [
128
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
129
+ "client-side:CLIENT_EVAL:client-side.js:135",
130
+ "client-side:CLIENT_EVAL:client-side.js:139",
131
+ "client-side:CLIENT_EVAL:client-side.js:140",
132
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
133
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
134
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
135
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
136
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
137
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
138
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
139
+ "ssrf-meta-hardcoded:go-extended.js:39",
140
+ "ssrf-meta-hardcoded:python-sinks.js:186",
141
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
142
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
143
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
144
+ "zip-slip:zip-slip.js:192:node-entry"
145
+ ]
146
+ },
147
+ {
148
+ "timestamp": "2026-05-27T09:09:50.637Z",
149
+ "label": "scan",
150
+ "total": 17,
151
+ "critical": 0,
152
+ "high": 0,
153
+ "medium": 2,
154
+ "low": 15,
155
+ "kev": 0,
156
+ "ids": [
157
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
158
+ "client-side:CLIENT_EVAL:client-side.js:135",
159
+ "client-side:CLIENT_EVAL:client-side.js:139",
160
+ "client-side:CLIENT_EVAL:client-side.js:140",
161
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
162
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
163
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
164
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
165
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
166
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
167
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
168
+ "ssrf-meta-hardcoded:go-extended.js:39",
169
+ "ssrf-meta-hardcoded:python-sinks.js:186",
170
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
171
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
172
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
173
+ "zip-slip:zip-slip.js:192:node-entry"
174
+ ]
175
+ },
176
+ {
177
+ "timestamp": "2026-05-27T09:10:10.121Z",
178
+ "label": "scan",
179
+ "total": 17,
180
+ "critical": 0,
181
+ "high": 0,
182
+ "medium": 2,
183
+ "low": 15,
184
+ "kev": 0,
185
+ "ids": [
186
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
187
+ "client-side:CLIENT_EVAL:client-side.js:135",
188
+ "client-side:CLIENT_EVAL:client-side.js:139",
189
+ "client-side:CLIENT_EVAL:client-side.js:140",
190
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
191
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
192
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
193
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
194
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
195
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
196
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
197
+ "ssrf-meta-hardcoded:go-extended.js:39",
198
+ "ssrf-meta-hardcoded:python-sinks.js:186",
199
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
200
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
201
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
202
+ "zip-slip:zip-slip.js:192:node-entry"
203
+ ]
204
+ },
205
+ {
206
+ "timestamp": "2026-05-27T09:12:25.348Z",
207
+ "label": "scan",
208
+ "total": 17,
209
+ "critical": 0,
210
+ "high": 0,
211
+ "medium": 2,
212
+ "low": 15,
213
+ "kev": 0,
214
+ "ids": [
215
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
216
+ "client-side:CLIENT_EVAL:client-side.js:135",
217
+ "client-side:CLIENT_EVAL:client-side.js:139",
218
+ "client-side:CLIENT_EVAL:client-side.js:140",
219
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
220
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
221
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
222
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
223
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
224
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
225
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
226
+ "ssrf-meta-hardcoded:go-extended.js:39",
227
+ "ssrf-meta-hardcoded:python-sinks.js:186",
228
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
229
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
230
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
231
+ "zip-slip:zip-slip.js:192:node-entry"
232
+ ]
233
+ },
234
+ {
235
+ "timestamp": "2026-05-27T09:17:13.165Z",
236
+ "label": "scan",
237
+ "total": 17,
238
+ "critical": 0,
239
+ "high": 0,
240
+ "medium": 2,
241
+ "low": 15,
242
+ "kev": 0,
243
+ "ids": [
244
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
245
+ "client-side:CLIENT_EVAL:client-side.js:135",
246
+ "client-side:CLIENT_EVAL:client-side.js:139",
247
+ "client-side:CLIENT_EVAL:client-side.js:140",
248
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
249
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
250
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
251
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
252
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
253
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
254
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
255
+ "ssrf-meta-hardcoded:go-extended.js:39",
256
+ "ssrf-meta-hardcoded:python-sinks.js:186",
257
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
258
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
259
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
260
+ "zip-slip:zip-slip.js:192:node-entry"
261
+ ]
262
+ },
263
+ {
264
+ "timestamp": "2026-05-27T09:21:04.965Z",
265
+ "label": "scan",
266
+ "total": 17,
267
+ "critical": 0,
268
+ "high": 0,
269
+ "medium": 2,
270
+ "low": 15,
271
+ "kev": 0,
272
+ "ids": [
273
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
274
+ "client-side:CLIENT_EVAL:client-side.js:135",
275
+ "client-side:CLIENT_EVAL:client-side.js:139",
276
+ "client-side:CLIENT_EVAL:client-side.js:140",
277
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
278
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
279
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
280
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
281
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
282
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
283
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
284
+ "ssrf-meta-hardcoded:go-extended.js:39",
285
+ "ssrf-meta-hardcoded:python-sinks.js:186",
286
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
287
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
288
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
289
+ "zip-slip:zip-slip.js:192:node-entry"
290
+ ]
291
+ },
292
+ {
293
+ "timestamp": "2026-05-27T09:21:46.189Z",
294
+ "label": "scan",
295
+ "total": 17,
296
+ "critical": 0,
297
+ "high": 0,
298
+ "medium": 2,
299
+ "low": 15,
300
+ "kev": 0,
301
+ "ids": [
302
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
303
+ "client-side:CLIENT_EVAL:client-side.js:135",
304
+ "client-side:CLIENT_EVAL:client-side.js:139",
305
+ "client-side:CLIENT_EVAL:client-side.js:140",
306
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
307
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
308
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
309
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
310
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
311
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
312
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
313
+ "ssrf-meta-hardcoded:go-extended.js:39",
314
+ "ssrf-meta-hardcoded:python-sinks.js:186",
315
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
316
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
317
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
318
+ "zip-slip:zip-slip.js:192:node-entry"
319
+ ]
320
+ },
321
+ {
322
+ "timestamp": "2026-05-27T09:24:34.687Z",
323
+ "label": "scan",
324
+ "total": 17,
325
+ "critical": 0,
326
+ "high": 0,
327
+ "medium": 2,
328
+ "low": 15,
329
+ "kev": 0,
330
+ "ids": [
331
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
332
+ "client-side:CLIENT_EVAL:client-side.js:135",
333
+ "client-side:CLIENT_EVAL:client-side.js:139",
334
+ "client-side:CLIENT_EVAL:client-side.js:140",
335
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
336
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
337
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
338
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
339
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
340
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
341
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
342
+ "ssrf-meta-hardcoded:go-extended.js:39",
343
+ "ssrf-meta-hardcoded:python-sinks.js:186",
344
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
345
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
346
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
347
+ "zip-slip:zip-slip.js:192:node-entry"
348
+ ]
349
+ },
350
+ {
351
+ "timestamp": "2026-05-27T09:43:08.807Z",
352
+ "label": "scan",
353
+ "total": 17,
354
+ "critical": 0,
355
+ "high": 0,
356
+ "medium": 2,
357
+ "low": 15,
358
+ "kev": 0,
359
+ "ids": [
360
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
361
+ "client-side:CLIENT_EVAL:client-side.js:135",
362
+ "client-side:CLIENT_EVAL:client-side.js:139",
363
+ "client-side:CLIENT_EVAL:client-side.js:140",
364
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
365
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
366
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
367
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
368
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
369
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
370
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
371
+ "ssrf-meta-hardcoded:go-extended.js:39",
372
+ "ssrf-meta-hardcoded:python-sinks.js:186",
373
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
374
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
375
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
376
+ "zip-slip:zip-slip.js:192:node-entry"
377
+ ]
378
+ },
379
+ {
380
+ "timestamp": "2026-05-27T09:43:30.205Z",
381
+ "label": "scan",
382
+ "total": 17,
383
+ "critical": 0,
384
+ "high": 0,
385
+ "medium": 2,
386
+ "low": 15,
387
+ "kev": 0,
388
+ "ids": [
389
+ "authz:authz.js:33:AuthZ__jwt_verify_called_without_algorithms_allow_list",
390
+ "client-side:CLIENT_EVAL:client-side.js:135",
391
+ "client-side:CLIENT_EVAL:client-side.js:139",
392
+ "client-side:CLIENT_EVAL:client-side.js:140",
393
+ "llm-owasp:llm-owasp.js:180:llm01-dynamic-system:concat",
394
+ "llm-owasp:llm-owasp.js:181:llm01-dynamic-system:fstring",
395
+ "llm-owasp:llm-owasp.js:182:llm01-dynamic-system:template",
396
+ "llm-owasp:llm-owasp.js:183:llm10-no-token-budget",
397
+ "prompt-tpl:llm-owasp.js:125:Prompt_Template__user_input_interpolated_into_prompt_string_",
398
+ "spec-drift:rate-limit-impl:rate-limit.js:34",
399
+ "spec-drift:rate-limit-impl:rate-limit.js:77",
400
+ "ssrf-meta-hardcoded:go-extended.js:39",
401
+ "ssrf-meta-hardcoded:python-sinks.js:186",
402
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:15",
403
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:48",
404
+ "ssrf-meta-hardcoded:ssrf-cloud-metadata.js:73",
405
+ "zip-slip:zip-slip.js:192:node-entry"
406
+ ]
407
+ }
408
+ ]
@@ -0,0 +1,20 @@
1
+ {
2
+ "firstScanDate": "2026-05-26T16:30:07.386Z",
3
+ "lastScanDate": "2026-05-27T09:43:30.233Z",
4
+ "totalScans": 14,
5
+ "daysCleanCritical": 2,
6
+ "lastCleanDate": "2026-05-27",
7
+ "lastCriticalDate": null,
8
+ "hasEverHadCritical": false,
9
+ "bestDaysCleanCritical": 2,
10
+ "totalFindingsAtFirstScan": 27,
11
+ "totalFindingsAtLastScan": 28,
12
+ "totalFixesInferred": 0,
13
+ "lastGrade": "A-",
14
+ "bestGrade": "A-",
15
+ "launchCheckPassedAt": null,
16
+ "achievements": [
17
+ "first-scan"
18
+ ],
19
+ "previousGrade": "A-"
20
+ }
@@ -0,0 +1,77 @@
1
+ // Cache poisoning via tainted response headers.
2
+ //
3
+ // Detects reflected request headers in responses that CDNs/proxies key on.
4
+ // X-Forwarded-Host, X-Forwarded-Proto, X-Original-URL reflected into
5
+ // response headers or HTML body enables web cache poisoning attacks.
6
+
7
+ function _line(raw, idx) { return raw.slice(0, idx).split('\n').length; }
8
+
9
+ const CACHE_KEY_HEADERS = /x-forwarded-host|x-forwarded-proto|x-original-url|x-rewrite-url|x-forwarded-for|x-host/i;
10
+ const TAINT_SOURCES = /req\.headers|request\.headers|request\.META|getHeader|get_header|\$_SERVER/;
11
+
12
+ export function scanCachePoisoning(fp, raw) {
13
+ if (!fp || !raw || typeof raw !== 'string') return [];
14
+ if (raw.length > 500_000) return [];
15
+ if (!/\.(?:js|jsx|ts|tsx|mjs|cjs|py|go|rb|php|phtml)$/i.test(fp)) return [];
16
+
17
+ const findings = [];
18
+
19
+ // Pattern 1: Reflected cache-key header in response
20
+ // res.setHeader('X-Forwarded-Host', req.headers['x-forwarded-host'])
21
+ const headerReflectRe = /(?:setHeader|set|header|add_header|Header\.Set|Header\.Add)\s*\(\s*['"]([^'"]+)['"]\s*,\s*[^)]*(?:req\.headers|request\.headers|request\.META|\$_SERVER)/g;
22
+ for (const m of raw.matchAll(headerReflectRe)) {
23
+ const headerName = m[1];
24
+ if (!CACHE_KEY_HEADERS.test(headerName)) continue;
25
+ const line = _line(raw, m.index);
26
+ findings.push({
27
+ id: `cache-poison-reflect:${fp}:${line}`,
28
+ file: fp, line,
29
+ vuln: `Cache Poisoning — ${headerName} reflected from request to response`,
30
+ severity: 'high',
31
+ family: 'cache-poisoning',
32
+ cwe: 'CWE-349',
33
+ parser: 'CACHE-POISON',
34
+ confidence: 0.75,
35
+ description: `The ${headerName} request header is reflected in the response. If a CDN or reverse proxy caches this response, an attacker can poison the cache for all users by sending a crafted ${headerName} value.`,
36
+ remediation: `Don't reflect ${headerName} into the response. If you need the value for redirects, validate it against an allow-list of trusted hosts.`,
37
+ });
38
+ }
39
+
40
+ // Pattern 2: Cache-Control or Vary set from user input
41
+ const cacheControlTaintRe = /(?:Cache-Control|Vary)\s*['"]\s*,\s*[^)]*(?:req\.|request\.|params|query|body|\$_GET|\$_REQUEST)/g;
42
+ for (const m of raw.matchAll(cacheControlTaintRe)) {
43
+ const line = _line(raw, m.index);
44
+ findings.push({
45
+ id: `cache-poison-control:${fp}:${line}`,
46
+ file: fp, line,
47
+ vuln: 'Cache Poisoning — Cache-Control or Vary header set from user input',
48
+ severity: 'high',
49
+ family: 'cache-poisoning',
50
+ cwe: 'CWE-349',
51
+ parser: 'CACHE-POISON',
52
+ confidence: 0.65,
53
+ description: 'Cache-Control or Vary response header is derived from user input. An attacker can manipulate caching behavior to serve stale or poisoned content.',
54
+ remediation: 'Set Cache-Control and Vary headers from server-side constants, never from user input.',
55
+ });
56
+ }
57
+
58
+ // Pattern 3: Host header used in URL generation (redirect/link)
59
+ const hostRedirectRe = /(?:req\.headers\.host|request\.get_host|request\.host|request\.META\s*\[\s*['"]HTTP_HOST['"])\s*[^;\n]*(?:redirect|location|href|url|link)/gi;
60
+ for (const m of raw.matchAll(hostRedirectRe)) {
61
+ const line = _line(raw, m.index);
62
+ findings.push({
63
+ id: `cache-poison-host:${fp}:${line}`,
64
+ file: fp, line,
65
+ vuln: 'Cache Poisoning — Host header used in URL/redirect generation',
66
+ severity: 'medium',
67
+ family: 'cache-poisoning',
68
+ cwe: 'CWE-644',
69
+ parser: 'CACHE-POISON',
70
+ confidence: 0.60,
71
+ description: 'The Host request header is used to generate URLs or redirects. Combined with caching, an attacker can redirect all cached users to a malicious host.',
72
+ remediation: 'Use a server-configured base URL instead of the Host header. Validate Host against an allow-list.',
73
+ });
74
+ }
75
+
76
+ return findings;
77
+ }
@@ -0,0 +1,73 @@
1
+ // Timing-safe comparison and type-coercion safety detector.
2
+ //
3
+ // Flags:
4
+ // 1. Direct === / == on HMAC/signature/token/OTP values (timing attack)
5
+ // 2. Loose == in authorization checks (type coercion)
6
+ // 3. Missing timingSafeEqual / hmac.compare_digest in verification functions
7
+
8
+ const TIMING_SENSITIVE = /\b(hmac|signature|digest|hash|mac|checksum|token|otp|apiKey|api_key|secret_key|webhook_secret|signing_key)\b/i;
9
+ const AUTH_CONTEXT = /\b(role|permission|isAdmin|is_admin|accessLevel|access_level|privilege|authorization|auth_level)\b/i;
10
+
11
+ function _line(raw, idx) { return raw.slice(0, idx).split('\n').length; }
12
+
13
+ export function scanComparisonSafety(fp, raw) {
14
+ if (!fp || !raw || typeof raw !== 'string') return [];
15
+ if (raw.length > 500_000) return [];
16
+ if (!/\.(?:js|jsx|ts|tsx|mjs|cjs|py|go)$/i.test(fp)) return [];
17
+
18
+ const findings = [];
19
+
20
+ // 1. Timing-unsafe comparison: x === y where x or y is named like a secret
21
+ for (const m of raw.matchAll(/(\w+)\s*===?\s*(\w+)/g)) {
22
+ const left = m[1], right = m[2];
23
+ if (!TIMING_SENSITIVE.test(left) && !TIMING_SENSITIVE.test(right)) continue;
24
+ const line = _line(raw, m.index);
25
+ const lineStart = raw.lastIndexOf('\n', m.index) + 1;
26
+ const lineText = raw.slice(lineStart, raw.indexOf('\n', m.index));
27
+ // Skip if timingSafeEqual or compare_digest is nearby
28
+ const context = raw.slice(Math.max(0, m.index - 200), m.index + 200);
29
+ if (/timingSafeEqual|compare_digest|ConstantTimeCompare|constant_time_compare/i.test(context)) continue;
30
+ // Skip if inside a comment
31
+ if (/^\s*\/\/|^\s*#|^\s*\*/.test(lineText)) continue;
32
+ findings.push({
33
+ id: `timing-unsafe:${fp}:${line}`,
34
+ file: fp, line,
35
+ vuln: 'Timing-Unsafe Comparison — secret/HMAC compared with === instead of timingSafeEqual',
36
+ severity: 'medium',
37
+ family: 'timing-attack',
38
+ cwe: 'CWE-208',
39
+ parser: 'COMPARISON',
40
+ confidence: 0.70,
41
+ description: `String === comparison on "${TIMING_SENSITIVE.exec(left + ' ' + right)?.[1] || 'secret'}" leaks timing information. An attacker can measure response times to guess the value byte-by-byte.`,
42
+ remediation: 'Use crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)) in Node.js, hmac.compare_digest(a, b) in Python, or subtle.ConstantTimeCompare(a, b) in Go.',
43
+ snippet: lineText.trim().slice(0, 100),
44
+ });
45
+ }
46
+
47
+ // 2. Loose equality == in auth context
48
+ for (const m of raw.matchAll(/(\w+)\s*==\s*(?!=)(\w+|['"][^'"]+['"])/g)) {
49
+ const left = m[1], right = m[2];
50
+ if (!AUTH_CONTEXT.test(left) && !AUTH_CONTEXT.test(right)) continue;
51
+ const line = _line(raw, m.index);
52
+ const lineStart = raw.lastIndexOf('\n', m.index) + 1;
53
+ const lineText = raw.slice(lineStart, raw.indexOf('\n', m.index));
54
+ if (/^\s*\/\/|^\s*#|^\s*\*/.test(lineText)) continue;
55
+ // Only flag JS/TS (Python and Go use == for equality, not coercion)
56
+ if (!/\.(?:js|jsx|ts|tsx|mjs|cjs)$/i.test(fp)) continue;
57
+ findings.push({
58
+ id: `loose-equality-auth:${fp}:${line}`,
59
+ file: fp, line,
60
+ vuln: 'Loose Equality in Auth Check — == allows type coercion bypass',
61
+ severity: 'high',
62
+ family: 'type-coercion',
63
+ cwe: 'CWE-697',
64
+ parser: 'COMPARISON',
65
+ confidence: 0.65,
66
+ description: `Loose equality (==) on "${AUTH_CONTEXT.exec(left + ' ' + right)?.[1] || 'auth field'}" allows type coercion. "1" == 1 is true; an attacker may bypass checks by sending a different type.`,
67
+ remediation: 'Use strict equality (===) for all authorization checks.',
68
+ snippet: lineText.trim().slice(0, 100),
69
+ });
70
+ }
71
+
72
+ return findings;
73
+ }
@@ -213,3 +213,57 @@ export function scanDbTaint(fp, raw) {
213
213
  }
214
214
  return findings;
215
215
  }
216
+
217
+ // ── Cross-file stored injection ────────────────────────────────────────────
218
+ //
219
+ // Extends the same-file detector to work across all files in the project.
220
+ // Collects ORM writes and render-of-reads across all files, then matches
221
+ // by field name to find stored XSS / stored injection paths.
222
+
223
+ export function scanDbTaintCrossFile(fileContents) {
224
+ if (!fileContents || typeof fileContents !== 'object') return [];
225
+ const allWrites = [];
226
+ const allReads = [];
227
+ for (const [fp, raw] of Object.entries(fileContents)) {
228
+ if (!raw || typeof raw !== 'string' || raw.length > 500_000) continue;
229
+ const lang = _lang(fp);
230
+ if (!lang) continue;
231
+ const code = blankComments(raw, lang === 'py' ? 'py' : undefined);
232
+ const writes = _findTaintedWrites(code, lang);
233
+ for (const w of writes) allWrites.push({ ...w, file: fp });
234
+ const reads = _findRendersOfReads(code, lang);
235
+ for (const r of reads) allReads.push({ ...r, file: fp });
236
+ }
237
+ const findings = [];
238
+ const seen = new Set();
239
+ for (const w of allWrites) {
240
+ for (const r of allReads) {
241
+ if (w.file === r.file) continue;
242
+ if (w.field !== r.field) continue;
243
+ const id = `db-taint-xfile:${w.file}:${w.line}->${r.file}:${r.line}:${w.field}`;
244
+ if (seen.has(id)) continue;
245
+ seen.add(id);
246
+ findings.push({
247
+ id,
248
+ file: r.file, line: r.line,
249
+ vuln: `Stored XSS via DB round-trip — cross-file (${w.model || '?'}.${w.field})`,
250
+ severity: 'high',
251
+ cwe: 'CWE-79',
252
+ family: 'stored-xss',
253
+ stride: 'Tampering',
254
+ remediation:
255
+ `User content written to ${w.model || '?'}.${w.field} at ${w.file}:${w.line} is later read at ${r.file}:${r.line} and rendered. ` +
256
+ 'Sanitize at write time (HTML-escape), escape at render time, and use CSP to block inline scripts.',
257
+ parser: 'DB-TAINT-XFILE',
258
+ confidence: 0.60,
259
+ source: { file: w.file, line: w.line, label: `${w.model || '?'}.${w.field} write` },
260
+ sink: { file: r.file, line: r.line, label: `${w.field} render` },
261
+ trace: [
262
+ { file: w.file, line: w.line, kind: 'db-write', sourceLabel: `${w.model || '?'}.${w.field} ← user input` },
263
+ { file: r.file, line: r.line, kind: 'db-read', sourceLabel: `${w.field} → render sink` },
264
+ ],
265
+ });
266
+ }
267
+ }
268
+ return findings;
269
+ }