@clear-capabilities/agentic-security-scanner 0.74.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 (331) hide show
  1. package/CHANGELOG.md +1580 -0
  2. package/bin/.agentic-security/findings.json +1577 -0
  3. package/bin/.agentic-security/last-scan.json +1577 -0
  4. package/bin/.agentic-security/last-scan.json.sig +1 -0
  5. package/bin/.agentic-security/scan-history.json +465 -0
  6. package/bin/.agentic-security/streak.json +25 -0
  7. package/bin/agentic-security-audit.js +198 -0
  8. package/bin/agentic-security-consistency.js +80 -0
  9. package/bin/agentic-security-diff.js +136 -0
  10. package/bin/agentic-security-lsp.js +12 -0
  11. package/bin/agentic-security-mcp.js +40 -0
  12. package/bin/agentic-security-rule.js +153 -0
  13. package/bin/agentic-security.js +1683 -0
  14. package/dist/117.index.js +207 -0
  15. package/dist/178.index.js +250 -0
  16. package/dist/218.index.js +793 -0
  17. package/dist/227.index.js +192 -0
  18. package/dist/301.index.js +167 -0
  19. package/dist/384.index.js +18 -0
  20. package/dist/476.index.js +126 -0
  21. package/dist/513.index.js +373 -0
  22. package/dist/520.index.js +13 -0
  23. package/dist/601.index.js +1038 -0
  24. package/dist/634.index.js +1892 -0
  25. package/dist/637.index.js +216 -0
  26. package/dist/660.index.js +131 -0
  27. package/dist/675.index.js +451 -0
  28. package/dist/826.index.js +188 -0
  29. package/dist/830.index.js +133 -0
  30. package/dist/agentic-security.mjs +272 -0
  31. package/dist/agentic-security.mjs.sha256 +1 -0
  32. package/dist/calibration-seed.json +27 -0
  33. package/package.json +77 -0
  34. package/src/.agentic-security/findings.json +80844 -0
  35. package/src/.agentic-security/last-scan.json +80844 -0
  36. package/src/.agentic-security/last-scan.json.sig +1 -0
  37. package/src/.agentic-security/scan-history.json +8408 -0
  38. package/src/.agentic-security/streak.json +26 -0
  39. package/src/badge.js +188 -0
  40. package/src/compare.js +203 -0
  41. package/src/dataflow/.agentic-security/findings.json +3487 -0
  42. package/src/dataflow/.agentic-security/last-scan.json +3487 -0
  43. package/src/dataflow/.agentic-security/last-scan.json.sig +1 -0
  44. package/src/dataflow/.agentic-security/scan-history.json +735 -0
  45. package/src/dataflow/.agentic-security/streak.json +24 -0
  46. package/src/dataflow/CLAUDE.md +38 -0
  47. package/src/dataflow/access-paths.js +172 -0
  48. package/src/dataflow/async-sequencing.js +177 -0
  49. package/src/dataflow/backward.js +201 -0
  50. package/src/dataflow/catalog-expanded.js +485 -0
  51. package/src/dataflow/catalog.js +659 -0
  52. package/src/dataflow/cross-repo.js +219 -0
  53. package/src/dataflow/engine.js +588 -0
  54. package/src/dataflow/exception-flow.js +116 -0
  55. package/src/dataflow/exploit-prover.js +187 -0
  56. package/src/dataflow/higher-order.js +221 -0
  57. package/src/dataflow/ifds.js +347 -0
  58. package/src/dataflow/implicit-flow.js +129 -0
  59. package/src/dataflow/incremental.js +229 -0
  60. package/src/dataflow/index.js +181 -0
  61. package/src/dataflow/numeric-domain.js +192 -0
  62. package/src/dataflow/path-feasibility.js +114 -0
  63. package/src/dataflow/points-to.js +337 -0
  64. package/src/dataflow/polyglot.js +190 -0
  65. package/src/dataflow/proven-clean.js +159 -0
  66. package/src/dataflow/receiver-context.js +76 -0
  67. package/src/dataflow/sanitizer-proof.js +154 -0
  68. package/src/dataflow/soft-taint.js +140 -0
  69. package/src/dataflow/string-domain.js +234 -0
  70. package/src/dataflow/stub-aware-filter.js +100 -0
  71. package/src/dataflow/summaries.js +132 -0
  72. package/src/dataflow/symbolic-exec.js +238 -0
  73. package/src/dataflow/tabulation.js +135 -0
  74. package/src/engine.js +7763 -0
  75. package/src/history-scan.js +229 -0
  76. package/src/index.js +3 -0
  77. package/src/integrations/.agentic-security/findings.json +1504 -0
  78. package/src/integrations/.agentic-security/last-scan.json +1504 -0
  79. package/src/integrations/.agentic-security/scan-history.json +40 -0
  80. package/src/integrations/.agentic-security/streak.json +21 -0
  81. package/src/integrations/index.js +321 -0
  82. package/src/integrations/tickets.js +200 -0
  83. package/src/ir/.agentic-security/findings.json +3036 -0
  84. package/src/ir/.agentic-security/last-scan.json +3036 -0
  85. package/src/ir/.agentic-security/last-scan.json.sig +1 -0
  86. package/src/ir/.agentic-security/scan-history.json +364 -0
  87. package/src/ir/.agentic-security/streak.json +23 -0
  88. package/src/ir/CLAUDE.md +172 -0
  89. package/src/ir/callgraph.js +73 -0
  90. package/src/ir/class-hierarchy.js +195 -0
  91. package/src/ir/index.js +152 -0
  92. package/src/ir/parser-cs.js +260 -0
  93. package/src/ir/parser-java.js +286 -0
  94. package/src/ir/parser-js.js +413 -0
  95. package/src/ir/parser-kt.js +258 -0
  96. package/src/ir/parser-py-cst.js +136 -0
  97. package/src/ir/parser-py.helper.py +501 -0
  98. package/src/ir/parser-py.js +312 -0
  99. package/src/ir/ssa.js +315 -0
  100. package/src/ir/type-stubs.js +288 -0
  101. package/src/leaderboard.js +152 -0
  102. package/src/llm-validator/.agentic-security/findings.json +1891 -0
  103. package/src/llm-validator/.agentic-security/last-scan.json +1891 -0
  104. package/src/llm-validator/.agentic-security/last-scan.json.sig +1 -0
  105. package/src/llm-validator/.agentic-security/scan-history.json +168 -0
  106. package/src/llm-validator/.agentic-security/streak.json +20 -0
  107. package/src/llm-validator/consistency.js +141 -0
  108. package/src/llm-validator/index.js +437 -0
  109. package/src/lsp/.agentic-security/findings.json +28 -0
  110. package/src/lsp/.agentic-security/last-scan.json +28 -0
  111. package/src/lsp/.agentic-security/scan-history.json +79 -0
  112. package/src/lsp/.agentic-security/streak.json +22 -0
  113. package/src/lsp/server.js +275 -0
  114. package/src/mcp/.agentic-security/findings.json +8358 -0
  115. package/src/mcp/.agentic-security/last-scan.json +8358 -0
  116. package/src/mcp/.agentic-security/last-scan.json.sig +1 -0
  117. package/src/mcp/.agentic-security/scan-history.json +1125 -0
  118. package/src/mcp/.agentic-security/streak.json +22 -0
  119. package/src/mcp/CLAUDE.md +54 -0
  120. package/src/mcp/audit.js +136 -0
  121. package/src/mcp/redact.js +75 -0
  122. package/src/mcp/server.js +158 -0
  123. package/src/mcp/stdio.js +83 -0
  124. package/src/mcp/tools.js +940 -0
  125. package/src/mcp/validate.js +49 -0
  126. package/src/personality.js +164 -0
  127. package/src/poc-video.js +239 -0
  128. package/src/posture/.agentic-security/findings.json +51239 -0
  129. package/src/posture/.agentic-security/last-scan.json +51239 -0
  130. package/src/posture/.agentic-security/last-scan.json.sig +1 -0
  131. package/src/posture/.agentic-security/scan-history.json +5557 -0
  132. package/src/posture/.agentic-security/streak.json +24 -0
  133. package/src/posture/CLAUDE.md +42 -0
  134. package/src/posture/adversarial-self-test.js +114 -0
  135. package/src/posture/adversary-agent.js +204 -0
  136. package/src/posture/agents-memory.js +135 -0
  137. package/src/posture/ai-code-fingerprint.js +171 -0
  138. package/src/posture/aibom.js +284 -0
  139. package/src/posture/api-inventory.js +96 -0
  140. package/src/posture/attack-playbooks.js +305 -0
  141. package/src/posture/auditor-agent.js +115 -0
  142. package/src/posture/auth-posture-import.js +135 -0
  143. package/src/posture/baseline-compare.js +114 -0
  144. package/src/posture/blast-radius.js +836 -0
  145. package/src/posture/bounty-prediction.js +141 -0
  146. package/src/posture/business-logic.js +239 -0
  147. package/src/posture/calibration-drift.js +93 -0
  148. package/src/posture/calibration-seed.json +27 -0
  149. package/src/posture/calibration.js +204 -0
  150. package/src/posture/clustering.js +75 -0
  151. package/src/posture/concurrency-checker.js +265 -0
  152. package/src/posture/confidence.js +65 -0
  153. package/src/posture/container-runtime.js +149 -0
  154. package/src/posture/counterfactual.js +109 -0
  155. package/src/posture/cross-lang-graphql.js +165 -0
  156. package/src/posture/cross-lang-grpc.js +166 -0
  157. package/src/posture/cross-lang-meta.js +101 -0
  158. package/src/posture/cross-lang-openapi.js +187 -0
  159. package/src/posture/cross-lang-orm.js +153 -0
  160. package/src/posture/cross-lang-queues.js +210 -0
  161. package/src/posture/crown-jewels.js +110 -0
  162. package/src/posture/custom-rules.js +361 -0
  163. package/src/posture/cve-alert-daemon.js +433 -0
  164. package/src/posture/cve-lookup.js +129 -0
  165. package/src/posture/dead-code.js +430 -0
  166. package/src/posture/defender-agent.js +158 -0
  167. package/src/posture/deploy-platform.js +204 -0
  168. package/src/posture/detector-fuzz.js +61 -0
  169. package/src/posture/deterministic.js +99 -0
  170. package/src/posture/drift.js +165 -0
  171. package/src/posture/epss.js +156 -0
  172. package/src/posture/exploitability-probability.js +212 -0
  173. package/src/posture/exploitability.js +121 -0
  174. package/src/posture/feature-flags.js +110 -0
  175. package/src/posture/finding-defaults.js +132 -0
  176. package/src/posture/fix-history.js +411 -0
  177. package/src/posture/fix-plan.js +121 -0
  178. package/src/posture/fix-verify-loop.js +157 -0
  179. package/src/posture/fix-verify.js +130 -0
  180. package/src/posture/flow-narration.js +105 -0
  181. package/src/posture/grader-calibration.js +156 -0
  182. package/src/posture/harness-discovery.js +113 -0
  183. package/src/posture/holdout-eval.js +144 -0
  184. package/src/posture/iac-reachability.js +163 -0
  185. package/src/posture/iam-policy.js +128 -0
  186. package/src/posture/integrity.js +97 -0
  187. package/src/posture/learning.js +166 -0
  188. package/src/posture/license-policy.js +109 -0
  189. package/src/posture/llm-redteam-prompts.js +418 -0
  190. package/src/posture/llm-redteam.js +303 -0
  191. package/src/posture/material-change.js +163 -0
  192. package/src/posture/mitigation-composite.js +55 -0
  193. package/src/posture/mttr.js +91 -0
  194. package/src/posture/network-policy-import.js +126 -0
  195. package/src/posture/path-predicates.js +99 -0
  196. package/src/posture/persona-prioritization.js +153 -0
  197. package/src/posture/poc-cwe-map.js +51 -0
  198. package/src/posture/poc-generator.js +500 -0
  199. package/src/posture/policy-gate.js +174 -0
  200. package/src/posture/pre-incident-archaeology.js +110 -0
  201. package/src/posture/profile.js +93 -0
  202. package/src/posture/reachability-filter.js +42 -0
  203. package/src/posture/regression-test-gen.js +200 -0
  204. package/src/posture/reverse-blast-radius.js +110 -0
  205. package/src/posture/router.js +109 -0
  206. package/src/posture/rule-overrides.js +198 -0
  207. package/src/posture/rule-pack-signing.js +209 -0
  208. package/src/posture/rule-packs.js +143 -0
  209. package/src/posture/rule-synthesis.js +108 -0
  210. package/src/posture/ruleset-version.js +71 -0
  211. package/src/posture/sbom.js +129 -0
  212. package/src/posture/schema-aware-bridge.js +207 -0
  213. package/src/posture/security-trend.js +87 -0
  214. package/src/posture/semantic-clone.js +114 -0
  215. package/src/posture/specification-mining.js +170 -0
  216. package/src/posture/stable-id.js +75 -0
  217. package/src/posture/stack-playbook.js +229 -0
  218. package/src/posture/streak.js +249 -0
  219. package/src/posture/suppressions.js +135 -0
  220. package/src/posture/telemetry-ingest.js +112 -0
  221. package/src/posture/threat-model.js +145 -0
  222. package/src/posture/three-agent-pipeline.js +74 -0
  223. package/src/posture/triage.js +146 -0
  224. package/src/posture/trust-boundary-diagram.js +115 -0
  225. package/src/posture/type-narrowing.js +129 -0
  226. package/src/posture/validator-metrics.js +179 -0
  227. package/src/posture/verifier-ephemeral.js +118 -0
  228. package/src/posture/verifier-target.js +147 -0
  229. package/src/posture/verifier.js +257 -0
  230. package/src/posture/version.js +75 -0
  231. package/src/posture/waf-ingest.js +200 -0
  232. package/src/posture/why-fired.js +141 -0
  233. package/src/pr-comment.js +172 -0
  234. package/src/pr-delta.js +198 -0
  235. package/src/report/.agentic-security/findings.json +79 -0
  236. package/src/report/.agentic-security/last-scan.json +79 -0
  237. package/src/report/.agentic-security/last-scan.json.sig +1 -0
  238. package/src/report/.agentic-security/scan-history.json +332 -0
  239. package/src/report/.agentic-security/streak.json +23 -0
  240. package/src/report/index.js +1136 -0
  241. package/src/report/mascot.js +42 -0
  242. package/src/runScan.js +141 -0
  243. package/src/sast/.agentic-security/findings.json +5051 -0
  244. package/src/sast/.agentic-security/last-scan.json +5051 -0
  245. package/src/sast/.agentic-security/last-scan.json.sig +1 -0
  246. package/src/sast/.agentic-security/scan-history.json +788 -0
  247. package/src/sast/.agentic-security/streak.json +23 -0
  248. package/src/sast/CLAUDE.md +39 -0
  249. package/src/sast/_comment-strip.js +46 -0
  250. package/src/sast/agent-tool-escalation.js +131 -0
  251. package/src/sast/auth-provider.js +171 -0
  252. package/src/sast/authz.js +236 -0
  253. package/src/sast/bench-shape/.agentic-security/findings.json +28 -0
  254. package/src/sast/bench-shape/.agentic-security/last-scan.json +28 -0
  255. package/src/sast/bench-shape/.agentic-security/scan-history.json +24 -0
  256. package/src/sast/bench-shape/.agentic-security/streak.json +22 -0
  257. package/src/sast/bench-shape/index.js +62 -0
  258. package/src/sast/claude-hook-injection.js +199 -0
  259. package/src/sast/claude-md-prompt-injection.js +170 -0
  260. package/src/sast/claude-settings.js +165 -0
  261. package/src/sast/client-side.js +149 -0
  262. package/src/sast/cpp-bench-extras.js +122 -0
  263. package/src/sast/cpp-dataflow.js +430 -0
  264. package/src/sast/cpp.js +248 -0
  265. package/src/sast/csharp.js +152 -0
  266. package/src/sast/csrf.js +82 -0
  267. package/src/sast/dart-flutter.js +173 -0
  268. package/src/sast/db-rls.js +147 -0
  269. package/src/sast/db-taint.js +215 -0
  270. package/src/sast/defi-deep.js +242 -0
  271. package/src/sast/deserialization-gadgets.js +113 -0
  272. package/src/sast/django-hardening.js +230 -0
  273. package/src/sast/env-hygiene.js +125 -0
  274. package/src/sast/fastapi-hardening.js +145 -0
  275. package/src/sast/go-extended.js +84 -0
  276. package/src/sast/host-header.js +106 -0
  277. package/src/sast/index.js +17 -0
  278. package/src/sast/java-ast-folding.js +561 -0
  279. package/src/sast/java-bench-extras.js +708 -0
  280. package/src/sast/java-collection-passthrough.js +178 -0
  281. package/src/sast/java-constant-fold.js +244 -0
  282. package/src/sast/java-deserialization.js +125 -0
  283. package/src/sast/jndi.js +104 -0
  284. package/src/sast/juliet-shape.js +324 -0
  285. package/src/sast/jwt-exp.js +104 -0
  286. package/src/sast/kotlin.js +82 -0
  287. package/src/sast/laravel-hardening.js +198 -0
  288. package/src/sast/ldap-injection.js +100 -0
  289. package/src/sast/llm-owasp.js +465 -0
  290. package/src/sast/llm-stored-prompt.js +103 -0
  291. package/src/sast/llm-trading-agent.js +161 -0
  292. package/src/sast/llm.js +308 -0
  293. package/src/sast/logic.js +140 -0
  294. package/src/sast/mass-assignment.js +101 -0
  295. package/src/sast/mcp-audit.js +242 -0
  296. package/src/sast/mobile-manifest.js +195 -0
  297. package/src/sast/model-load.js +164 -0
  298. package/src/sast/mutation-xss.js +87 -0
  299. package/src/sast/nosql-injection.js +82 -0
  300. package/src/sast/open-redirect.js +119 -0
  301. package/src/sast/php.js +91 -0
  302. package/src/sast/pipeline.js +122 -0
  303. package/src/sast/primary-cwe-java.js +155 -0
  304. package/src/sast/prompt-firewall.js +151 -0
  305. package/src/sast/prompt-template.js +157 -0
  306. package/src/sast/prototype-pollution.js +112 -0
  307. package/src/sast/python-sinks.js +195 -0
  308. package/src/sast/quarkus-hardening.js +102 -0
  309. package/src/sast/rag-poisoning.js +118 -0
  310. package/src/sast/rate-limit.js +128 -0
  311. package/src/sast/response-splitting.js +138 -0
  312. package/src/sast/ruby.js +108 -0
  313. package/src/sast/rust.js +105 -0
  314. package/src/sast/solidity.js +167 -0
  315. package/src/sast/springboot-hardening.js +186 -0
  316. package/src/sast/ssrf-cloud-metadata.js +80 -0
  317. package/src/sast/ssti.js +116 -0
  318. package/src/sast/swift.js +162 -0
  319. package/src/sast/toctou.js +95 -0
  320. package/src/sast/webhook.js +101 -0
  321. package/src/sast/xpath-injection.js +51 -0
  322. package/src/sast/xxe.js +140 -0
  323. package/src/sast/zip-slip.js +200 -0
  324. package/src/sca/base-images.json +45 -0
  325. package/src/sca/container.js +107 -0
  326. package/src/sca/dep-confusion.js +134 -0
  327. package/src/sca/index.js +6 -0
  328. package/src/sca/popular-packages.json +41 -0
  329. package/src/sca/sarif-ingest.js +187 -0
  330. package/src/sca/vuln-function-hints.json +89 -0
  331. package/src/secrets/index.js +4 -0
