@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,198 @@
1
+ // Laravel framework hardening.
2
+ //
3
+ // Extends scanner/src/sast/php.js with Laravel-specific patterns. Targets
4
+ // `.env`, `config/*.php`, controllers, models, and routes.
5
+ //
6
+ // Coverage:
7
+ // 1. APP_DEBUG=true in .env (production-shaped)
8
+ // 2. APP_KEY= empty
9
+ // 3. dd() / dump() / var_dump() left in controller / model / view
10
+ // 4. Eloquent model without $fillable / $guarded (mass assignment)
11
+ // 5. SESSION_SECURE_COOKIE=false
12
+ // 6. CSRF disabled via VerifyCsrfToken::$except wildcard
13
+ // 7. DB::raw with $request input concatenated
14
+ // 8. Hash::needsRehash usage without follow-up Hash::make
15
+
16
+ const _ENV_FILE_RE = /(?:^|[\\/])\.env(?:\.[\w-]+)?$/;
17
+ const _PHP_FILE_RE = /\.php$/i;
18
+ const _MODEL_FILE_RE = /(?:^|[\\/])(?:app[\\/])?Models[\\/]/;
19
+ const _ROUTES_FILE_RE = /(?:^|[\\/])routes[\\/]/;
20
+
21
+ function _line(raw, idx) {
22
+ return raw.slice(0, idx).split('\n').length;
23
+ }
24
+
25
+ // Heuristic for "this PHP file is an application (not the framework / vendor)".
26
+ // The framework itself defines dd/dump as helpers; flagging calls inside the
27
+ // framework source is pure false-positive.
28
+ function _isApplicationPhpFile(file, raw) {
29
+ if (/(?:^|[\\/])(?:vendor|node_modules)[\\/]/.test(file)) return false;
30
+ if (/(?:^|[\\/])src[\\/]Illuminate[\\/]/.test(file)) return false;
31
+ if (/(?:^|[\\/])framework[\\/]src[\\/]/.test(file)) return false;
32
+ if (/\bnamespace\s+Illuminate\\/.test(raw)) return false;
33
+ // Must look like a Laravel application file.
34
+ return /\bnamespace\s+App\\\w+/.test(raw) ||
35
+ /\b(?:use|extends|implements)\s+(?:App|Illuminate)\\/.test(raw);
36
+ }
37
+
38
+ export function scanLaravelHardening(file, raw) {
39
+ if (!file || !raw || typeof raw !== 'string') return [];
40
+ if (raw.length > 200_000) return [];
41
+
42
+ const findings = [];
43
+
44
+ // ── .env file checks ────────────────────────────────────────────────────
45
+ if (_ENV_FILE_RE.test(file)) {
46
+ // APP_DEBUG=true
47
+ for (const m of raw.matchAll(/^\s*APP_DEBUG\s*=\s*true\b/gmi)) {
48
+ findings.push({
49
+ id: `laravel:app-debug-true:${file}:${_line(raw, m.index)}`,
50
+ file, line: _line(raw, m.index),
51
+ vuln: 'Laravel APP_DEBUG=true in .env',
52
+ severity: /\.env\.example$|\.env\.dev$|\.env\.local$/.test(file) ? 'medium' : 'critical',
53
+ family: 'laravel-debug-enabled',
54
+ cwe: 'CWE-489',
55
+ confidence: 0.95,
56
+ description: 'APP_DEBUG=true exposes the Whoops/Ignition error page on any exception — full stack trace, environment, query log, request payload. Catastrophic in production.',
57
+ remediation: 'Set APP_DEBUG=false in every production .env. Use APP_ENV=local + APP_DEBUG=true only locally.',
58
+ });
59
+ }
60
+ // APP_KEY empty
61
+ for (const m of raw.matchAll(/^\s*APP_KEY\s*=\s*$/gm)) {
62
+ findings.push({
63
+ id: `laravel:app-key-empty:${file}:${_line(raw, m.index)}`,
64
+ file, line: _line(raw, m.index),
65
+ vuln: 'Laravel APP_KEY is empty',
66
+ severity: 'critical',
67
+ family: 'laravel-no-app-key',
68
+ cwe: 'CWE-321',
69
+ confidence: 0.95,
70
+ description: 'APP_KEY is the encryption key for cookies / sessions / Crypt::encrypt. An empty key disables Laravel\'s encryption entirely and makes signed URLs / Laravel Sanctum / encrypted casts insecure.',
71
+ remediation: 'Run `php artisan key:generate` and commit the result to your secrets store (NOT to the .env in source). Rotate immediately if the key was ever empty in production.',
72
+ });
73
+ }
74
+ // SESSION_SECURE_COOKIE=false
75
+ for (const m of raw.matchAll(/^\s*SESSION_SECURE_COOKIE\s*=\s*false\b/gmi)) {
76
+ findings.push({
77
+ id: `laravel:session-not-secure:${file}:${_line(raw, m.index)}`,
78
+ file, line: _line(raw, m.index),
79
+ vuln: 'Laravel SESSION_SECURE_COOKIE=false',
80
+ severity: /\.env\.example$|\.env\.dev$/.test(file) ? 'medium' : 'high',
81
+ family: 'laravel-cookie-not-secure',
82
+ cwe: 'CWE-614',
83
+ confidence: 0.9,
84
+ description: 'Session cookie is not marked Secure — sent over plain HTTP. Any network observer steals the session.',
85
+ remediation: 'Set SESSION_SECURE_COOKIE=true (and SESSION_HTTP_ONLY=true, SESSION_SAME_SITE=lax).',
86
+ });
87
+ }
88
+ return findings;
89
+ }
90
+
91
+ // ── PHP source checks ───────────────────────────────────────────────────
92
+ if (!_PHP_FILE_RE.test(file)) return [];
93
+ if (!_isApplicationPhpFile(file, raw)) return [];
94
+
95
+ // 1. dd() / dump() / var_dump() left in code — only as standalone calls,
96
+ // not as method declarations (`function dump(...)`) or method references
97
+ // (`->dump`, `::dump`).
98
+ for (const m of raw.matchAll(/(?<!function\s)(?<!->)(?<!::)\b(?:dd|dump|var_dump)\s*\(/g)) {
99
+ findings.push({
100
+ id: `laravel:debug-dump:${file}:${_line(raw, m.index)}`,
101
+ file, line: _line(raw, m.index),
102
+ vuln: 'Laravel dd() / dump() / var_dump() left in source',
103
+ severity: 'medium',
104
+ family: 'laravel-debug-leak',
105
+ cwe: 'CWE-209',
106
+ confidence: 0.8,
107
+ description: 'dd() halts execution and dumps every variable in scope (often including request / database / config). If reached in production it leaks PII, DB connection strings, and API keys.',
108
+ remediation: 'Remove the dd()/dump()/var_dump() call. Use \\Log::debug() / \\Log::info() with explicit fields if you need ongoing diagnostics.',
109
+ });
110
+ }
111
+
112
+ // 2. Eloquent model without $fillable AND without $guarded
113
+ if (_MODEL_FILE_RE.test(file) && /\bextends\s+Model\b/.test(raw) && /\bclass\s+(\w+)\s+extends\s+Model\b/.test(raw)) {
114
+ if (!/\$fillable\s*=/.test(raw) && !/\$guarded\s*=/.test(raw)) {
115
+ const m = /\bclass\s+\w+\s+extends\s+Model\b/.exec(raw);
116
+ findings.push({
117
+ id: `laravel:mass-assignment:${file}:${_line(raw, m.index)}`,
118
+ file, line: _line(raw, m.index),
119
+ vuln: 'Eloquent model has no $fillable or $guarded — mass assignment risk',
120
+ severity: 'high',
121
+ family: 'laravel-mass-assignment',
122
+ cwe: 'CWE-915',
123
+ confidence: 0.85,
124
+ description: 'Without $fillable (allow-list) or $guarded (deny-list), Model::create($request->all()) accepts every field — including ones the user shouldn\'t set (is_admin, role_id, balance).',
125
+ remediation: 'Add protected $fillable = [\'name\', \'email\', ...]; with the explicit allow-list. Or protected $guarded = [\'id\', \'is_admin\', ...]; with the deny-list. Never set $guarded = [].',
126
+ });
127
+ }
128
+ }
129
+
130
+ // 3. CSRF exception wildcard in VerifyCsrfToken
131
+ for (const m of raw.matchAll(/protected\s+\$except\s*=\s*\[\s*['"]\*['"]/g)) {
132
+ findings.push({
133
+ id: `laravel:csrf-wildcard-except:${file}:${_line(raw, m.index)}`,
134
+ file, line: _line(raw, m.index),
135
+ vuln: 'VerifyCsrfToken::$except = [\'*\'] — CSRF disabled globally',
136
+ severity: 'critical',
137
+ family: 'laravel-csrf-disabled',
138
+ cwe: 'CWE-352',
139
+ confidence: 0.95,
140
+ description: 'Wildcard exception in VerifyCsrfToken disables CSRF protection on every POST endpoint. Cross-origin state-changing requests succeed.',
141
+ remediation: 'Restrict $except to specific webhook routes (e.g., ["stripe/webhook", "api/svix/*"]). For SPA APIs, use Sanctum stateful auth which provides CSRF via XSRF-TOKEN cookie.',
142
+ });
143
+ }
144
+
145
+ // 4. DB::raw with concatenated request input
146
+ for (const m of raw.matchAll(/DB::raw\s*\(\s*['"][^'"]*['"]\s*\.\s*\$(?:request|input)/g)) {
147
+ findings.push({
148
+ id: `laravel:db-raw-injection:${file}:${_line(raw, m.index)}`,
149
+ file, line: _line(raw, m.index),
150
+ vuln: 'Laravel DB::raw(...) with concatenated request input — SQL injection',
151
+ severity: 'critical',
152
+ family: 'laravel-sql-injection',
153
+ cwe: 'CWE-89',
154
+ confidence: 0.95,
155
+ description: 'DB::raw() bypasses query bindings. Concatenating user input into the SQL string lets the attacker rewrite the query.',
156
+ remediation: 'Use bindings: DB::select("SELECT * FROM users WHERE email = ?", [$request->email]). Or use the Eloquent / Query Builder where() chain.',
157
+ });
158
+ }
159
+
160
+ // 5. App\Console\Kernel::commands missing schedule() restrictions — skip (out of scope here).
161
+
162
+ // 6. Plain password comparison via ==
163
+ for (const m of raw.matchAll(/\$(?:user|model)->password\s*==\s*\$(?:request|input)\b/g)) {
164
+ findings.push({
165
+ id: `laravel:plain-password-compare:${file}:${_line(raw, m.index)}`,
166
+ file, line: _line(raw, m.index),
167
+ vuln: 'Plaintext password comparison ($user->password == $request->password)',
168
+ severity: 'critical',
169
+ family: 'laravel-plain-password',
170
+ cwe: 'CWE-916',
171
+ confidence: 0.85,
172
+ description: 'Comparing the user\'s password column to the request value with == implies the password is stored plaintext (or the comparison is wrong against a bcrypt hash).',
173
+ remediation: 'Use Hash::check($request->password, $user->password). Store passwords with Hash::make() — never plaintext.',
174
+ });
175
+ }
176
+
177
+ // 7. Route::middleware('web')->... missing auth on admin
178
+ if (_ROUTES_FILE_RE.test(file)) {
179
+ for (const m of raw.matchAll(/Route::(?:get|post|put|patch|delete)\s*\(\s*['"](\/admin[^'"]*)['"][^;]*?(?:->name\s*\([^)]*\))?[^;]*?;/g)) {
180
+ const block = m[0];
181
+ if (!/middleware\s*\([^)]*['"](?:auth|auth:sanctum|auth:web|admin)['"]/.test(block)) {
182
+ findings.push({
183
+ id: `laravel:admin-route-no-auth:${file}:${_line(raw, m.index)}`,
184
+ file, line: _line(raw, m.index),
185
+ vuln: `Admin route ${m[1]} has no auth middleware`,
186
+ severity: 'high',
187
+ family: 'laravel-missing-auth',
188
+ cwe: 'CWE-862',
189
+ confidence: 0.75,
190
+ description: 'A route under /admin/* is declared without ->middleware(\'auth\') (or auth:sanctum, etc.). Anyone reaching the URL bypasses authentication.',
191
+ remediation: 'Wrap admin routes in Route::middleware([\'auth\', \'can:admin\'])->prefix(\'admin\')->group(function () { ... }).',
192
+ });
193
+ }
194
+ }
195
+ }
196
+
197
+ return findings;
198
+ }
@@ -0,0 +1,100 @@
1
+ import { blankComments } from './_comment-strip.js';
2
+ // LDAP injection (CWE-90).
3
+ //
4
+ // LDAP filters use a parens/operator syntax; concatenating user input into a
5
+ // filter lets a client smuggle additional `|(|(`-style clauses that return
6
+ // records they shouldn't see, or auth-bypass via `(uid=*)(uid=admin*)`.
7
+ //
8
+ // We catch:
9
+ // - Node ldapjs: client.search(base, { filter: "(uid=" + name + ")" })
10
+ // - Java JNDI: ctx.search(base, "(cn=" + name + ")", ...)
11
+ // - Java w/ var: String filter = "(uid=" + name + ")"; ctx.search(base, filter);
12
+ // - Python ldap3: conn.search(base, "(uid=" + name + ")")
13
+ // - Python python-ldap: conn.search_s(base, scope, "(uid=" + name + ")")
14
+ // - Python f-strings: conn.search_s(base, scope, f"(uid={name})")
15
+ //
16
+ // We require an LDAP context hint in the file (DirContext, javax.naming,
17
+ // ldap.initialize, ldapjs, etc.) so we don't fire on every `"foo=" + bar`
18
+ // concatenation in unrelated code.
19
+
20
+ const FILTER_INLINE_RE = {
21
+ // Filter string concatenated INSIDE the .search call.
22
+ js: /\bfilter\s*:\s*[`"']?\([^`"')]*\b(?:uid|cn|mail|sAMAccountName|givenName|sn|memberOf)\s*=\s*[`"']?\s*(?:\+|\$\{)/g,
23
+ java: /\.search(?:_s)?\s*\(\s*[^,]+,\s*"[^"]*\b(?:uid|cn|mail|sAMAccountName|givenName|sn|memberOf)\s*=[^"]*"\s*\+\s*\w+/g,
24
+ py: /\.(?:search|search_s|search_ext|paged_search)\s*\([^)]*\b(?:uid|cn|mail|sAMAccountName|givenName|sn|memberOf)\s*=[^)]*['"]?\s*\+\s*\w+/g,
25
+ };
26
+
27
+ // Filter built in a variable then passed to .search. The signal is the
28
+ // "(<attr>= + " pattern anywhere in the file.
29
+ const FILTER_VAR_RE =
30
+ /["'`]\s*\(\s*(?:uid|cn|mail|sAMAccountName|givenName|sn|memberOf)\s*=\s*["'`]?\s*\+\s*[A-Za-z_][\w.]*|f["']\s*\(\s*(?:uid|cn|mail|sAMAccountName|givenName|sn|memberOf)\s*=\s*\{/g;
31
+
32
+ // LDAP context hint: at least one of these must be in the file before we
33
+ // trust the variable-form heuristic.
34
+ const LDAP_HINT_RE =
35
+ /\b(?:DirContext|javax\.naming|ldap\.initialize|ldap3|ldapjs|LdapContext|InitialDirContext|SearchResult|conn\.search|client\.search|\.search_s|getLdapTemplate)\b/;
36
+
37
+ function lineOf(raw, idx) { return raw.substring(0, idx).split('\n').length; }
38
+ function _lang(fp) {
39
+ if (/\.(?:js|jsx|ts|tsx|mjs|cjs)$/i.test(fp)) return 'js';
40
+ if (/\.java$/i.test(fp)) return 'java';
41
+ if (/\.py$/i.test(fp)) return 'py';
42
+ return null;
43
+ }
44
+
45
+ function _emit(fp, raw, line, why) {
46
+ return {
47
+ id: `ldap-injection:${fp}:${line}:${why}`,
48
+ file: fp, line,
49
+ vuln: 'LDAP Injection: filter string built via concatenation',
50
+ severity: 'high',
51
+ cwe: 'CWE-90',
52
+ family: 'ldap-injection',
53
+ stride: 'Tampering',
54
+ snippet: (raw.split('\n')[line - 1] || '').trim().slice(0, 200),
55
+ remediation: 'Escape LDAP filter metacharacters (`*`, `(`, `)`, `\\`, NUL) before substitution, or use a parameterized API. ' +
56
+ 'Node ldapjs: `new EqualityFilter({ attribute: "uid", value: name })`. ' +
57
+ 'Java JNDI: bind via search filter args — `ctx.search(base, "(uid={0})", new Object[]{ name }, controls)`. ' +
58
+ 'Python python-ldap: `ldap.filter.escape_filter_chars(name)`. ' +
59
+ 'Python ldap3: pass the value through a Connection bind-format helper.',
60
+ parser: 'LDAP-INJECTION',
61
+ confidence: 0.85,
62
+ };
63
+ }
64
+
65
+ export function scanLDAPInjection(fp, raw) {
66
+ if (!raw || raw.length > 500_000) return [];
67
+ const lang = _lang(fp);
68
+ if (!lang) return [];
69
+ const code = blankComments(raw, lang === 'py' ? 'py' : undefined);
70
+ const findings = [];
71
+ const seen = new Set();
72
+ // Path A — concatenation inside the .search call. High-confidence,
73
+ // doesn't need the context hint.
74
+ {
75
+ const re = new RegExp(FILTER_INLINE_RE[lang].source, FILTER_INLINE_RE[lang].flags);
76
+ let m;
77
+ while ((m = re.exec(code))) {
78
+ const line = lineOf(raw, m.index);
79
+ const key = `inline:${line}`;
80
+ if (seen.has(key)) continue;
81
+ seen.add(key);
82
+ findings.push(_emit(fp, raw, line, 'inline'));
83
+ }
84
+ }
85
+ // Path B — filter built into a variable then passed downstream. Lower-
86
+ // confidence so we gate on a file-level LDAP hint to suppress unrelated
87
+ // string concatenations.
88
+ if (LDAP_HINT_RE.test(code)) {
89
+ const re = new RegExp(FILTER_VAR_RE.source, FILTER_VAR_RE.flags);
90
+ let m;
91
+ while ((m = re.exec(code))) {
92
+ const line = lineOf(raw, m.index);
93
+ const key = `var:${line}`;
94
+ if (seen.has(key)) continue;
95
+ seen.add(key);
96
+ findings.push(_emit(fp, raw, line, 'var'));
97
+ }
98
+ }
99
+ return findings;
100
+ }