@@ -0,0 +1 @@
1
+ a1c1494ded2872530fa2cf6558148e7bc5f680b69e56ad96435f859f2d8c3078
@@ -0,0 +1,364 @@
1
+ [
2
+ {
3
+ "timestamp": "2026-05-18T18:07:37.257Z",
4
+ "label": "scan",
5
+ "total": 0,
6
+ "critical": 0,
7
+ "high": 0,
8
+ "medium": 0,
9
+ "low": 0,
10
+ "kev": 0,
11
+ "ids": []
12
+ },
13
+ {
14
+ "timestamp": "2026-05-18T18:07:52.302Z",
15
+ "label": "scan",
16
+ "total": 0,
17
+ "critical": 0,
18
+ "high": 0,
19
+ "medium": 0,
20
+ "low": 0,
21
+ "kev": 0,
22
+ "ids": []
23
+ },
24
+ {
25
+ "timestamp": "2026-05-18T18:07:55.308Z",
26
+ "label": "scan",
27
+ "total": 0,
28
+ "critical": 0,
29
+ "high": 0,
30
+ "medium": 0,
31
+ "low": 0,
32
+ "kev": 0,
33
+ "ids": []
34
+ },
35
+ {
36
+ "timestamp": "2026-05-19T15:08:17.723Z",
37
+ "label": "scan",
38
+ "total": 0,
39
+ "critical": 0,
40
+ "high": 0,
41
+ "medium": 0,
42
+ "low": 0,
43
+ "kev": 0,
44
+ "ids": []
45
+ },
46
+ {
47
+ "timestamp": "2026-05-19T15:13:36.271Z",
48
+ "label": "scan",
49
+ "total": 0,
50
+ "critical": 0,
51
+ "high": 0,
52
+ "medium": 0,
53
+ "low": 0,
54
+ "kev": 0,
55
+ "ids": []
56
+ },
57
+ {
58
+ "timestamp": "2026-05-19T15:14:33.487Z",
59
+ "label": "scan",
60
+ "total": 0,
61
+ "critical": 0,
62
+ "high": 0,
63
+ "medium": 0,
64
+ "low": 0,
65
+ "kev": 0,
66
+ "ids": []
67
+ },
68
+ {
69
+ "timestamp": "2026-05-19T15:15:32.497Z",
70
+ "label": "scan",
71
+ "total": 0,
72
+ "critical": 0,
73
+ "high": 0,
74
+ "medium": 0,
75
+ "low": 0,
76
+ "kev": 0,
77
+ "ids": []
78
+ },
79
+ {
80
+ "timestamp": "2026-05-19T15:16:14.591Z",
81
+ "label": "scan",
82
+ "total": 0,
83
+ "critical": 0,
84
+ "high": 0,
85
+ "medium": 0,
86
+ "low": 0,
87
+ "kev": 0,
88
+ "ids": []
89
+ },
90
+ {
91
+ "timestamp": "2026-05-19T15:16:47.095Z",
92
+ "label": "scan",
93
+ "total": 0,
94
+ "critical": 0,
95
+ "high": 0,
96
+ "medium": 0,
97
+ "low": 0,
98
+ "kev": 0,
99
+ "ids": []
100
+ },
101
+ {
102
+ "timestamp": "2026-05-19T15:39:01.083Z",
103
+ "label": "scan",
104
+ "total": 0,
105
+ "critical": 0,
106
+ "high": 0,
107
+ "medium": 0,
108
+ "low": 0,
109
+ "kev": 0,
110
+ "ids": []
111
+ },
112
+ {
113
+ "timestamp": "2026-05-19T20:23:03.902Z",
114
+ "label": "scan",
115
+ "total": 0,
116
+ "critical": 0,
117
+ "high": 0,
118
+ "medium": 0,
119
+ "low": 0,
120
+ "kev": 0,
121
+ "ids": []
122
+ },
123
+ {
124
+ "timestamp": "2026-05-19T20:39:09.197Z",
125
+ "label": "scan",
126
+ "total": 0,
127
+ "critical": 0,
128
+ "high": 0,
129
+ "medium": 0,
130
+ "low": 0,
131
+ "kev": 0,
132
+ "ids": []
133
+ },
134
+ {
135
+ "timestamp": "2026-05-19T20:39:21.000Z",
136
+ "label": "scan",
137
+ "total": 0,
138
+ "critical": 0,
139
+ "high": 0,
140
+ "medium": 0,
141
+ "low": 0,
142
+ "kev": 0,
143
+ "ids": []
144
+ },
145
+ {
146
+ "timestamp": "2026-05-20T03:49:13.475Z",
147
+ "label": "scan",
148
+ "total": 0,
149
+ "critical": 0,
150
+ "high": 0,
151
+ "medium": 0,
152
+ "low": 0,
153
+ "kev": 0,
154
+ "ids": []
155
+ },
156
+ {
157
+ "timestamp": "2026-05-20T03:50:06.549Z",
158
+ "label": "scan",
159
+ "total": 1,
160
+ "critical": 0,
161
+ "high": 0,
162
+ "medium": 1,
163
+ "low": 0,
164
+ "kev": 0,
165
+ "ids": [
166
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
167
+ ]
168
+ },
169
+ {
170
+ "timestamp": "2026-05-20T03:50:48.754Z",
171
+ "label": "scan",
172
+ "total": 1,
173
+ "critical": 0,
174
+ "high": 0,
175
+ "medium": 1,
176
+ "low": 0,
177
+ "kev": 0,
178
+ "ids": [
179
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
180
+ ]
181
+ },
182
+ {
183
+ "timestamp": "2026-05-20T03:52:59.627Z",
184
+ "label": "scan",
185
+ "total": 1,
186
+ "critical": 0,
187
+ "high": 0,
188
+ "medium": 1,
189
+ "low": 0,
190
+ "kev": 0,
191
+ "ids": [
192
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
193
+ ]
194
+ },
195
+ {
196
+ "timestamp": "2026-05-20T12:32:01.638Z",
197
+ "label": "scan",
198
+ "total": 2,
199
+ "critical": 0,
200
+ "high": 1,
201
+ "medium": 1,
202
+ "low": 0,
203
+ "kev": 0,
204
+ "ids": [
205
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
206
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
207
+ ]
208
+ },
209
+ {
210
+ "timestamp": "2026-05-20T12:32:06.661Z",
211
+ "label": "scan",
212
+ "total": 2,
213
+ "critical": 0,
214
+ "high": 1,
215
+ "medium": 1,
216
+ "low": 0,
217
+ "kev": 0,
218
+ "ids": [
219
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
220
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
221
+ ]
222
+ },
223
+ {
224
+ "timestamp": "2026-05-20T12:32:15.761Z",
225
+ "label": "scan",
226
+ "total": 2,
227
+ "critical": 0,
228
+ "high": 1,
229
+ "medium": 1,
230
+ "low": 0,
231
+ "kev": 0,
232
+ "ids": [
233
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
234
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
235
+ ]
236
+ },
237
+ {
238
+ "timestamp": "2026-05-20T12:32:20.916Z",
239
+ "label": "scan",
240
+ "total": 2,
241
+ "critical": 0,
242
+ "high": 1,
243
+ "medium": 1,
244
+ "low": 0,
245
+ "kev": 0,
246
+ "ids": [
247
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
248
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
249
+ ]
250
+ },
251
+ {
252
+ "timestamp": "2026-05-20T12:32:26.044Z",
253
+ "label": "scan",
254
+ "total": 2,
255
+ "critical": 0,
256
+ "high": 1,
257
+ "medium": 1,
258
+ "low": 0,
259
+ "kev": 0,
260
+ "ids": [
261
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
262
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
263
+ ]
264
+ },
265
+ {
266
+ "timestamp": "2026-05-20T12:34:20.136Z",
267
+ "label": "scan",
268
+ "total": 3,
269
+ "critical": 0,
270
+ "high": 2,
271
+ "medium": 1,
272
+ "low": 0,
273
+ "kev": 0,
274
+ "ids": [
275
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
276
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
277
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
278
+ ]
279
+ },
280
+ {
281
+ "timestamp": "2026-05-20T12:34:25.000Z",
282
+ "label": "scan",
283
+ "total": 3,
284
+ "critical": 0,
285
+ "high": 2,
286
+ "medium": 1,
287
+ "low": 0,
288
+ "kev": 0,
289
+ "ids": [
290
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
291
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
292
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
293
+ ]
294
+ },
295
+ {
296
+ "timestamp": "2026-05-20T12:34:32.674Z",
297
+ "label": "scan",
298
+ "total": 3,
299
+ "critical": 0,
300
+ "high": 2,
301
+ "medium": 1,
302
+ "low": 0,
303
+ "kev": 0,
304
+ "ids": [
305
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
306
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
307
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
308
+ ]
309
+ },
310
+ {
311
+ "timestamp": "2026-05-20T12:34:40.056Z",
312
+ "label": "scan",
313
+ "total": 3,
314
+ "critical": 0,
315
+ "high": 2,
316
+ "medium": 1,
317
+ "low": 0,
318
+ "kev": 0,
319
+ "ids": [
320
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
321
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
322
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
323
+ ]
324
+ },
325
+ {
326
+ "timestamp": "2026-05-20T12:34:45.128Z",
327
+ "label": "scan",
328
+ "total": 3,
329
+ "critical": 0,
330
+ "high": 2,
331
+ "medium": 1,
332
+ "low": 0,
333
+ "kev": 0,
334
+ "ids": [
335
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
336
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
337
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)"
338
+ ]
339
+ },
340
+ {
341
+ "timestamp": "2026-05-20T17:01:27.551Z",
342
+ "label": "scan",
343
+ "total": 12,
344
+ "critical": 0,
345
+ "high": 2,
346
+ "medium": 10,
347
+ "low": 0,
348
+ "kev": 0,
349
+ "ids": [
350
+ "struct:parser-cs.js:208:Mass_Assignment_(req.body_Direct_to_Model)",
351
+ "struct:parser-kt.js:207:Mass_Assignment_(req.body_Direct_to_Model)",
352
+ "struct:parser-py-cst.js:91:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
353
+ "struct:type-stubs.js:190:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
354
+ "struct:type-stubs.js:198:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
355
+ "struct:type-stubs.js:216:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
356
+ "struct:type-stubs.js:245:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
357
+ "struct:type-stubs.js:48:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
358
+ "struct:type-stubs.js:57:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
359
+ "struct:type-stubs.js:58:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
360
+ "struct:type-stubs.js:79:Synchronous_Blocking_I/O_(DoS_Risk_in_Server_Context)",
361
+ "toctou-fs:type-stubs.js:48"
362
+ ]
363
+ }
364
+ ]
@@ -0,0 +1,23 @@
1
+ {
2
+ "firstScanDate": "2026-05-18T18:07:37.263Z",
3
+ "lastScanDate": "2026-05-20T17:01:27.572Z",
4
+ "totalScans": 28,
5
+ "daysCleanCritical": 3,
6
+ "lastCleanDate": "2026-05-20",
7
+ "lastCriticalDate": null,
8
+ "hasEverHadCritical": false,
9
+ "bestDaysCleanCritical": 3,
10
+ "totalFindingsAtFirstScan": 0,
11
+ "totalFindingsAtLastScan": 13,
12
+ "totalFixesInferred": 0,
13
+ "lastGrade": "A-",
14
+ "bestGrade": "A+",
15
+ "launchCheckPassedAt": null,
16
+ "achievements": [
17
+ "first-scan",
18
+ "grade-a",
19
+ "grade-a-plus",
20
+ "scan-veteran-25"
21
+ ],
22
+ "previousGrade": "A-"
23
+ }
@@ -0,0 +1,172 @@
1
+ # scanner/src/ir/
2
+
3
+ Layer-1 intermediate representation. Per-file IR + cross-file call graph;
4
+ consumed by `scanner/src/dataflow/` for taint analysis.
5
+
6
+ ## Parsers
7
+
8
+ | Language | Module | Backend |
9
+ |----------|-----------------------|--------------------------------------------------|
10
+ | JS / TS | `parser-js.js` | `@babel/parser` |
11
+ | Python | `parser-py-cst.js` | Python 3.8+ stdlib `ast` via subprocess (default when available) |
12
+ | Python | `parser-py.js` | Hand-rolled regex parser (fallback when python3 missing) |
13
+ | Java | `parser-java.js` | `java-parser` npm package (async) |
14
+
15
+ ## Python parser — dual-path with auto fallback
16
+
17
+ The Python pipeline has two implementations. The dispatcher in `index.js`
18
+ picks one at scan time:
19
+
20
+ ```
21
+ AGENTIC_SECURITY_PY_PARSER=auto (default) — try CST, fall back to regex
22
+ AGENTIC_SECURITY_PY_PARSER=cst force CST; error if python3 missing
23
+ AGENTIC_SECURITY_PY_PARSER=regex force regex
24
+ ```
25
+
26
+ ### Why two
27
+
28
+ The regex parser (`parser-py.js`) was the original v1. It's a hand-rolled
29
+ indentation walker with a balanced-paren expression matcher. It admits in
30
+ its own comments to dropping:
31
+
32
+ - list / dict / set / generator comprehensions
33
+ - decorators (parsed but body never lowered)
34
+ - `match` statements
35
+ - `async` / `await` (modeled as transparent unwrap)
36
+ - lambda bodies (collapsed to opaque)
37
+
38
+ Real-world Python is full of these. The taint engine silently no-ops on
39
+ every dropped function.
40
+
41
+ The CST parser (`parser-py-cst.js`) shells out to a small Python helper
42
+ script (`parser-py.helper.py`) that uses the stdlib `ast` module — zero
43
+ external dependencies, ships with every Python 3.8+ install. The helper
44
+ emits the same IR shape (`{functions[{qid,name,line,params,cfg,file}],
45
+ topLevel}`) as the regex parser. The CFG is built from the real AST, so:
46
+
47
+ - decorators don't drop the function record
48
+ - async def is recognized
49
+ - match statements don't drop the function (the body's `case` arms are a
50
+ noop placeholder for now — future work — but the function is captured)
51
+ - comprehensions surface their elt expression so taint propagates through
52
+ `[x for x in untrusted]`
53
+ - nested function defs become separate entries in `functions[]`
54
+ - `def f(x=Foo(1,2))` and `db.execute(sanitize(x))` parse correctly
55
+
56
+ ### Cost
57
+
58
+ - One `python3` subprocess **per `runScan`** (NOT per file). All Python
59
+ files in the project go in a single batched stdin payload, parsed in a
60
+ single linear loop on the Python side.
61
+ - Capability probe (`python3 --version`) runs ONCE per process and is
62
+ cached.
63
+ - When the helper crashes mid-batch, the dispatcher silently falls back
64
+ to the regex parser. Set `AGENTIC_SECURITY_PY_PARSER_DEBUG=1` to see
65
+ the failure on stderr.
66
+
67
+ ### When `auto` falls back to regex
68
+
69
+ - `python3` / `python` not on PATH
70
+ - Python version < 3.8
71
+ - helper script's stdin JSON corruption
72
+ - helper subprocess timeout (10 s for the whole batch — generous)
73
+ - helper output isn't parseable JSON
74
+
75
+ Each of these is a real failure mode; the regex fallback keeps the scan
76
+ producing findings instead of returning empty.
77
+
78
+ ### What CST still doesn't model
79
+
80
+ The helper builds a CFG only for shapes the dataflow engine actually
81
+ consumes. The following still emit `kind: 'noop'` nodes (future work):
82
+
83
+ - `match` case bodies (the function is captured; the per-case taint flow
84
+ isn't lowered)
85
+ - destructuring assignment (`a, b = req.body`) — only single-target
86
+ assignments get a precise `target` field
87
+ - comprehension generators (`for x in iter`) — the iter expression is
88
+ visible via the elt, but the generator's own `if` filters aren't yet
89
+ modeled
90
+ - walrus `:=` at expression position — the RHS flows forward, but the
91
+ named binding isn't tracked as a separate variable
92
+
93
+ These are deliberate to keep the CFG bounded; the regex parser's behavior
94
+ was strictly worse (it dropped the entire function).
95
+
96
+ ## IR shape contract
97
+
98
+ Every parser must produce this shape:
99
+
100
+ ```js
101
+ {
102
+ file: 'rel/path.py',
103
+ functions: [
104
+ {
105
+ qid: 'file.py::name@line#sha', // stable cross-file identifier
106
+ name: 'function_name',
107
+ line: 42,
108
+ params: ['arg1', 'arg2', ...],
109
+ file: 'file.py',
110
+ cfg: {
111
+ entry: 'nodeId',
112
+ exit: 'nodeId',
113
+ nodes: {
114
+ [nodeId]: {
115
+ kind: 'entry'|'exit'|'noop'|'loop-header'|'assign'|'call'
116
+ |'if'|'return'|'throw'|'unknown',
117
+ line: number,
118
+ succ: [nodeId, ...],
119
+ pred: [nodeId, ...],
120
+ // kind-specific:
121
+ // assign: target (string or null), source (expr)
122
+ // call: callee (string or expr), args ([expr])
123
+ // if: cond (expr)
124
+ // return: value (expr or null)
125
+ }
126
+ }
127
+ }
128
+ }
129
+ ],
130
+ topLevel: null, // reserved for module-level code; not yet used
131
+ }
132
+ ```
133
+
134
+ Expression shape (returned by `source`, `cond`, `args[i]`, etc.):
135
+
136
+ ```js
137
+ { kind: 'literal', value: any }
138
+ { kind: 'ident', name: 'x' }
139
+ { kind: 'member', object: expr, prop: 'attr' }
140
+ { kind: 'binary', op: '+', left: expr, right: expr }
141
+ { kind: 'logical', op: 'and'|'or', left: expr, right: expr }
142
+ { kind: 'tpl', parts: [expr, ...] }
143
+ { kind: 'call', callee: string|expr, args: [expr, ...] }
144
+ { kind: 'array', elements: [expr, ...] }
145
+ { kind: 'object', props: [{value: expr}, ...] }
146
+ { kind: 'union', branches: [expr, expr] }
147
+ { kind: 'unknown' }
148
+ ```
149
+
150
+ Any change to this shape must update **all** parsers AND the dataflow
151
+ engine — `scanner/src/dataflow/engine.js` is the contract reader.
152
+
153
+ ## Adding a Python construct
154
+
155
+ 1. Add a recognizer + lowerer branch in `parser-py.helper.py`'s
156
+ `_lower_stmt` or `_lower_expr` function.
157
+ 2. Add a fixture-style test in `scanner/test/parser-py-cst.test.js`.
158
+ 3. Run `npm run test:dataflow` to confirm no IR-consumer regressions.
159
+ 4. Optionally: add a corresponding case to `parser-py.js` so the regex
160
+ fallback handles the same shape (but don't block on this — the regex
161
+ parser is a fallback, not a target).
162
+
163
+ ## When to retire the regex parser
164
+
165
+ The regex parser stays as long as some customers run scans on machines
166
+ without Python 3.8+. Realistic CI / dev environments have Python; locked-
167
+ down enterprise runners sometimes don't. Targets for retirement:
168
+
169
+ - Two minor releases with zero `parser-py-cst → regex` fallbacks reported
170
+ via the optional telemetry surface.
171
+ - Or, the `AGENTIC_SECURITY_PY_PARSER` env defaults to `cst` (strict
172
+ mode) for one release with no customer complaint tickets filed.
@@ -0,0 +1,73 @@
1
+ // Cross-file call graph from the JS/TS IR.
2
+ //
3
+ // Resolves call sites to callee function IDs. Resolution rules (best-effort):
4
+ // 1. Direct function call to a name defined in the same file → resolve to that fn.
5
+ // 2. Method call `obj.foo()` where obj is a class instance with a known
6
+ // method `foo` in the same file → resolve to that method.
7
+ // 3. Module-imported name: look up in another file's exports.
8
+ // 4. Anything else → unresolved; the dataflow engine treats the callee as
9
+ // an opaque sink for taint.
10
+
11
+ export function buildCallGraph(perFileIR) {
12
+ // perFileIR is { [file]: parseJsFile output }
13
+ const functions = new Map(); // qid → FunctionIR
14
+ const byNameInFile = new Map(); // file → Map<name, qid>
15
+ const classMethods = new Map(); // 'ClassName.method' → qid
16
+
17
+ for (const file of Object.keys(perFileIR || {})) {
18
+ const ir = perFileIR[file];
19
+ if (!ir || !ir.functions) continue;
20
+ byNameInFile.set(file, new Map());
21
+ for (const fn of ir.functions) {
22
+ functions.set(fn.qid, fn);
23
+ byNameInFile.get(file).set(fn.name, fn.qid);
24
+ // Class methods: qid carries the class name as the scope.
25
+ const m = fn.qid.match(/::([A-Z]\w*)::(\w+)@/);
26
+ if (m) classMethods.set(`${m[1]}.${m[2]}`, fn.qid);
27
+ }
28
+ }
29
+
30
+ // Resolve each call site.
31
+ const edges = []; // { caller, site, callee, ambiguous? }
32
+ for (const fn of functions.values()) {
33
+ for (const c of (fn.calls || [])) {
34
+ if (!c.callee) { edges.push({ caller: fn.qid, site: c.site, callee: null, line: c.line }); continue; }
35
+ // 1. Direct name in same file
36
+ const sameFileMap = byNameInFile.get(fn.file);
37
+ const resolved = sameFileMap?.get(c.callee) ||
38
+ classMethods.get(c.callee) ||
39
+ // 2. ClassName.method form
40
+ (c.callee.includes('.') ? classMethods.get(c.callee) : null) ||
41
+ null;
42
+ edges.push({ caller: fn.qid, site: c.site, callee: resolved, calleeName: c.callee, line: c.line });
43
+ }
44
+ }
45
+
46
+ // Reverse index: callee → callers.
47
+ const callersOf = new Map();
48
+ for (const e of edges) {
49
+ if (!e.callee) continue;
50
+ if (!callersOf.has(e.callee)) callersOf.set(e.callee, []);
51
+ callersOf.get(e.callee).push(e);
52
+ }
53
+ // Premortem #7: expose a name→qid resolver so the taint engine can ask
54
+ // the call graph for the callee's qid at the assign-from-call site.
55
+ // Same precedence as the edge resolution above (same-file ident wins,
56
+ // ClassName.method falls back).
57
+ function resolve(name) {
58
+ if (!name || typeof name !== 'string') return null;
59
+ // Direct ident match — search every file's same-file map.
60
+ for (const m of byNameInFile.values()) {
61
+ if (m.has(name)) return m.get(name);
62
+ }
63
+ if (classMethods.has(name)) return classMethods.get(name);
64
+ if (name.includes('.')) {
65
+ const tail = name.split('.').slice(-1)[0];
66
+ for (const m of byNameInFile.values()) {
67
+ if (m.has(tail)) return m.get(tail);
68
+ }
69
+ }
70
+ return null;
71
+ }
72
+ return { functions, edges, callersOf, resolve };
73
+ }