agentic-qe 3.3.3 → 3.3.5

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 (553) hide show
  1. package/.claude/agents/v3/README.md +100 -0
  2. package/.claude/agents/v3/qe-accessibility-auditor.md +112 -11
  3. package/.claude/agents/v3/qe-bdd-generator.md +40 -0
  4. package/.claude/agents/v3/qe-coverage-specialist.md +39 -0
  5. package/.claude/agents/v3/qe-defect-predictor.md +36 -0
  6. package/.claude/agents/v3/qe-gap-detector.md +39 -0
  7. package/.claude/agents/v3/qe-pattern-learner.md +31 -0
  8. package/.claude/agents/v3/qe-product-factors-assessor.md +34 -0
  9. package/.claude/agents/v3/qe-quality-criteria-recommender.md +32 -0
  10. package/.claude/agents/v3/qe-quality-gate.md +39 -0
  11. package/.claude/agents/v3/qe-queen-coordinator.md +332 -166
  12. package/.claude/agents/v3/qe-requirements-validator.md +37 -0
  13. package/.claude/agents/v3/qe-risk-assessor.md +33 -0
  14. package/.claude/agents/v3/qe-tdd-specialist.md +33 -0
  15. package/.claude/agents/v3/qe-test-architect.md +36 -0
  16. package/.claude/helpers/statusline-v3.cjs +38 -42
  17. package/.claude/skills/README.md +30 -104
  18. package/.claude/skills/a11y-ally/SKILL.md +1658 -0
  19. package/.claude/skills/qcsd-ideation-swarm/SKILL.md +1750 -0
  20. package/.claude/skills/skills-manifest.json +78 -8
  21. package/README.md +18 -4
  22. package/package.json +1 -1
  23. package/scripts/cloud-db-config.json +10 -5
  24. package/scripts/demo-warmup.sh +45 -0
  25. package/scripts/fetch-content.js +460 -0
  26. package/scripts/merge-v3-to-root.sql +48 -0
  27. package/v3/CHANGELOG.md +166 -0
  28. package/v3/README.md +11 -6
  29. package/v3/assets/agents/v3/README.md +100 -0
  30. package/v3/assets/agents/v3/qe-accessibility-auditor.md +112 -11
  31. package/v3/assets/agents/v3/qe-bdd-generator.md +40 -0
  32. package/v3/assets/agents/v3/qe-coverage-specialist.md +39 -0
  33. package/v3/assets/agents/v3/qe-defect-predictor.md +36 -0
  34. package/v3/assets/agents/v3/qe-gap-detector.md +39 -0
  35. package/v3/assets/agents/v3/qe-pattern-learner.md +31 -0
  36. package/v3/assets/agents/v3/qe-product-factors-assessor.md +34 -0
  37. package/v3/assets/agents/v3/qe-quality-criteria-recommender.md +32 -0
  38. package/v3/assets/agents/v3/qe-quality-gate.md +39 -0
  39. package/v3/assets/agents/v3/qe-queen-coordinator.md +332 -166
  40. package/v3/assets/agents/v3/qe-requirements-validator.md +37 -0
  41. package/v3/assets/agents/v3/qe-risk-assessor.md +33 -0
  42. package/v3/assets/agents/v3/qe-tdd-specialist.md +33 -0
  43. package/v3/assets/agents/v3/qe-test-architect.md +36 -0
  44. package/v3/assets/hooks/cross-phase-memory.yaml +253 -0
  45. package/v3/assets/skills/a11y-ally/SKILL.md +1658 -0
  46. package/v3/assets/skills/qcsd-ideation-swarm/SKILL.md +1750 -0
  47. package/v3/assets/skills/skills-manifest.json +753 -0
  48. package/v3/dist/adapters/claude-flow/model-router-bridge.d.ts.map +1 -1
  49. package/v3/dist/adapters/claude-flow/model-router-bridge.js +6 -4
  50. package/v3/dist/adapters/claude-flow/model-router-bridge.js.map +1 -1
  51. package/v3/dist/adapters/claude-flow/pretrain-bridge.d.ts.map +1 -1
  52. package/v3/dist/adapters/claude-flow/pretrain-bridge.js +13 -8
  53. package/v3/dist/adapters/claude-flow/pretrain-bridge.js.map +1 -1
  54. package/v3/dist/adapters/claude-flow/trajectory-bridge.d.ts.map +1 -1
  55. package/v3/dist/adapters/claude-flow/trajectory-bridge.js +9 -6
  56. package/v3/dist/adapters/claude-flow/trajectory-bridge.js.map +1 -1
  57. package/v3/dist/benchmarks/performance-benchmarks.d.ts.map +1 -1
  58. package/v3/dist/benchmarks/performance-benchmarks.js +5 -3
  59. package/v3/dist/benchmarks/performance-benchmarks.js.map +1 -1
  60. package/v3/dist/cli/bundle.js +27646 -23204
  61. package/v3/dist/cli/commands/hooks.d.ts.map +1 -1
  62. package/v3/dist/cli/commands/hooks.js +288 -0
  63. package/v3/dist/cli/commands/hooks.js.map +1 -1
  64. package/v3/dist/cli/commands/sync.d.ts.map +1 -1
  65. package/v3/dist/cli/commands/sync.js +0 -6
  66. package/v3/dist/cli/commands/sync.js.map +1 -1
  67. package/v3/dist/cli/handlers/init-handler.d.ts.map +1 -1
  68. package/v3/dist/cli/handlers/init-handler.js +11 -0
  69. package/v3/dist/cli/handlers/init-handler.js.map +1 -1
  70. package/v3/dist/cli/index.js +14 -2
  71. package/v3/dist/cli/index.js.map +1 -1
  72. package/v3/dist/cli/scheduler/persistent-scheduler.d.ts.map +1 -1
  73. package/v3/dist/cli/scheduler/persistent-scheduler.js +3 -2
  74. package/v3/dist/cli/scheduler/persistent-scheduler.js.map +1 -1
  75. package/v3/dist/cli/wizards/test-wizard.d.ts.map +1 -1
  76. package/v3/dist/cli/wizards/test-wizard.js +6 -4
  77. package/v3/dist/cli/wizards/test-wizard.js.map +1 -1
  78. package/v3/dist/coordination/consensus/providers/claude-provider.js +1 -1
  79. package/v3/dist/coordination/consensus/providers/gemini-provider.js +1 -1
  80. package/v3/dist/coordination/consensus/providers/native-learning-provider.d.ts.map +1 -1
  81. package/v3/dist/coordination/consensus/providers/native-learning-provider.js +10 -8
  82. package/v3/dist/coordination/consensus/providers/native-learning-provider.js.map +1 -1
  83. package/v3/dist/coordination/consensus/providers/ollama-provider.d.ts.map +1 -1
  84. package/v3/dist/coordination/consensus/providers/ollama-provider.js +5 -4
  85. package/v3/dist/coordination/consensus/providers/ollama-provider.js.map +1 -1
  86. package/v3/dist/coordination/consensus/providers/openai-provider.d.ts.map +1 -1
  87. package/v3/dist/coordination/consensus/providers/openai-provider.js +5 -4
  88. package/v3/dist/coordination/consensus/providers/openai-provider.js.map +1 -1
  89. package/v3/dist/coordination/constants.d.ts +198 -0
  90. package/v3/dist/coordination/constants.d.ts.map +1 -0
  91. package/v3/dist/coordination/constants.js +210 -0
  92. package/v3/dist/coordination/constants.js.map +1 -0
  93. package/v3/dist/coordination/mincut/dream-integration.d.ts.map +1 -1
  94. package/v3/dist/coordination/mincut/dream-integration.js +5 -1
  95. package/v3/dist/coordination/mincut/dream-integration.js.map +1 -1
  96. package/v3/dist/coordination/queen-coordinator.d.ts +9 -1
  97. package/v3/dist/coordination/queen-coordinator.d.ts.map +1 -1
  98. package/v3/dist/coordination/queen-coordinator.js +49 -9
  99. package/v3/dist/coordination/queen-coordinator.js.map +1 -1
  100. package/v3/dist/coordination/task-executor.d.ts.map +1 -1
  101. package/v3/dist/coordination/task-executor.js +7 -8
  102. package/v3/dist/coordination/task-executor.js.map +1 -1
  103. package/v3/dist/coordination/workflow-orchestrator.d.ts.map +1 -1
  104. package/v3/dist/coordination/workflow-orchestrator.js +261 -0
  105. package/v3/dist/coordination/workflow-orchestrator.js.map +1 -1
  106. package/v3/dist/domains/chaos-resilience/plugin.d.ts +14 -3
  107. package/v3/dist/domains/chaos-resilience/plugin.d.ts.map +1 -1
  108. package/v3/dist/domains/chaos-resilience/plugin.js +96 -0
  109. package/v3/dist/domains/chaos-resilience/plugin.js.map +1 -1
  110. package/v3/dist/domains/chaos-resilience/services/performance-profiler.d.ts.map +1 -1
  111. package/v3/dist/domains/chaos-resilience/services/performance-profiler.js +12 -8
  112. package/v3/dist/domains/chaos-resilience/services/performance-profiler.js.map +1 -1
  113. package/v3/dist/domains/code-intelligence/plugin.d.ts +13 -3
  114. package/v3/dist/domains/code-intelligence/plugin.d.ts.map +1 -1
  115. package/v3/dist/domains/code-intelligence/plugin.js +85 -0
  116. package/v3/dist/domains/code-intelligence/plugin.js.map +1 -1
  117. package/v3/dist/domains/code-intelligence/services/product-factors-bridge.d.ts.map +1 -1
  118. package/v3/dist/domains/code-intelligence/services/product-factors-bridge.js +3 -2
  119. package/v3/dist/domains/code-intelligence/services/product-factors-bridge.js.map +1 -1
  120. package/v3/dist/domains/constants.d.ts +481 -0
  121. package/v3/dist/domains/constants.d.ts.map +1 -0
  122. package/v3/dist/domains/constants.js +503 -0
  123. package/v3/dist/domains/constants.js.map +1 -0
  124. package/v3/dist/domains/contract-testing/plugin.d.ts +6 -1
  125. package/v3/dist/domains/contract-testing/plugin.d.ts.map +1 -1
  126. package/v3/dist/domains/contract-testing/plugin.js +80 -1
  127. package/v3/dist/domains/contract-testing/plugin.js.map +1 -1
  128. package/v3/dist/domains/contract-testing/services/contract-validator.d.ts.map +1 -1
  129. package/v3/dist/domains/contract-testing/services/contract-validator.js +5 -4
  130. package/v3/dist/domains/contract-testing/services/contract-validator.js.map +1 -1
  131. package/v3/dist/domains/coverage-analysis/services/hnsw-index.d.ts.map +1 -1
  132. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js +1 -0
  133. package/v3/dist/domains/coverage-analysis/services/hnsw-index.js.map +1 -1
  134. package/v3/dist/domains/defect-intelligence/coordinator.d.ts +2 -2
  135. package/v3/dist/domains/defect-intelligence/coordinator.d.ts.map +1 -1
  136. package/v3/dist/domains/defect-intelligence/coordinator.js.map +1 -1
  137. package/v3/dist/domains/defect-intelligence/plugin.d.ts +6 -1
  138. package/v3/dist/domains/defect-intelligence/plugin.d.ts.map +1 -1
  139. package/v3/dist/domains/defect-intelligence/plugin.js +101 -0
  140. package/v3/dist/domains/defect-intelligence/plugin.js.map +1 -1
  141. package/v3/dist/domains/defect-intelligence/services/defect-predictor.d.ts.map +1 -1
  142. package/v3/dist/domains/defect-intelligence/services/defect-predictor.js +3 -2
  143. package/v3/dist/domains/defect-intelligence/services/defect-predictor.js.map +1 -1
  144. package/v3/dist/domains/domain-interface.d.ts.map +1 -1
  145. package/v3/dist/domains/domain-interface.js +24 -9
  146. package/v3/dist/domains/domain-interface.js.map +1 -1
  147. package/v3/dist/domains/learning-optimization/plugin.d.ts +2 -1
  148. package/v3/dist/domains/learning-optimization/plugin.d.ts.map +1 -1
  149. package/v3/dist/domains/learning-optimization/plugin.js +49 -0
  150. package/v3/dist/domains/learning-optimization/plugin.js.map +1 -1
  151. package/v3/dist/domains/quality-assessment/coordinator.d.ts +90 -1
  152. package/v3/dist/domains/quality-assessment/coordinator.d.ts.map +1 -1
  153. package/v3/dist/domains/quality-assessment/coordinator.js +310 -0
  154. package/v3/dist/domains/quality-assessment/coordinator.js.map +1 -1
  155. package/v3/dist/domains/requirements-validation/index.d.ts +1 -0
  156. package/v3/dist/domains/requirements-validation/index.d.ts.map +1 -1
  157. package/v3/dist/domains/requirements-validation/index.js +2 -0
  158. package/v3/dist/domains/requirements-validation/index.js.map +1 -1
  159. package/v3/dist/domains/requirements-validation/plugin.d.ts +13 -1
  160. package/v3/dist/domains/requirements-validation/plugin.d.ts.map +1 -1
  161. package/v3/dist/domains/requirements-validation/plugin.js +94 -0
  162. package/v3/dist/domains/requirements-validation/plugin.js.map +1 -1
  163. package/v3/dist/domains/requirements-validation/qcsd-ideation-plugin.d.ts +245 -0
  164. package/v3/dist/domains/requirements-validation/qcsd-ideation-plugin.d.ts.map +1 -0
  165. package/v3/dist/domains/requirements-validation/qcsd-ideation-plugin.js +1143 -0
  166. package/v3/dist/domains/requirements-validation/qcsd-ideation-plugin.js.map +1 -0
  167. package/v3/dist/domains/requirements-validation/services/product-factors-assessment/code-intelligence/codebase-analyzer.d.ts.map +1 -1
  168. package/v3/dist/domains/requirements-validation/services/product-factors-assessment/code-intelligence/codebase-analyzer.js +3 -2
  169. package/v3/dist/domains/requirements-validation/services/product-factors-assessment/code-intelligence/codebase-analyzer.js.map +1 -1
  170. package/v3/dist/domains/security-compliance/plugin.d.ts +3 -2
  171. package/v3/dist/domains/security-compliance/plugin.d.ts.map +1 -1
  172. package/v3/dist/domains/security-compliance/plugin.js +64 -0
  173. package/v3/dist/domains/security-compliance/plugin.js.map +1 -1
  174. package/v3/dist/domains/security-compliance/services/scanners/dast-auth-testing.d.ts +25 -0
  175. package/v3/dist/domains/security-compliance/services/scanners/dast-auth-testing.d.ts.map +1 -0
  176. package/v3/dist/domains/security-compliance/services/scanners/dast-auth-testing.js +160 -0
  177. package/v3/dist/domains/security-compliance/services/scanners/dast-auth-testing.js.map +1 -0
  178. package/v3/dist/domains/security-compliance/services/scanners/dast-helpers.d.ts +48 -0
  179. package/v3/dist/domains/security-compliance/services/scanners/dast-helpers.d.ts.map +1 -0
  180. package/v3/dist/domains/security-compliance/services/scanners/dast-helpers.js +385 -0
  181. package/v3/dist/domains/security-compliance/services/scanners/dast-helpers.js.map +1 -0
  182. package/v3/dist/domains/security-compliance/services/scanners/dast-injection-testing.d.ts +20 -0
  183. package/v3/dist/domains/security-compliance/services/scanners/dast-injection-testing.d.ts.map +1 -0
  184. package/v3/dist/domains/security-compliance/services/scanners/dast-injection-testing.js +99 -0
  185. package/v3/dist/domains/security-compliance/services/scanners/dast-injection-testing.js.map +1 -0
  186. package/v3/dist/domains/security-compliance/services/scanners/dast-scanner.d.ts +62 -0
  187. package/v3/dist/domains/security-compliance/services/scanners/dast-scanner.d.ts.map +1 -0
  188. package/v3/dist/domains/security-compliance/services/scanners/dast-scanner.js +329 -0
  189. package/v3/dist/domains/security-compliance/services/scanners/dast-scanner.js.map +1 -0
  190. package/v3/dist/domains/security-compliance/services/scanners/dependency-scanner.d.ts +46 -0
  191. package/v3/dist/domains/security-compliance/services/scanners/dependency-scanner.d.ts.map +1 -0
  192. package/v3/dist/domains/security-compliance/services/scanners/dependency-scanner.js +180 -0
  193. package/v3/dist/domains/security-compliance/services/scanners/dependency-scanner.js.map +1 -0
  194. package/v3/dist/domains/security-compliance/services/scanners/index.d.ts +14 -0
  195. package/v3/dist/domains/security-compliance/services/scanners/index.d.ts.map +1 -0
  196. package/v3/dist/domains/security-compliance/services/scanners/index.js +16 -0
  197. package/v3/dist/domains/security-compliance/services/scanners/index.js.map +1 -0
  198. package/v3/dist/domains/security-compliance/services/scanners/sast-scanner.d.ts +92 -0
  199. package/v3/dist/domains/security-compliance/services/scanners/sast-scanner.d.ts.map +1 -0
  200. package/v3/dist/domains/security-compliance/services/scanners/sast-scanner.js +440 -0
  201. package/v3/dist/domains/security-compliance/services/scanners/sast-scanner.js.map +1 -0
  202. package/v3/dist/domains/security-compliance/services/scanners/scanner-orchestrator.d.ts +78 -0
  203. package/v3/dist/domains/security-compliance/services/scanners/scanner-orchestrator.d.ts.map +1 -0
  204. package/v3/dist/domains/security-compliance/services/scanners/scanner-orchestrator.js +179 -0
  205. package/v3/dist/domains/security-compliance/services/scanners/scanner-orchestrator.js.map +1 -0
  206. package/v3/dist/domains/security-compliance/services/scanners/scanner-types.d.ts +91 -0
  207. package/v3/dist/domains/security-compliance/services/scanners/scanner-types.d.ts.map +1 -0
  208. package/v3/dist/domains/security-compliance/services/scanners/scanner-types.js +15 -0
  209. package/v3/dist/domains/security-compliance/services/scanners/scanner-types.js.map +1 -0
  210. package/v3/dist/domains/security-compliance/services/scanners/security-patterns.d.ts +16 -0
  211. package/v3/dist/domains/security-compliance/services/scanners/security-patterns.d.ts.map +1 -0
  212. package/v3/dist/domains/security-compliance/services/scanners/security-patterns.js +507 -0
  213. package/v3/dist/domains/security-compliance/services/scanners/security-patterns.js.map +1 -0
  214. package/v3/dist/domains/security-compliance/services/security-auditor.d.ts.map +1 -1
  215. package/v3/dist/domains/security-compliance/services/security-auditor.js +2 -1
  216. package/v3/dist/domains/security-compliance/services/security-auditor.js.map +1 -1
  217. package/v3/dist/domains/security-compliance/services/security-scanner.d.ts +20 -213
  218. package/v3/dist/domains/security-compliance/services/security-scanner.d.ts.map +1 -1
  219. package/v3/dist/domains/security-compliance/services/security-scanner.js +37 -2013
  220. package/v3/dist/domains/security-compliance/services/security-scanner.js.map +1 -1
  221. package/v3/dist/domains/security-compliance/services/semgrep-integration.d.ts.map +1 -1
  222. package/v3/dist/domains/security-compliance/services/semgrep-integration.js +7 -6
  223. package/v3/dist/domains/security-compliance/services/semgrep-integration.js.map +1 -1
  224. package/v3/dist/domains/test-execution/services/auth-state-manager.d.ts.map +1 -1
  225. package/v3/dist/domains/test-execution/services/auth-state-manager.js +6 -4
  226. package/v3/dist/domains/test-execution/services/auth-state-manager.js.map +1 -1
  227. package/v3/dist/domains/test-execution/services/e2e/assertion-handlers.d.ts +55 -0
  228. package/v3/dist/domains/test-execution/services/e2e/assertion-handlers.d.ts.map +1 -0
  229. package/v3/dist/domains/test-execution/services/e2e/assertion-handlers.js +407 -0
  230. package/v3/dist/domains/test-execution/services/e2e/assertion-handlers.js.map +1 -0
  231. package/v3/dist/domains/test-execution/services/e2e/browser-orchestrator.d.ts +122 -0
  232. package/v3/dist/domains/test-execution/services/e2e/browser-orchestrator.d.ts.map +1 -0
  233. package/v3/dist/domains/test-execution/services/e2e/browser-orchestrator.js +325 -0
  234. package/v3/dist/domains/test-execution/services/e2e/browser-orchestrator.js.map +1 -0
  235. package/v3/dist/domains/test-execution/services/e2e/e2e-coordinator.d.ts +97 -0
  236. package/v3/dist/domains/test-execution/services/e2e/e2e-coordinator.d.ts.map +1 -0
  237. package/v3/dist/domains/test-execution/services/e2e/e2e-coordinator.js +297 -0
  238. package/v3/dist/domains/test-execution/services/e2e/e2e-coordinator.js.map +1 -0
  239. package/v3/dist/domains/test-execution/services/e2e/index.d.ts +22 -0
  240. package/v3/dist/domains/test-execution/services/e2e/index.d.ts.map +1 -0
  241. package/v3/dist/domains/test-execution/services/e2e/index.js +52 -0
  242. package/v3/dist/domains/test-execution/services/e2e/index.js.map +1 -0
  243. package/v3/dist/domains/test-execution/services/e2e/result-collector.d.ts +51 -0
  244. package/v3/dist/domains/test-execution/services/e2e/result-collector.d.ts.map +1 -0
  245. package/v3/dist/domains/test-execution/services/e2e/result-collector.js +133 -0
  246. package/v3/dist/domains/test-execution/services/e2e/result-collector.js.map +1 -0
  247. package/v3/dist/domains/test-execution/services/e2e/step-executors.d.ts +48 -0
  248. package/v3/dist/domains/test-execution/services/e2e/step-executors.d.ts.map +1 -0
  249. package/v3/dist/domains/test-execution/services/e2e/step-executors.js +422 -0
  250. package/v3/dist/domains/test-execution/services/e2e/step-executors.js.map +1 -0
  251. package/v3/dist/domains/test-execution/services/e2e/step-retry-handler.d.ts +49 -0
  252. package/v3/dist/domains/test-execution/services/e2e/step-retry-handler.d.ts.map +1 -0
  253. package/v3/dist/domains/test-execution/services/e2e/step-retry-handler.js +146 -0
  254. package/v3/dist/domains/test-execution/services/e2e/step-retry-handler.js.map +1 -0
  255. package/v3/dist/domains/test-execution/services/e2e/types.d.ts +138 -0
  256. package/v3/dist/domains/test-execution/services/e2e/types.d.ts.map +1 -0
  257. package/v3/dist/domains/test-execution/services/e2e/types.js +65 -0
  258. package/v3/dist/domains/test-execution/services/e2e/types.js.map +1 -0
  259. package/v3/dist/domains/test-execution/services/e2e/wait-condition-handler.d.ts +33 -0
  260. package/v3/dist/domains/test-execution/services/e2e/wait-condition-handler.d.ts.map +1 -0
  261. package/v3/dist/domains/test-execution/services/e2e/wait-condition-handler.js +114 -0
  262. package/v3/dist/domains/test-execution/services/e2e/wait-condition-handler.js.map +1 -0
  263. package/v3/dist/domains/test-execution/services/e2e-runner.d.ts +18 -392
  264. package/v3/dist/domains/test-execution/services/e2e-runner.d.ts.map +1 -1
  265. package/v3/dist/domains/test-execution/services/e2e-runner.js +25 -1757
  266. package/v3/dist/domains/test-execution/services/e2e-runner.js.map +1 -1
  267. package/v3/dist/domains/test-execution/services/flaky-detector.d.ts.map +1 -1
  268. package/v3/dist/domains/test-execution/services/flaky-detector.js +12 -9
  269. package/v3/dist/domains/test-execution/services/flaky-detector.js.map +1 -1
  270. package/v3/dist/domains/test-execution/services/retry-handler.d.ts.map +1 -1
  271. package/v3/dist/domains/test-execution/services/retry-handler.js +7 -5
  272. package/v3/dist/domains/test-execution/services/retry-handler.js.map +1 -1
  273. package/v3/dist/domains/test-execution/services/test-executor.d.ts.map +1 -1
  274. package/v3/dist/domains/test-execution/services/test-executor.js +4 -3
  275. package/v3/dist/domains/test-execution/services/test-executor.js.map +1 -1
  276. package/v3/dist/domains/visual-accessibility/coordinator.d.ts +70 -0
  277. package/v3/dist/domains/visual-accessibility/coordinator.d.ts.map +1 -1
  278. package/v3/dist/domains/visual-accessibility/coordinator.js +172 -0
  279. package/v3/dist/domains/visual-accessibility/coordinator.js.map +1 -1
  280. package/v3/dist/domains/visual-accessibility/plugin.d.ts +2 -1
  281. package/v3/dist/domains/visual-accessibility/plugin.d.ts.map +1 -1
  282. package/v3/dist/domains/visual-accessibility/plugin.js +63 -0
  283. package/v3/dist/domains/visual-accessibility/plugin.js.map +1 -1
  284. package/v3/dist/domains/visual-accessibility/services/accessibility-tester.d.ts.map +1 -1
  285. package/v3/dist/domains/visual-accessibility/services/accessibility-tester.js +3 -2
  286. package/v3/dist/domains/visual-accessibility/services/accessibility-tester.js.map +1 -1
  287. package/v3/dist/domains/visual-accessibility/services/browser-security-scanner.d.ts.map +1 -1
  288. package/v3/dist/domains/visual-accessibility/services/browser-security-scanner.js +22 -12
  289. package/v3/dist/domains/visual-accessibility/services/browser-security-scanner.js.map +1 -1
  290. package/v3/dist/domains/visual-accessibility/services/viewport-capture.d.ts.map +1 -1
  291. package/v3/dist/domains/visual-accessibility/services/viewport-capture.js +3 -2
  292. package/v3/dist/domains/visual-accessibility/services/viewport-capture.js.map +1 -1
  293. package/v3/dist/domains/visual-accessibility/services/visual-regression.d.ts.map +1 -1
  294. package/v3/dist/domains/visual-accessibility/services/visual-regression.js +3 -2
  295. package/v3/dist/domains/visual-accessibility/services/visual-regression.js.map +1 -1
  296. package/v3/dist/hooks/cross-phase-hooks.d.ts +42 -0
  297. package/v3/dist/hooks/cross-phase-hooks.d.ts.map +1 -0
  298. package/v3/dist/hooks/cross-phase-hooks.js +338 -0
  299. package/v3/dist/hooks/cross-phase-hooks.js.map +1 -0
  300. package/v3/dist/hooks/index.d.ts +9 -0
  301. package/v3/dist/hooks/index.d.ts.map +1 -0
  302. package/v3/dist/hooks/index.js +9 -0
  303. package/v3/dist/hooks/index.js.map +1 -0
  304. package/v3/dist/init/agents-installer.d.ts.map +1 -1
  305. package/v3/dist/init/agents-installer.js +6 -4
  306. package/v3/dist/init/agents-installer.js.map +1 -1
  307. package/v3/dist/init/enhancements/claude-flow-adapter.d.ts.map +1 -1
  308. package/v3/dist/init/enhancements/claude-flow-adapter.js +15 -9
  309. package/v3/dist/init/enhancements/claude-flow-adapter.js.map +1 -1
  310. package/v3/dist/init/enhancements/detector.js +6 -4
  311. package/v3/dist/init/enhancements/detector.js.map +1 -1
  312. package/v3/dist/init/init-wizard.d.ts +5 -0
  313. package/v3/dist/init/init-wizard.d.ts.map +1 -1
  314. package/v3/dist/init/init-wizard.js +77 -14
  315. package/v3/dist/init/init-wizard.js.map +1 -1
  316. package/v3/dist/init/migration/data-migrator.d.ts.map +1 -1
  317. package/v3/dist/init/migration/data-migrator.js +6 -4
  318. package/v3/dist/init/migration/data-migrator.js.map +1 -1
  319. package/v3/dist/init/phases/02-analysis.js +2 -2
  320. package/v3/dist/init/phases/02-analysis.js.map +1 -1
  321. package/v3/dist/init/phases/04-database.d.ts.map +1 -1
  322. package/v3/dist/init/phases/04-database.js +0 -1
  323. package/v3/dist/init/phases/04-database.js.map +1 -1
  324. package/v3/dist/init/phases/05-learning.js +1 -1
  325. package/v3/dist/init/phases/05-learning.js.map +1 -1
  326. package/v3/dist/init/phases/11-claude-md.d.ts.map +1 -1
  327. package/v3/dist/init/phases/11-claude-md.js +25 -0
  328. package/v3/dist/init/phases/11-claude-md.js.map +1 -1
  329. package/v3/dist/init/phases/12-verification.d.ts.map +1 -1
  330. package/v3/dist/init/phases/12-verification.js +2 -1
  331. package/v3/dist/init/phases/12-verification.js.map +1 -1
  332. package/v3/dist/init/project-analyzer.d.ts.map +1 -1
  333. package/v3/dist/init/project-analyzer.js +12 -8
  334. package/v3/dist/init/project-analyzer.js.map +1 -1
  335. package/v3/dist/init/skills-installer.d.ts.map +1 -1
  336. package/v3/dist/init/skills-installer.js +6 -4
  337. package/v3/dist/init/skills-installer.js.map +1 -1
  338. package/v3/dist/init/token-bootstrap.d.ts.map +1 -1
  339. package/v3/dist/init/token-bootstrap.js +2 -1
  340. package/v3/dist/init/token-bootstrap.js.map +1 -1
  341. package/v3/dist/integrations/agent-booster-wasm/index.d.ts.map +1 -1
  342. package/v3/dist/integrations/agent-booster-wasm/index.js +8 -4
  343. package/v3/dist/integrations/agent-booster-wasm/index.js.map +1 -1
  344. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.d.ts.map +1 -1
  345. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js +3 -2
  346. package/v3/dist/integrations/agentic-flow/model-router/signal-collector.js.map +1 -1
  347. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.d.ts.map +1 -1
  348. package/v3/dist/integrations/agentic-flow/reasoning-bank/experience-replay.js.map +1 -1
  349. package/v3/dist/integrations/agentic-flow/reasoning-bank/trajectory-tracker.d.ts.map +1 -1
  350. package/v3/dist/integrations/agentic-flow/reasoning-bank/trajectory-tracker.js.map +1 -1
  351. package/v3/dist/integrations/browser/agent-browser/client.d.ts.map +1 -1
  352. package/v3/dist/integrations/browser/agent-browser/client.js +9 -6
  353. package/v3/dist/integrations/browser/agent-browser/client.js.map +1 -1
  354. package/v3/dist/integrations/browser/agent-browser/command-executor.d.ts.map +1 -1
  355. package/v3/dist/integrations/browser/agent-browser/command-executor.js +3 -2
  356. package/v3/dist/integrations/browser/agent-browser/command-executor.js.map +1 -1
  357. package/v3/dist/integrations/browser/index.d.ts +1 -0
  358. package/v3/dist/integrations/browser/index.d.ts.map +1 -1
  359. package/v3/dist/integrations/browser/index.js +6 -0
  360. package/v3/dist/integrations/browser/index.js.map +1 -1
  361. package/v3/dist/integrations/browser/web-content-fetcher.d.ts +154 -0
  362. package/v3/dist/integrations/browser/web-content-fetcher.d.ts.map +1 -0
  363. package/v3/dist/integrations/browser/web-content-fetcher.js +529 -0
  364. package/v3/dist/integrations/browser/web-content-fetcher.js.map +1 -0
  365. package/v3/dist/integrations/coherence/threshold-tuner.d.ts.map +1 -1
  366. package/v3/dist/integrations/coherence/threshold-tuner.js +3 -2
  367. package/v3/dist/integrations/coherence/threshold-tuner.js.map +1 -1
  368. package/v3/dist/integrations/coherence/wasm-loader.d.ts.map +1 -1
  369. package/v3/dist/integrations/coherence/wasm-loader.js +3 -2
  370. package/v3/dist/integrations/coherence/wasm-loader.js.map +1 -1
  371. package/v3/dist/integrations/n8n/agent-factory.d.ts.map +1 -1
  372. package/v3/dist/integrations/n8n/agent-factory.js +6 -4
  373. package/v3/dist/integrations/n8n/agent-factory.js.map +1 -1
  374. package/v3/dist/integrations/rl-suite/persistence/q-value-store.d.ts.map +1 -1
  375. package/v3/dist/integrations/rl-suite/persistence/q-value-store.js.map +1 -1
  376. package/v3/dist/integrations/ruvector/interfaces.js +1 -1
  377. package/v3/dist/integrations/ruvector/interfaces.js.map +1 -1
  378. package/v3/dist/integrations/ruvector/sona-persistence.d.ts.map +1 -1
  379. package/v3/dist/integrations/ruvector/sona-persistence.js +6 -4
  380. package/v3/dist/integrations/ruvector/sona-persistence.js.map +1 -1
  381. package/v3/dist/integrations/vibium/client.d.ts.map +1 -1
  382. package/v3/dist/integrations/vibium/client.js +3 -2
  383. package/v3/dist/integrations/vibium/client.js.map +1 -1
  384. package/v3/dist/kernel/agent-coordinator.d.ts +1 -1
  385. package/v3/dist/kernel/agent-coordinator.d.ts.map +1 -1
  386. package/v3/dist/kernel/agent-coordinator.js +4 -4
  387. package/v3/dist/kernel/agent-coordinator.js.map +1 -1
  388. package/v3/dist/kernel/constants.d.ts +155 -0
  389. package/v3/dist/kernel/constants.d.ts.map +1 -0
  390. package/v3/dist/kernel/constants.js +169 -0
  391. package/v3/dist/kernel/constants.js.map +1 -0
  392. package/v3/dist/kernel/event-bus.d.ts +8 -0
  393. package/v3/dist/kernel/event-bus.d.ts.map +1 -1
  394. package/v3/dist/kernel/event-bus.js +79 -17
  395. package/v3/dist/kernel/event-bus.js.map +1 -1
  396. package/v3/dist/kernel/hybrid-backend.d.ts.map +1 -1
  397. package/v3/dist/kernel/hybrid-backend.js +4 -3
  398. package/v3/dist/kernel/hybrid-backend.js.map +1 -1
  399. package/v3/dist/kernel/index.d.ts +1 -1
  400. package/v3/dist/kernel/index.d.ts.map +1 -1
  401. package/v3/dist/kernel/index.js +3 -1
  402. package/v3/dist/kernel/index.js.map +1 -1
  403. package/v3/dist/kernel/kernel.d.ts.map +1 -1
  404. package/v3/dist/kernel/kernel.js +3 -2
  405. package/v3/dist/kernel/kernel.js.map +1 -1
  406. package/v3/dist/kernel/memory-backend.d.ts.map +1 -1
  407. package/v3/dist/kernel/memory-backend.js +4 -3
  408. package/v3/dist/kernel/memory-backend.js.map +1 -1
  409. package/v3/dist/kernel/unified-memory.d.ts +26 -0
  410. package/v3/dist/kernel/unified-memory.d.ts.map +1 -1
  411. package/v3/dist/kernel/unified-memory.js +68 -13
  412. package/v3/dist/kernel/unified-memory.js.map +1 -1
  413. package/v3/dist/kernel/unified-persistence.js +3 -2
  414. package/v3/dist/kernel/unified-persistence.js.map +1 -1
  415. package/v3/dist/learning/aqe-learning-engine.d.ts.map +1 -1
  416. package/v3/dist/learning/aqe-learning-engine.js +12 -8
  417. package/v3/dist/learning/aqe-learning-engine.js.map +1 -1
  418. package/v3/dist/learning/dream/index.d.ts +1 -1
  419. package/v3/dist/learning/dream/index.d.ts.map +1 -1
  420. package/v3/dist/learning/dream/index.js +3 -1
  421. package/v3/dist/learning/dream/index.js.map +1 -1
  422. package/v3/dist/learning/dream/spreading-activation.d.ts +41 -0
  423. package/v3/dist/learning/dream/spreading-activation.d.ts.map +1 -1
  424. package/v3/dist/learning/dream/spreading-activation.js +79 -0
  425. package/v3/dist/learning/dream/spreading-activation.js.map +1 -1
  426. package/v3/dist/learning/pattern-store.d.ts.map +1 -1
  427. package/v3/dist/learning/pattern-store.js +16 -6
  428. package/v3/dist/learning/pattern-store.js.map +1 -1
  429. package/v3/dist/learning/qe-unified-memory.js +1 -1
  430. package/v3/dist/learning/qe-unified-memory.js.map +1 -1
  431. package/v3/dist/learning/real-embeddings.d.ts.map +1 -1
  432. package/v3/dist/learning/real-embeddings.js +7 -1
  433. package/v3/dist/learning/real-embeddings.js.map +1 -1
  434. package/v3/dist/learning/real-qe-reasoning-bank.js +2 -2
  435. package/v3/dist/learning/real-qe-reasoning-bank.js.map +1 -1
  436. package/v3/dist/learning/sqlite-persistence.d.ts +2 -2
  437. package/v3/dist/learning/sqlite-persistence.d.ts.map +1 -1
  438. package/v3/dist/learning/sqlite-persistence.js +1 -1
  439. package/v3/dist/learning/sqlite-persistence.js.map +1 -1
  440. package/v3/dist/learning/v2-to-v3-migration.d.ts +2 -2
  441. package/v3/dist/learning/v2-to-v3-migration.d.ts.map +1 -1
  442. package/v3/dist/learning/v2-to-v3-migration.js +2 -2
  443. package/v3/dist/learning/v2-to-v3-migration.js.map +1 -1
  444. package/v3/dist/logging/console-logger.d.ts +96 -0
  445. package/v3/dist/logging/console-logger.d.ts.map +1 -0
  446. package/v3/dist/logging/console-logger.js +247 -0
  447. package/v3/dist/logging/console-logger.js.map +1 -0
  448. package/v3/dist/logging/index.d.ts +42 -0
  449. package/v3/dist/logging/index.d.ts.map +1 -0
  450. package/v3/dist/logging/index.js +39 -0
  451. package/v3/dist/logging/index.js.map +1 -0
  452. package/v3/dist/logging/logger-factory.d.ts +145 -0
  453. package/v3/dist/logging/logger-factory.d.ts.map +1 -0
  454. package/v3/dist/logging/logger-factory.js +218 -0
  455. package/v3/dist/logging/logger-factory.js.map +1 -0
  456. package/v3/dist/logging/logger.d.ts +89 -0
  457. package/v3/dist/logging/logger.d.ts.map +1 -0
  458. package/v3/dist/logging/logger.js +74 -0
  459. package/v3/dist/logging/logger.js.map +1 -0
  460. package/v3/dist/mcp/bundle.js +11385 -7526
  461. package/v3/dist/mcp/handlers/agent-handlers.js +2 -2
  462. package/v3/dist/mcp/handlers/agent-handlers.js.map +1 -1
  463. package/v3/dist/mcp/handlers/core-handlers.d.ts +2 -0
  464. package/v3/dist/mcp/handlers/core-handlers.d.ts.map +1 -1
  465. package/v3/dist/mcp/handlers/core-handlers.js +33 -0
  466. package/v3/dist/mcp/handlers/core-handlers.js.map +1 -1
  467. package/v3/dist/mcp/handlers/cross-phase-handlers.d.ts +110 -0
  468. package/v3/dist/mcp/handlers/cross-phase-handlers.d.ts.map +1 -0
  469. package/v3/dist/mcp/handlers/cross-phase-handlers.js +216 -0
  470. package/v3/dist/mcp/handlers/cross-phase-handlers.js.map +1 -0
  471. package/v3/dist/mcp/handlers/domain-handler-configs.d.ts +151 -0
  472. package/v3/dist/mcp/handlers/domain-handler-configs.d.ts.map +1 -0
  473. package/v3/dist/mcp/handlers/domain-handler-configs.js +486 -0
  474. package/v3/dist/mcp/handlers/domain-handler-configs.js.map +1 -0
  475. package/v3/dist/mcp/handlers/domain-handlers.d.ts +174 -120
  476. package/v3/dist/mcp/handlers/domain-handlers.d.ts.map +1 -1
  477. package/v3/dist/mcp/handlers/domain-handlers.js +178 -1049
  478. package/v3/dist/mcp/handlers/domain-handlers.js.map +1 -1
  479. package/v3/dist/mcp/handlers/handler-factory.d.ts +182 -0
  480. package/v3/dist/mcp/handlers/handler-factory.d.ts.map +1 -0
  481. package/v3/dist/mcp/handlers/handler-factory.js +327 -0
  482. package/v3/dist/mcp/handlers/handler-factory.js.map +1 -0
  483. package/v3/dist/mcp/handlers/index.d.ts +1 -0
  484. package/v3/dist/mcp/handlers/index.d.ts.map +1 -1
  485. package/v3/dist/mcp/handlers/index.js +2 -0
  486. package/v3/dist/mcp/handlers/index.js.map +1 -1
  487. package/v3/dist/mcp/handlers/task-handlers.d.ts +1 -0
  488. package/v3/dist/mcp/handlers/task-handlers.d.ts.map +1 -1
  489. package/v3/dist/mcp/handlers/task-handlers.js +91 -7
  490. package/v3/dist/mcp/handlers/task-handlers.js.map +1 -1
  491. package/v3/dist/mcp/server.d.ts.map +1 -1
  492. package/v3/dist/mcp/server.js +107 -1
  493. package/v3/dist/mcp/server.js.map +1 -1
  494. package/v3/dist/mcp/types.d.ts +5 -3
  495. package/v3/dist/mcp/types.d.ts.map +1 -1
  496. package/v3/dist/memory/cross-phase-memory.d.ts +55 -0
  497. package/v3/dist/memory/cross-phase-memory.d.ts.map +1 -0
  498. package/v3/dist/memory/cross-phase-memory.js +265 -0
  499. package/v3/dist/memory/cross-phase-memory.js.map +1 -0
  500. package/v3/dist/memory/index.d.ts +9 -0
  501. package/v3/dist/memory/index.d.ts.map +1 -0
  502. package/v3/dist/memory/index.js +9 -0
  503. package/v3/dist/memory/index.js.map +1 -0
  504. package/v3/dist/shared/io/file-reader.d.ts.map +1 -1
  505. package/v3/dist/shared/io/file-reader.js +3 -2
  506. package/v3/dist/shared/io/file-reader.js.map +1 -1
  507. package/v3/dist/shared/utils/binary-insert.d.ts +85 -0
  508. package/v3/dist/shared/utils/binary-insert.d.ts.map +1 -0
  509. package/v3/dist/shared/utils/binary-insert.js +122 -0
  510. package/v3/dist/shared/utils/binary-insert.js.map +1 -0
  511. package/v3/dist/shared/utils/index.d.ts +1 -0
  512. package/v3/dist/shared/utils/index.d.ts.map +1 -1
  513. package/v3/dist/shared/utils/index.js +1 -0
  514. package/v3/dist/shared/utils/index.js.map +1 -1
  515. package/v3/dist/strange-loop/belief-reconciler.d.ts.map +1 -1
  516. package/v3/dist/strange-loop/belief-reconciler.js +3 -2
  517. package/v3/dist/strange-loop/belief-reconciler.js.map +1 -1
  518. package/v3/dist/sync/claude-flow-bridge.d.ts +1 -1
  519. package/v3/dist/sync/claude-flow-bridge.d.ts.map +1 -1
  520. package/v3/dist/sync/claude-flow-bridge.js +7 -5
  521. package/v3/dist/sync/claude-flow-bridge.js.map +1 -1
  522. package/v3/dist/sync/cloud/postgres-writer.d.ts.map +1 -1
  523. package/v3/dist/sync/cloud/postgres-writer.js +0 -1
  524. package/v3/dist/sync/cloud/postgres-writer.js.map +1 -1
  525. package/v3/dist/sync/interfaces.d.ts +6 -0
  526. package/v3/dist/sync/interfaces.d.ts.map +1 -1
  527. package/v3/dist/sync/interfaces.js +34 -47
  528. package/v3/dist/sync/interfaces.js.map +1 -1
  529. package/v3/dist/sync/readers/sqlite-reader.d.ts +1 -1
  530. package/v3/dist/sync/readers/sqlite-reader.d.ts.map +1 -1
  531. package/v3/dist/sync/readers/sqlite-reader.js +4 -3
  532. package/v3/dist/sync/readers/sqlite-reader.js.map +1 -1
  533. package/v3/dist/test-scheduling/flaky-tracking/flaky-tracker.d.ts.map +1 -1
  534. package/v3/dist/test-scheduling/flaky-tracking/flaky-tracker.js +3 -2
  535. package/v3/dist/test-scheduling/flaky-tracking/flaky-tracker.js.map +1 -1
  536. package/v3/dist/test-scheduling/git-aware/test-selector.d.ts.map +1 -1
  537. package/v3/dist/test-scheduling/git-aware/test-selector.js +3 -2
  538. package/v3/dist/test-scheduling/git-aware/test-selector.js.map +1 -1
  539. package/v3/dist/types/cross-phase-signals.d.ts +119 -0
  540. package/v3/dist/types/cross-phase-signals.d.ts.map +1 -0
  541. package/v3/dist/types/cross-phase-signals.js +33 -0
  542. package/v3/dist/types/cross-phase-signals.js.map +1 -0
  543. package/v3/dist/types/index.d.ts +9 -0
  544. package/v3/dist/types/index.d.ts.map +1 -0
  545. package/v3/dist/types/index.js +9 -0
  546. package/v3/dist/types/index.js.map +1 -0
  547. package/v3/dist/workers/worker-manager.d.ts.map +1 -1
  548. package/v3/dist/workers/worker-manager.js +3 -2
  549. package/v3/dist/workers/worker-manager.js.map +1 -1
  550. package/v3/dist/workflows/browser/workflow-loader.d.ts +3 -3
  551. package/v3/dist/workflows/browser/workflow-loader.d.ts.map +1 -1
  552. package/v3/dist/workflows/browser/workflow-loader.js.map +1 -1
  553. package/v3/package.json +4 -1
@@ -1,2015 +1,39 @@
1
1
  /**
2
- * Agentic QE v3 - Security Scanner Service
3
- * Implements SAST and DAST security scanning capabilities
4
- * Includes OSV API integration for dependency vulnerability scanning
5
- */
6
- import { v4 as uuidv4 } from 'uuid';
7
- import { ok, err } from '../../../shared/types/index.js';
8
- import { OSVClient } from '../../../shared/security/index.js';
9
- const DEFAULT_CONFIG = {
10
- defaultRuleSets: ['owasp-top-10', 'cwe-sans-25'],
11
- maxConcurrentScans: 4,
12
- timeout: 300000, // 5 minutes
13
- enableFalsePositiveDetection: true,
14
- dastMaxDepth: 5,
15
- dastActiveScanning: false,
16
- enableLLMAnalysis: true, // On by default - opt-out (ADR-051)
17
- llmModelTier: 4, // Opus for security analysis (needs expert reasoning)
18
- };
19
- /**
20
- * SQL Injection Detection Patterns
21
- * OWASP A03:2021 - Injection
22
- */
23
- const SQL_INJECTION_PATTERNS = [
24
- {
25
- id: 'sqli-string-concat',
26
- pattern: /query\s*\(\s*['"`].*\+.*['"`]\s*\)/g,
27
- category: 'injection',
28
- severity: 'critical',
29
- title: 'SQL Injection via String Concatenation',
30
- description: 'SQL query constructed using string concatenation with potentially untrusted input',
31
- owaspId: 'A03:2021',
32
- cweId: 'CWE-89',
33
- remediation: 'Use parameterized queries or prepared statements instead of string concatenation',
34
- fixExample: 'db.query("SELECT * FROM users WHERE id = $1", [userId])',
35
- },
36
- {
37
- id: 'sqli-template-literal',
38
- pattern: /execute\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g,
39
- category: 'injection',
40
- severity: 'critical',
41
- title: 'SQL Injection via Template Literal',
42
- description: 'SQL query constructed using template literals with embedded expressions',
43
- owaspId: 'A03:2021',
44
- cweId: 'CWE-89',
45
- remediation: 'Use parameterized queries instead of template literals for SQL',
46
- fixExample: 'db.execute("DELETE FROM users WHERE id = ?", [userId])',
47
- },
48
- {
49
- id: 'sqli-raw-query',
50
- pattern: /\.raw\s*\(\s*['"`].*\+|\.raw\s*\(\s*`[^`]*\$\{/g,
51
- category: 'injection',
52
- severity: 'high',
53
- title: 'SQL Injection via Raw Query',
54
- description: 'Raw SQL query with potential user input interpolation',
55
- owaspId: 'A03:2021',
56
- cweId: 'CWE-89',
57
- remediation: 'Avoid raw queries with user input; use ORM methods or parameterized queries',
58
- },
59
- {
60
- id: 'sqli-exec',
61
- pattern: /exec(?:ute)?(?:Sql|Query)?\s*\([^)]*\+[^)]*\)/gi,
62
- category: 'injection',
63
- severity: 'critical',
64
- title: 'SQL Injection via Dynamic Execution',
65
- description: 'Dynamic SQL execution with string concatenation detected',
66
- owaspId: 'A03:2021',
67
- cweId: 'CWE-89',
68
- remediation: 'Never concatenate user input into SQL queries',
69
- },
70
- ];
71
- /**
72
- * XSS (Cross-Site Scripting) Detection Patterns
73
- * OWASP A03:2021 - Injection (includes XSS)
74
- */
75
- const XSS_PATTERNS = [
76
- {
77
- id: 'xss-innerhtml',
78
- pattern: /\.innerHTML\s*=\s*[^'"`;\n]+/g,
79
- category: 'xss',
80
- severity: 'high',
81
- title: 'XSS via innerHTML Assignment',
82
- description: 'Direct innerHTML assignment with potentially unsanitized content',
83
- owaspId: 'A03:2021',
84
- cweId: 'CWE-79',
85
- remediation: 'Use textContent for text, or sanitize HTML with DOMPurify before innerHTML assignment',
86
- fixExample: 'element.textContent = userInput; // or DOMPurify.sanitize(userInput)',
87
- },
88
- {
89
- id: 'xss-document-write',
90
- pattern: /document\.write\s*\([^)]+\)/g,
91
- category: 'xss',
92
- severity: 'high',
93
- title: 'XSS via document.write',
94
- description: 'document.write() can execute scripts from untrusted data',
95
- owaspId: 'A03:2021',
96
- cweId: 'CWE-79',
97
- remediation: 'Avoid document.write(); use DOM manipulation methods instead',
98
- fixExample: 'document.body.appendChild(document.createTextNode(text))',
99
- },
100
- {
101
- id: 'xss-dangerously-set',
102
- pattern: /dangerouslySetInnerHTML\s*=\s*\{/g,
103
- category: 'xss',
104
- severity: 'medium',
105
- title: 'React dangerouslySetInnerHTML Usage',
106
- description: 'dangerouslySetInnerHTML bypasses React XSS protections',
107
- owaspId: 'A03:2021',
108
- cweId: 'CWE-79',
109
- remediation: 'Sanitize HTML content with DOMPurify before using dangerouslySetInnerHTML',
110
- fixExample: 'dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}',
111
- },
112
- {
113
- id: 'xss-eval',
114
- pattern: /eval\s*\([^)]+\)/g,
115
- category: 'xss',
116
- severity: 'critical',
117
- title: 'Code Injection via eval()',
118
- description: 'eval() executes arbitrary code and is a major security risk',
119
- owaspId: 'A03:2021',
120
- cweId: 'CWE-95',
121
- remediation: 'Never use eval(); use JSON.parse() for JSON data or safer alternatives',
122
- fixExample: 'JSON.parse(jsonString) // instead of eval(jsonString)',
123
- },
124
- {
125
- id: 'xss-new-function',
126
- pattern: /new\s+Function\s*\([^)]+\)/g,
127
- category: 'xss',
128
- severity: 'critical',
129
- title: 'Code Injection via Function Constructor',
130
- description: 'Function constructor can execute arbitrary code like eval()',
131
- owaspId: 'A03:2021',
132
- cweId: 'CWE-95',
133
- remediation: 'Avoid the Function constructor; use predefined functions instead',
134
- },
135
- {
136
- id: 'xss-outerhtml',
137
- pattern: /\.outerHTML\s*=\s*[^'"`;\n]+/g,
138
- category: 'xss',
139
- severity: 'high',
140
- title: 'XSS via outerHTML Assignment',
141
- description: 'Direct outerHTML assignment with potentially unsanitized content',
142
- owaspId: 'A03:2021',
143
- cweId: 'CWE-79',
144
- remediation: 'Sanitize content before outerHTML assignment',
145
- },
146
- ];
147
- /**
148
- * Hardcoded Secrets Detection Patterns
149
- * OWASP A02:2021 - Cryptographic Failures
150
- */
151
- const SECRET_PATTERNS = [
152
- {
153
- id: 'secret-aws-access-key',
154
- pattern: /['"`]AKIA[0-9A-Z]{16}['"`]/g,
155
- category: 'sensitive-data',
156
- severity: 'critical',
157
- title: 'AWS Access Key Detected',
158
- description: 'Hardcoded AWS Access Key ID found in source code',
159
- owaspId: 'A02:2021',
160
- cweId: 'CWE-798',
161
- remediation: 'Use environment variables or AWS Secrets Manager for credentials',
162
- fixExample: 'const accessKey = process.env.AWS_ACCESS_KEY_ID',
163
- },
164
- {
165
- id: 'secret-aws-secret-key',
166
- pattern: /['"`][A-Za-z0-9/+=]{40}['"`]/g,
167
- category: 'sensitive-data',
168
- severity: 'critical',
169
- title: 'Potential AWS Secret Key Detected',
170
- description: 'Potential hardcoded AWS Secret Access Key found',
171
- owaspId: 'A02:2021',
172
- cweId: 'CWE-798',
173
- remediation: 'Store secrets in environment variables or secrets manager',
174
- },
175
- {
176
- id: 'secret-openai-key',
177
- pattern: /['"`]sk-[a-zA-Z0-9]{48,}['"`]/g,
178
- category: 'sensitive-data',
179
- severity: 'critical',
180
- title: 'OpenAI API Key Detected',
181
- description: 'Hardcoded OpenAI API key found in source code',
182
- owaspId: 'A02:2021',
183
- cweId: 'CWE-798',
184
- remediation: 'Use environment variables for API keys',
185
- fixExample: 'const apiKey = process.env.OPENAI_API_KEY',
186
- },
187
- {
188
- id: 'secret-generic-password',
189
- pattern: /password\s*[:=]\s*['"`][^'"`]{4,}['"`]/gi,
190
- category: 'sensitive-data',
191
- severity: 'high',
192
- title: 'Hardcoded Password Detected',
193
- description: 'Hardcoded password found in source code',
194
- owaspId: 'A02:2021',
195
- cweId: 'CWE-798',
196
- remediation: 'Never hardcode passwords; use environment variables or secrets manager',
197
- fixExample: 'const password = process.env.DB_PASSWORD',
198
- },
199
- {
200
- id: 'secret-api-key',
201
- pattern: /api[_-]?key\s*[:=]\s*['"`][a-zA-Z0-9_\-]{16,}['"`]/gi,
202
- category: 'sensitive-data',
203
- severity: 'high',
204
- title: 'Hardcoded API Key Detected',
205
- description: 'Hardcoded API key found in source code',
206
- owaspId: 'A02:2021',
207
- cweId: 'CWE-798',
208
- remediation: 'Use environment variables for API keys',
209
- fixExample: 'const apiKey = process.env.API_KEY',
210
- },
211
- {
212
- id: 'secret-jwt',
213
- pattern: /['"`]eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+['"`]/g,
214
- category: 'sensitive-data',
215
- severity: 'high',
216
- title: 'Hardcoded JWT Token Detected',
217
- description: 'Hardcoded JWT token found in source code',
218
- owaspId: 'A02:2021',
219
- cweId: 'CWE-798',
220
- remediation: 'Generate JWT tokens dynamically; never hardcode them',
221
- },
222
- {
223
- id: 'secret-private-key',
224
- pattern: /-----BEGIN\s+(RSA|EC|OPENSSH|DSA)?\s*PRIVATE\s+KEY-----/g,
225
- category: 'sensitive-data',
226
- severity: 'critical',
227
- title: 'Private Key Detected',
228
- description: 'Private key found in source code',
229
- owaspId: 'A02:2021',
230
- cweId: 'CWE-798',
231
- remediation: 'Store private keys in secure key management systems, not in code',
232
- },
233
- {
234
- id: 'secret-github-token',
235
- pattern: /['"`]ghp_[a-zA-Z0-9]{36}['"`]|['"`]github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}['"`]/g,
236
- category: 'sensitive-data',
237
- severity: 'critical',
238
- title: 'GitHub Token Detected',
239
- description: 'Hardcoded GitHub personal access token found',
240
- owaspId: 'A02:2021',
241
- cweId: 'CWE-798',
242
- remediation: 'Use environment variables or GitHub Actions secrets',
243
- },
244
- {
245
- id: 'secret-slack-token',
246
- pattern: /['"`]xox[baprs]-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*['"`]/g,
247
- category: 'sensitive-data',
248
- severity: 'high',
249
- title: 'Slack Token Detected',
250
- description: 'Hardcoded Slack token found in source code',
251
- owaspId: 'A02:2021',
252
- cweId: 'CWE-798',
253
- remediation: 'Use environment variables for Slack tokens',
254
- },
255
- ];
256
- /**
257
- * Path Traversal Detection Patterns
258
- * OWASP A01:2021 - Broken Access Control
259
- */
260
- const PATH_TRAVERSAL_PATTERNS = [
261
- {
262
- id: 'path-traversal-readfile',
263
- pattern: /(?:readFile|readFileSync)\s*\([^)]*\+/g,
264
- category: 'access-control',
265
- severity: 'high',
266
- title: 'Path Traversal via File Read',
267
- description: 'File read operation with concatenated path may allow directory traversal',
268
- owaspId: 'A01:2021',
269
- cweId: 'CWE-22',
270
- remediation: 'Validate and sanitize file paths; use path.resolve() and check against base directory',
271
- fixExample: 'const safePath = path.resolve(baseDir, path.basename(userInput))',
272
- },
273
- {
274
- id: 'path-traversal-pattern',
275
- pattern: /\.\.\/.*\.\.\//g,
276
- category: 'access-control',
277
- severity: 'medium',
278
- title: 'Path Traversal Pattern Detected',
279
- description: 'Suspicious path traversal pattern (../) found in code',
280
- owaspId: 'A01:2021',
281
- cweId: 'CWE-22',
282
- remediation: 'Validate paths and ensure they resolve within expected directories',
283
- },
284
- {
285
- id: 'path-traversal-writefile',
286
- pattern: /(?:writeFile|writeFileSync)\s*\([^)]*\+/g,
287
- category: 'access-control',
288
- severity: 'high',
289
- title: 'Path Traversal via File Write',
290
- description: 'File write operation with concatenated path may allow directory traversal',
291
- owaspId: 'A01:2021',
292
- cweId: 'CWE-22',
293
- remediation: 'Validate file paths before writing; ensure path is within allowed directory',
294
- },
295
- {
296
- id: 'path-traversal-createstream',
297
- pattern: /createReadStream\s*\([^)]*\+/g,
298
- category: 'access-control',
299
- severity: 'high',
300
- title: 'Path Traversal via Stream',
301
- description: 'Stream creation with concatenated path may allow directory traversal',
302
- owaspId: 'A01:2021',
303
- cweId: 'CWE-22',
304
- remediation: 'Validate and sanitize file paths before creating streams',
305
- },
306
- ];
307
- /**
308
- * Command Injection Detection Patterns
309
- * OWASP A03:2021 - Injection
310
- */
311
- const COMMAND_INJECTION_PATTERNS = [
312
- {
313
- id: 'cmd-injection-exec',
314
- pattern: /exec\s*\([^)]*\+[^)]*\)|exec\s*\(\s*`[^`]*\$\{/g,
315
- category: 'injection',
316
- severity: 'critical',
317
- title: 'Command Injection via exec()',
318
- description: 'Shell command execution with unsanitized input',
319
- owaspId: 'A03:2021',
320
- cweId: 'CWE-78',
321
- remediation: 'Use execFile() with argument array instead of exec() with string concatenation',
322
- fixExample: 'execFile("command", [arg1, arg2], callback)',
323
- },
324
- {
325
- id: 'cmd-injection-spawn',
326
- pattern: /spawn\s*\(\s*[^,]+\+|spawn\s*\(\s*`[^`]*\$\{/g,
327
- category: 'injection',
328
- severity: 'critical',
329
- title: 'Command Injection via spawn()',
330
- description: 'Process spawn with potentially unsanitized command',
331
- owaspId: 'A03:2021',
332
- cweId: 'CWE-78',
333
- remediation: 'Use spawn with command and args array; validate inputs',
334
- fixExample: 'spawn("command", [sanitizedArg1, sanitizedArg2])',
335
- },
336
- {
337
- id: 'cmd-injection-shell-true',
338
- pattern: /spawn\s*\([^)]+,\s*\{[^}]*shell\s*:\s*true/g,
339
- category: 'injection',
340
- severity: 'high',
341
- title: 'Dangerous Shell Option in spawn()',
342
- description: 'spawn() with shell: true can enable command injection',
343
- owaspId: 'A03:2021',
344
- cweId: 'CWE-78',
345
- remediation: 'Avoid shell: true option; use direct command execution',
346
- },
347
- ];
348
- /**
349
- * Insecure Configuration Detection Patterns
350
- * OWASP A05:2021 - Security Misconfiguration
351
- */
352
- const MISCONFIGURATION_PATTERNS = [
353
- {
354
- id: 'misc-cors-wildcard',
355
- pattern: /cors\s*\(\s*\{[^}]*origin\s*:\s*['"]\*['"]/gi,
356
- category: 'security-misconfiguration',
357
- severity: 'medium',
358
- title: 'Permissive CORS Configuration',
359
- description: 'CORS allows all origins (*) which may expose sensitive data',
360
- owaspId: 'A05:2021',
361
- cweId: 'CWE-942',
362
- remediation: 'Restrict CORS to specific trusted origins',
363
- fixExample: 'cors({ origin: ["https://trusted-domain.com"] })',
364
- },
365
- {
366
- id: 'misc-debug-enabled',
367
- pattern: /debug\s*[:=]\s*true|DEBUG\s*[:=]\s*['"]?true['"]?/gi,
368
- category: 'security-misconfiguration',
369
- severity: 'low',
370
- title: 'Debug Mode Enabled',
371
- description: 'Debug mode may expose sensitive information in production',
372
- owaspId: 'A05:2021',
373
- cweId: 'CWE-489',
374
- remediation: 'Disable debug mode in production environments',
375
- },
376
- {
377
- id: 'misc-ssl-disabled',
378
- pattern: /rejectUnauthorized\s*:\s*false|NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*['"]?0['"]?/g,
379
- category: 'security-misconfiguration',
380
- severity: 'high',
381
- title: 'TLS Certificate Validation Disabled',
382
- description: 'Disabling TLS certificate validation exposes to MITM attacks',
383
- owaspId: 'A05:2021',
384
- cweId: 'CWE-295',
385
- remediation: 'Always enable TLS certificate validation in production',
386
- },
387
- {
388
- id: 'misc-helmet-missing',
389
- pattern: /app\.use\s*\(\s*express\s*\(\s*\)\s*\)/g,
390
- category: 'security-misconfiguration',
391
- severity: 'low',
392
- title: 'Express App Without Security Headers',
393
- description: 'Express app initialized without helmet or security headers',
394
- owaspId: 'A05:2021',
395
- cweId: 'CWE-693',
396
- remediation: 'Use helmet middleware for security headers',
397
- fixExample: 'app.use(helmet())',
398
- },
399
- ];
400
- /**
401
- * Insecure Deserialization Patterns
402
- * OWASP A08:2021 - Software and Data Integrity Failures
403
- */
404
- const DESERIALIZATION_PATTERNS = [
405
- {
406
- id: 'deser-yaml-load',
407
- pattern: /yaml\.load\s*\([^)]+\)/g,
408
- category: 'insecure-deserialization',
409
- severity: 'high',
410
- title: 'Unsafe YAML Deserialization',
411
- description: 'yaml.load() can execute arbitrary code from untrusted YAML',
412
- owaspId: 'A08:2021',
413
- cweId: 'CWE-502',
414
- remediation: 'Use yaml.safeLoad() or schema-constrained loading',
415
- fixExample: 'yaml.load(content, { schema: yaml.SAFE_SCHEMA })',
416
- },
417
- {
418
- id: 'deser-serialize-js',
419
- pattern: /serialize\s*\([^)]+\)|unserialize\s*\([^)]+\)/g,
420
- category: 'insecure-deserialization',
421
- severity: 'high',
422
- title: 'Unsafe Serialization Function',
423
- description: 'Node serialize/unserialize functions can execute arbitrary code',
424
- owaspId: 'A08:2021',
425
- cweId: 'CWE-502',
426
- remediation: 'Use JSON.parse/stringify for serialization',
427
- },
428
- ];
429
- /**
430
- * Authentication Weakness Patterns
431
- * OWASP A07:2021 - Identification and Authentication Failures
432
- */
433
- const AUTH_PATTERNS = [
434
- {
435
- id: 'auth-weak-jwt-secret',
436
- pattern: /jwt\.sign\s*\([^)]+,\s*['"][a-zA-Z0-9]{1,16}['"]/g,
437
- category: 'broken-auth',
438
- severity: 'high',
439
- title: 'Weak JWT Secret',
440
- description: 'JWT signed with a weak or short secret key',
441
- owaspId: 'A07:2021',
442
- cweId: 'CWE-327',
443
- remediation: 'Use a strong, randomly generated secret of at least 256 bits',
444
- },
445
- {
446
- id: 'auth-no-algorithm',
447
- pattern: /jwt\.verify\s*\([^)]+\)\s*(?!.*algorithm)/g,
448
- category: 'broken-auth',
449
- severity: 'medium',
450
- title: 'JWT Without Algorithm Specification',
451
- description: 'JWT verification without explicit algorithm can be exploited',
452
- owaspId: 'A07:2021',
453
- cweId: 'CWE-347',
454
- remediation: 'Always specify the expected algorithm in JWT verification',
455
- fixExample: 'jwt.verify(token, secret, { algorithms: ["HS256"] })',
456
- },
457
- ];
458
- /**
459
- * All security patterns combined by category for rule set application
460
- */
461
- const ALL_SECURITY_PATTERNS = [
462
- ...SQL_INJECTION_PATTERNS,
463
- ...XSS_PATTERNS,
464
- ...SECRET_PATTERNS,
465
- ...PATH_TRAVERSAL_PATTERNS,
466
- ...COMMAND_INJECTION_PATTERNS,
467
- ...MISCONFIGURATION_PATTERNS,
468
- ...DESERIALIZATION_PATTERNS,
469
- ...AUTH_PATTERNS,
470
- ];
471
- // ============================================================================
472
- // Built-in Rule Sets
473
- // ============================================================================
474
- const BUILT_IN_RULE_SETS = [
475
- {
476
- id: 'owasp-top-10',
477
- name: 'OWASP Top 10',
478
- description: 'OWASP Top 10 most critical security risks',
479
- ruleCount: 45,
480
- categories: [
481
- 'injection',
482
- 'broken-auth',
483
- 'sensitive-data',
484
- 'xxe',
485
- 'access-control',
486
- 'security-misconfiguration',
487
- 'xss',
488
- 'insecure-deserialization',
489
- 'vulnerable-components',
490
- 'insufficient-logging',
491
- ],
492
- },
493
- {
494
- id: 'cwe-sans-25',
495
- name: 'CWE/SANS Top 25',
496
- description: 'Most dangerous software errors',
497
- ruleCount: 38,
498
- categories: [
499
- 'injection',
500
- 'xss',
501
- 'access-control',
502
- 'sensitive-data',
503
- 'broken-auth',
504
- ],
505
- },
506
- {
507
- id: 'nodejs-security',
508
- name: 'Node.js Security',
509
- description: 'Node.js specific security rules',
510
- ruleCount: 25,
511
- categories: ['injection', 'xss', 'sensitive-data', 'security-misconfiguration'],
512
- },
513
- {
514
- id: 'typescript-security',
515
- name: 'TypeScript Security',
516
- description: 'TypeScript specific security rules',
517
- ruleCount: 20,
518
- categories: ['injection', 'xss', 'sensitive-data'],
519
- },
520
- ];
521
- // ============================================================================
522
- // Security Scanner Service Implementation
523
- // ============================================================================
524
- export class SecurityScannerService {
525
- config;
526
- activeScans = new Map();
527
- osvClient;
528
- memory;
529
- llmRouter;
530
- constructor(dependencies, config = {}) {
531
- this.config = { ...DEFAULT_CONFIG, ...config };
532
- this.osvClient = new OSVClient({ enableCache: true });
533
- // Support both old and new constructor signatures
534
- if ('memory' in dependencies) {
535
- this.memory = dependencies.memory;
536
- this.llmRouter = dependencies.llmRouter;
537
- }
538
- else {
539
- this.memory = dependencies;
540
- }
541
- }
542
- // ============================================================================
543
- // ADR-051: LLM Enhancement Methods
544
- // ============================================================================
545
- /**
546
- * Check if LLM analysis is available and enabled
547
- */
548
- isLLMAnalysisAvailable() {
549
- return this.config.enableLLMAnalysis && this.llmRouter !== undefined;
550
- }
551
- /**
552
- * Get model ID for the configured tier
553
- */
554
- getModelForTier(tier) {
555
- switch (tier) {
556
- case 1: return 'claude-3-5-haiku-20241022';
557
- case 2: return 'claude-sonnet-4-20250514';
558
- case 3: return 'claude-sonnet-4-20250514';
559
- case 4: return 'claude-opus-4-5-20251101';
560
- default: return 'claude-opus-4-5-20251101'; // Default to Opus for security
561
- }
562
- }
563
- /**
564
- * Analyze vulnerability with LLM for deeper insights
565
- * Provides context-aware remediation advice
566
- */
567
- async analyzeVulnerabilityWithLLM(vuln, codeContext) {
568
- if (!this.llmRouter) {
569
- return this.getDefaultRemediation(vuln);
570
- }
571
- try {
572
- const modelId = this.getModelForTier(this.config.llmModelTier);
573
- const response = await this.llmRouter.chat({
574
- messages: [
575
- {
576
- role: 'system',
577
- content: `You are a senior security engineer. Analyze the vulnerability and provide:
578
- 1. Detailed explanation of the risk
579
- 2. Code example showing the fix
580
- 3. Effort estimate (trivial/minor/moderate/major)
581
- 4. Whether it's automatable
582
- Be specific to the code context provided. Return JSON with: { "description": "", "fixExample": "", "estimatedEffort": "minor", "automatable": false }`,
583
- },
584
- {
585
- role: 'user',
586
- content: `Vulnerability: ${vuln.title} (${vuln.category})
587
- Severity: ${vuln.severity}
588
- Description: ${vuln.description}
589
-
590
- Code context:
591
- \`\`\`
592
- ${codeContext}
593
- \`\`\`
594
-
595
- Provide detailed remediation advice specific to this code.`,
596
- },
597
- ],
598
- model: modelId,
599
- maxTokens: 1500,
600
- temperature: 0.2, // Low temperature for accurate security advice
601
- });
602
- if (response.content) {
603
- try {
604
- const jsonMatch = response.content.match(/\{[\s\S]*\}/);
605
- if (jsonMatch) {
606
- const analysis = JSON.parse(jsonMatch[0]);
607
- return {
608
- description: analysis.description || vuln.remediation?.description || 'Review and fix the vulnerability',
609
- fixExample: analysis.fixExample || vuln.remediation?.fixExample,
610
- estimatedEffort: analysis.estimatedEffort || vuln.remediation?.estimatedEffort || 'moderate',
611
- automatable: analysis.automatable ?? vuln.remediation?.automatable ?? false,
612
- llmEnhanced: true,
613
- };
614
- }
615
- }
616
- catch {
617
- // JSON parse failed - use default
618
- }
619
- }
620
- }
621
- catch (error) {
622
- console.warn('[SecurityScanner] LLM analysis failed:', error);
623
- }
624
- return this.getDefaultRemediation(vuln);
625
- }
626
- /**
627
- * Get default remediation advice without LLM
628
- */
629
- getDefaultRemediation(vuln) {
630
- return vuln.remediation || {
631
- description: 'Review and fix the vulnerability following security best practices',
632
- estimatedEffort: 'moderate',
633
- automatable: false,
634
- };
635
- }
636
- // ==========================================================================
637
- // SAST Methods
638
- // ==========================================================================
639
- /**
640
- * Scan files for security vulnerabilities using static analysis
641
- */
642
- async scanFiles(files) {
643
- return this.scanWithRules(files, this.config.defaultRuleSets);
644
- }
645
- /**
646
- * Scan with specific rule sets
647
- */
648
- async scanWithRules(files, ruleSetIds) {
649
- const scanId = uuidv4();
650
- try {
651
- if (files.length === 0) {
652
- return err(new Error('No files provided for scanning'));
653
- }
654
- this.activeScans.set(scanId, 'running');
655
- const startTime = Date.now();
656
- // Get applicable rule sets
657
- const ruleSets = BUILT_IN_RULE_SETS.filter((rs) => ruleSetIds.includes(rs.id));
658
- if (ruleSets.length === 0) {
659
- return err(new Error(`No valid rule sets found: ${ruleSetIds.join(', ')}`));
660
- }
661
- // Perform static analysis on each file
662
- const vulnerabilities = [];
663
- let linesScanned = 0;
664
- for (const file of files) {
665
- const fileVulns = await this.analyzeFile(file, ruleSets);
666
- vulnerabilities.push(...fileVulns.vulnerabilities);
667
- linesScanned += fileVulns.linesScanned;
668
- }
669
- const scanDurationMs = Date.now() - startTime;
670
- // Calculate summary
671
- const summary = this.calculateSummary(vulnerabilities, files.length, scanDurationMs);
672
- // Calculate coverage
673
- const coverage = {
674
- filesScanned: files.length,
675
- linesScanned,
676
- rulesApplied: ruleSets.reduce((acc, rs) => acc + rs.ruleCount, 0),
677
- };
678
- // Store scan results in memory
679
- await this.storeScanResults(scanId, 'sast', vulnerabilities, summary);
680
- this.activeScans.set(scanId, 'completed');
681
- return ok({
682
- scanId,
683
- vulnerabilities,
684
- summary,
685
- coverage,
686
- });
687
- }
688
- catch (error) {
689
- this.activeScans.set(scanId, 'failed');
690
- return err(error instanceof Error ? error : new Error(String(error)));
691
- }
692
- }
693
- /**
694
- * Get available rule sets
695
- */
696
- async getAvailableRuleSets() {
697
- // Return built-in rule sets plus any custom ones from memory
698
- const customRuleSets = await this.memory.get('security:custom-rule-sets');
699
- return [...BUILT_IN_RULE_SETS, ...(customRuleSets || [])];
700
- }
701
- /**
702
- * Check if vulnerability is a false positive
703
- */
704
- async checkFalsePositive(vulnerability) {
705
- try {
706
- if (!this.config.enableFalsePositiveDetection) {
707
- return ok({
708
- isFalsePositive: false,
709
- confidence: 0,
710
- reason: 'False positive detection is disabled',
711
- });
712
- }
713
- // Analyze vulnerability using heuristics-based false positive detection
714
- const analysis = await this.analyzeFalsePositive(vulnerability);
715
- // Store the check result for learning
716
- await this.memory.set(`security:fp-check:${vulnerability.id}`, { vulnerability, analysis }, { namespace: 'security-compliance', ttl: 86400 * 30 } // 30 days
717
- );
718
- return ok(analysis);
719
- }
720
- catch (error) {
721
- return err(error instanceof Error ? error : new Error(String(error)));
722
- }
723
- }
724
- // ==========================================================================
725
- // DAST Methods
726
- // ==========================================================================
727
- /**
728
- * Scan running application using dynamic analysis
729
- */
730
- async scanUrl(targetUrl, options) {
731
- const scanId = uuidv4();
732
- try {
733
- this.activeScans.set(scanId, 'running');
734
- const startTime = Date.now();
735
- const mergedOptions = {
736
- maxDepth: options?.maxDepth ?? this.config.dastMaxDepth,
737
- activeScanning: options?.activeScanning ?? this.config.dastActiveScanning,
738
- timeout: options?.timeout ?? this.config.timeout,
739
- excludePatterns: options?.excludePatterns ?? [],
740
- };
741
- // Perform dynamic analysis
742
- const result = await this.performDynamicScan(targetUrl, mergedOptions);
743
- const scanDurationMs = Date.now() - startTime;
744
- const summary = this.calculateSummary(result.vulnerabilities, 1, scanDurationMs);
745
- // Store results
746
- await this.storeScanResults(scanId, 'dast', result.vulnerabilities, summary);
747
- this.activeScans.set(scanId, 'completed');
748
- return ok({
749
- scanId,
750
- targetUrl,
751
- vulnerabilities: result.vulnerabilities,
752
- summary,
753
- crawledUrls: result.crawledUrls,
754
- });
755
- }
756
- catch (error) {
757
- this.activeScans.set(scanId, 'failed');
758
- return err(error instanceof Error ? error : new Error(String(error)));
759
- }
760
- }
761
- /**
762
- * Scan authenticated endpoints
763
- */
764
- async scanAuthenticated(targetUrl, credentials, options) {
765
- const scanId = uuidv4();
766
- try {
767
- this.activeScans.set(scanId, 'running');
768
- const startTime = Date.now();
769
- // Validate credentials
770
- const credValidation = this.validateCredentials(credentials);
771
- if (!credValidation.valid) {
772
- return err(new Error(credValidation.reason));
773
- }
774
- const mergedOptions = {
775
- maxDepth: options?.maxDepth ?? this.config.dastMaxDepth,
776
- activeScanning: options?.activeScanning ?? this.config.dastActiveScanning,
777
- timeout: options?.timeout ?? this.config.timeout,
778
- excludePatterns: options?.excludePatterns ?? [],
779
- };
780
- // Perform authenticated dynamic analysis
781
- const result = await this.performAuthenticatedScan(targetUrl, credentials, mergedOptions);
782
- const scanDurationMs = Date.now() - startTime;
783
- const summary = this.calculateSummary(result.vulnerabilities, 1, scanDurationMs);
784
- // Store results (without credentials)
785
- await this.storeScanResults(scanId, 'dast-auth', result.vulnerabilities, summary);
786
- this.activeScans.set(scanId, 'completed');
787
- return ok({
788
- scanId,
789
- targetUrl,
790
- vulnerabilities: result.vulnerabilities,
791
- summary,
792
- crawledUrls: result.crawledUrls,
793
- });
794
- }
795
- catch (error) {
796
- this.activeScans.set(scanId, 'failed');
797
- return err(error instanceof Error ? error : new Error(String(error)));
798
- }
799
- }
800
- /**
801
- * Get scan status
802
- */
803
- async getScanStatus(scanId) {
804
- return this.activeScans.get(scanId) ?? 'pending';
805
- }
806
- // ==========================================================================
807
- // Combined Scanning
808
- // ==========================================================================
809
- /**
810
- * Run combined SAST and DAST scan
811
- */
812
- async runFullScan(files, targetUrl, options) {
813
- try {
814
- // Run SAST scan
815
- const sastResult = await this.scanWithRules(files, this.config.defaultRuleSets);
816
- if (sastResult.success === false) {
817
- return err(sastResult.error);
818
- }
819
- // Run DAST scan if target URL provided
820
- let dastResult;
821
- if (targetUrl) {
822
- const dastScan = await this.scanUrl(targetUrl, options);
823
- if (dastScan.success) {
824
- dastResult = dastScan.value;
825
- }
826
- // Don't fail the full scan if DAST fails
827
- }
828
- // Combine summaries
829
- const combinedSummary = this.combineSummaries(sastResult.value.summary, dastResult?.summary);
830
- return ok({
831
- sastResult: sastResult.value,
832
- dastResult,
833
- combinedSummary,
834
- });
835
- }
836
- catch (error) {
837
- return err(error instanceof Error ? error : new Error(String(error)));
838
- }
839
- }
840
- // ==========================================================================
841
- // Private Helper Methods
842
- // ==========================================================================
843
- /**
844
- * Analyze a file for security vulnerabilities using pattern-based detection
845
- */
846
- async analyzeFile(file, ruleSets) {
847
- const vulnerabilities = [];
848
- const filePath = file.value;
849
- const extension = file.extension;
850
- // Read file content
851
- let content;
852
- let lines;
853
- try {
854
- const fs = await import('fs/promises');
855
- content = await fs.readFile(filePath, 'utf-8');
856
- lines = content.split('\n');
857
- }
858
- catch (error) {
859
- // File not accessible - return empty results
860
- return { vulnerabilities: [], linesScanned: 0 };
861
- }
862
- const linesScanned = lines.length;
863
- // Only scan supported file types
864
- const supportedExtensions = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'];
865
- if (!supportedExtensions.includes(extension)) {
866
- return { vulnerabilities: [], linesScanned };
867
- }
868
- // Get applicable categories from rule sets
869
- const applicableCategories = new Set(ruleSets.flatMap((rs) => rs.categories));
870
- // Filter patterns to only those matching applicable categories
871
- const applicablePatterns = ALL_SECURITY_PATTERNS.filter((pattern) => applicableCategories.has(pattern.category));
872
- // Scan content for each pattern
873
- for (const securityPattern of applicablePatterns) {
874
- const matches = this.findPatternMatches(content, lines, securityPattern);
875
- for (const match of matches) {
876
- // Skip if in comments or string that looks like documentation
877
- if (this.isInComment(content, match.index) || this.isInDocumentation(match.snippet)) {
878
- continue;
879
- }
880
- // Skip nosec annotations
881
- if (this.hasNosecAnnotation(lines, match.line)) {
882
- continue;
883
- }
884
- vulnerabilities.push(this.createVulnerabilityFromPattern(securityPattern, filePath, match));
885
- }
886
- }
887
- return { vulnerabilities, linesScanned };
888
- }
889
- /**
890
- * Find all matches of a security pattern in the file content
891
- */
892
- findPatternMatches(content, lines, securityPattern) {
893
- const matches = [];
894
- // Reset regex state for global patterns
895
- const pattern = new RegExp(securityPattern.pattern.source, securityPattern.pattern.flags);
896
- let match;
897
- while ((match = pattern.exec(content)) !== null) {
898
- const index = match.index;
899
- const { line, column } = this.getLineAndColumn(content, index);
900
- // Extract snippet with context (the matched line plus surrounding context)
901
- const snippetLines = [];
902
- const startLine = Math.max(0, line - 2);
903
- const endLine = Math.min(lines.length - 1, line + 1);
904
- for (let i = startLine; i <= endLine; i++) {
905
- snippetLines.push(lines[i]);
906
- }
907
- const snippet = snippetLines.join('\n');
908
- matches.push({ index, line: line + 1, column: column + 1, snippet }); // 1-indexed
909
- }
910
- return matches;
911
- }
912
- /**
913
- * Convert character index to line and column numbers
914
- */
915
- getLineAndColumn(content, index) {
916
- const beforeMatch = content.substring(0, index);
917
- const lines = beforeMatch.split('\n');
918
- const line = lines.length - 1;
919
- const column = lines[lines.length - 1].length;
920
- return { line, column };
921
- }
922
- /**
923
- * Check if the match is inside a comment
924
- */
925
- isInComment(content, index) {
926
- const beforeMatch = content.substring(0, index);
927
- // Check for single-line comment
928
- const lastNewline = beforeMatch.lastIndexOf('\n');
929
- const currentLine = beforeMatch.substring(lastNewline + 1);
930
- if (currentLine.includes('//')) {
931
- const commentStart = currentLine.indexOf('//');
932
- if (index - (beforeMatch.length - currentLine.length) > commentStart) {
933
- return true;
934
- }
935
- }
936
- // Check for multi-line comment
937
- const lastBlockCommentStart = beforeMatch.lastIndexOf('/*');
938
- const lastBlockCommentEnd = beforeMatch.lastIndexOf('*/');
939
- if (lastBlockCommentStart > lastBlockCommentEnd) {
940
- return true;
941
- }
942
- return false;
943
- }
944
- /**
945
- * Check if the snippet appears to be in documentation or test code examples
946
- */
947
- isInDocumentation(snippet) {
948
- const docPatterns = [
949
- /\*\s*@example/i,
950
- /\*\s*@description/i,
951
- /\/\/\s*example:/i,
952
- /\/\/\s*e\.g\./i,
953
- /```[\s\S]*```/,
954
- ];
955
- return docPatterns.some((pattern) => pattern.test(snippet));
956
- }
957
- /**
958
- * Check if the line has a nosec annotation
959
- */
960
- hasNosecAnnotation(lines, lineNumber) {
961
- const lineIndex = lineNumber - 1;
962
- if (lineIndex < 0 || lineIndex >= lines.length) {
963
- return false;
964
- }
965
- const currentLine = lines[lineIndex];
966
- const previousLine = lineIndex > 0 ? lines[lineIndex - 1] : '';
967
- const nosecPatterns = [
968
- /\/\/\s*nosec/i,
969
- /\/\/\s*security-ignore/i,
970
- /\/\*\s*nosec\s*\*\//i,
971
- /#\s*nosec/i,
972
- ];
973
- return nosecPatterns.some((pattern) => pattern.test(currentLine) || pattern.test(previousLine));
974
- }
975
- /**
976
- * Create a Vulnerability object from a pattern match
977
- */
978
- createVulnerabilityFromPattern(pattern, file, match) {
979
- const location = {
980
- file,
981
- line: match.line,
982
- column: match.column,
983
- snippet: match.snippet,
984
- };
985
- const remediation = {
986
- description: pattern.remediation,
987
- fixExample: pattern.fixExample,
988
- estimatedEffort: this.getEffortForSeverity(pattern.severity),
989
- automatable: pattern.severity === 'low' || pattern.severity === 'informational',
990
- };
991
- return {
992
- id: uuidv4(),
993
- cveId: undefined,
994
- title: pattern.title,
995
- description: `${pattern.description} [${pattern.cweId}]`,
996
- severity: pattern.severity,
997
- category: pattern.category,
998
- location,
999
- remediation,
1000
- references: [
1001
- `https://owasp.org/Top10/${pattern.owaspId.replace(':', '_')}/`,
1002
- `https://cwe.mitre.org/data/definitions/${pattern.cweId.replace('CWE-', '')}.html`,
1003
- ],
1004
- };
1005
- }
1006
- getEffortForSeverity(severity) {
1007
- const efforts = {
1008
- critical: 'major',
1009
- high: 'moderate',
1010
- medium: 'minor',
1011
- low: 'trivial',
1012
- informational: 'trivial',
1013
- };
1014
- return efforts[severity];
1015
- }
1016
- /**
1017
- * Perform dynamic (DAST) scanning on a target URL
1018
- * Makes actual HTTP requests to detect security vulnerabilities
1019
- *
1020
- * **Capabilities:**
1021
- * - Security header analysis (HSTS, CSP, X-Frame-Options, etc.)
1022
- * - Cookie security (Secure, HttpOnly, SameSite flags)
1023
- * - CORS misconfiguration detection
1024
- * - Sensitive file exposure (/.git, /.env, etc.)
1025
- * - Link crawling with same-origin scope
1026
- * - XSS reflection testing (GET parameters)
1027
- * - SQL injection error-based detection (GET parameters)
1028
- * - Form security analysis (CSRF tokens, autocomplete, action URLs)
1029
- *
1030
- * **Limitations:**
1031
- * - Injection testing: GET parameters only (POST form submission not implemented)
1032
- * - Crawling: Same-origin only, max 10 links per page, single depth
1033
- * - Auth flows: Header-based only, no login form automation
1034
- * - No JavaScript execution (static response analysis only)
1035
- * - No session management testing beyond cookie attributes
1036
- */
1037
- async performDynamicScan(targetUrl, options) {
1038
- const vulnerabilities = [];
1039
- let crawledUrls = 0;
1040
- try {
1041
- // Validate and parse URL
1042
- let parsedUrl;
1043
- try {
1044
- parsedUrl = new URL(targetUrl);
1045
- }
1046
- catch {
1047
- // Invalid URL - return informational finding
1048
- vulnerabilities.push({
1049
- id: uuidv4(),
1050
- title: 'Invalid Target URL',
1051
- description: 'The provided target URL is not valid',
1052
- severity: 'informational',
1053
- category: 'security-misconfiguration',
1054
- location: { file: targetUrl },
1055
- remediation: { description: 'Provide a valid URL', estimatedEffort: 'trivial', automatable: false },
1056
- references: [],
1057
- });
1058
- return { vulnerabilities, crawledUrls: 0 };
1059
- }
1060
- const timeout = options.timeout ?? this.config.timeout;
1061
- const maxDepth = options.maxDepth ?? this.config.dastMaxDepth;
1062
- // Perform main page scan
1063
- const controller = new AbortController();
1064
- const timeoutId = setTimeout(() => controller.abort(), Math.min(timeout, 30000));
1065
- try {
1066
- const response = await fetch(targetUrl, {
1067
- method: 'GET',
1068
- headers: {
1069
- 'User-Agent': 'AgenticQE-DAST-Scanner/3.0',
1070
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
1071
- },
1072
- signal: controller.signal,
1073
- redirect: 'follow',
1074
- });
1075
- clearTimeout(timeoutId);
1076
- crawledUrls++;
1077
- // Security header analysis
1078
- const headers = response.headers;
1079
- // Check for missing security headers
1080
- const headerChecks = [
1081
- { header: 'strict-transport-security', title: 'Missing HSTS Header', severity: 'medium', remediation: 'Add Strict-Transport-Security header' },
1082
- { header: 'x-content-type-options', title: 'Missing X-Content-Type-Options', severity: 'low', remediation: 'Add X-Content-Type-Options: nosniff' },
1083
- { header: 'x-frame-options', title: 'Missing X-Frame-Options', severity: 'medium', remediation: 'Add X-Frame-Options: DENY or SAMEORIGIN' },
1084
- { header: 'content-security-policy', title: 'Missing Content-Security-Policy', severity: 'medium', remediation: 'Implement a Content-Security-Policy' },
1085
- { header: 'referrer-policy', title: 'Missing Referrer-Policy', severity: 'low', remediation: 'Add Referrer-Policy header' },
1086
- { header: 'permissions-policy', title: 'Missing Permissions-Policy', severity: 'low', remediation: 'Add Permissions-Policy header' },
1087
- ];
1088
- for (const check of headerChecks) {
1089
- if (!headers.get(check.header)) {
1090
- vulnerabilities.push({
1091
- id: uuidv4(),
1092
- title: check.title,
1093
- description: `Security header ${check.header} is not present in the response`,
1094
- severity: check.severity,
1095
- category: 'security-misconfiguration',
1096
- location: { file: targetUrl, snippet: `Missing: ${check.header}` },
1097
- remediation: { description: check.remediation, estimatedEffort: 'minor', automatable: true },
1098
- references: ['https://owasp.org/www-project-secure-headers/'],
1099
- });
1100
- }
1101
- }
1102
- // Check for insecure protocol
1103
- if (parsedUrl.protocol === 'http:') {
1104
- vulnerabilities.push({
1105
- id: uuidv4(),
1106
- title: 'Insecure HTTP Protocol',
1107
- description: 'Application is accessible over unencrypted HTTP',
1108
- severity: 'high',
1109
- category: 'sensitive-data',
1110
- location: { file: targetUrl },
1111
- remediation: { description: 'Redirect all HTTP traffic to HTTPS', estimatedEffort: 'moderate', automatable: false },
1112
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1113
- });
1114
- }
1115
- // Check for cookie security
1116
- const setCookie = headers.get('set-cookie');
1117
- if (setCookie) {
1118
- const cookieLower = setCookie.toLowerCase();
1119
- if (!cookieLower.includes('secure')) {
1120
- vulnerabilities.push({
1121
- id: uuidv4(),
1122
- title: 'Cookie Missing Secure Flag',
1123
- description: 'Cookie is set without the Secure attribute',
1124
- severity: 'medium',
1125
- category: 'sensitive-data',
1126
- location: { file: targetUrl, snippet: `Set-Cookie header without Secure flag` },
1127
- remediation: { description: 'Add Secure flag to all cookies', estimatedEffort: 'trivial', automatable: true },
1128
- references: ['https://owasp.org/www-community/controls/SecureCookieAttribute'],
1129
- });
1130
- }
1131
- if (!cookieLower.includes('httponly')) {
1132
- vulnerabilities.push({
1133
- id: uuidv4(),
1134
- title: 'Cookie Missing HttpOnly Flag',
1135
- description: 'Cookie is accessible to client-side JavaScript',
1136
- severity: 'medium',
1137
- category: 'sensitive-data',
1138
- location: { file: targetUrl, snippet: `Set-Cookie header without HttpOnly flag` },
1139
- remediation: { description: 'Add HttpOnly flag to session cookies', estimatedEffort: 'trivial', automatable: true },
1140
- references: ['https://owasp.org/www-community/HttpOnly'],
1141
- });
1142
- }
1143
- }
1144
- // Check for server version disclosure
1145
- const serverHeader = headers.get('server') || headers.get('x-powered-by');
1146
- if (serverHeader && /\d+\.\d+/.test(serverHeader)) {
1147
- vulnerabilities.push({
1148
- id: uuidv4(),
1149
- title: 'Server Version Disclosure',
1150
- description: `Server version information exposed: ${serverHeader}`,
1151
- severity: 'low',
1152
- category: 'security-misconfiguration',
1153
- location: { file: targetUrl, snippet: `Server: ${serverHeader}` },
1154
- remediation: { description: 'Remove or obfuscate server version headers', estimatedEffort: 'trivial', automatable: true },
1155
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1156
- });
1157
- }
1158
- // Scan for sensitive file exposure (only if active scanning is enabled)
1159
- if (options.activeScanning ?? this.config.dastActiveScanning) {
1160
- const sensitiveEndpoints = [
1161
- { path: '/.git/config', name: 'Git Configuration' },
1162
- { path: '/.env', name: 'Environment File' },
1163
- { path: '/robots.txt', name: 'Robots.txt' },
1164
- { path: '/sitemap.xml', name: 'Sitemap' },
1165
- { path: '/.htaccess', name: 'htaccess File' },
1166
- { path: '/web.config', name: 'IIS Configuration' },
1167
- ];
1168
- for (const endpoint of sensitiveEndpoints) {
1169
- if (crawledUrls >= maxDepth * 10)
1170
- break; // Limit crawling
1171
- try {
1172
- const testUrl = new URL(endpoint.path, parsedUrl.origin).toString();
1173
- const testResponse = await fetch(testUrl, {
1174
- method: 'GET',
1175
- signal: AbortSignal.timeout(5000),
1176
- });
1177
- if (testResponse.ok) {
1178
- crawledUrls++;
1179
- const contentType = testResponse.headers.get('content-type') || '';
1180
- const text = await testResponse.text();
1181
- // Verify it's not a custom 404 page
1182
- if (text.length > 20 && !text.toLowerCase().includes('not found') && !text.toLowerCase().includes('404')) {
1183
- // Check for sensitive content markers
1184
- const isSensitive = endpoint.path.includes('.git') ||
1185
- endpoint.path.includes('.env') ||
1186
- endpoint.path.includes('.htaccess') ||
1187
- endpoint.path.includes('web.config');
1188
- if (isSensitive) {
1189
- vulnerabilities.push({
1190
- id: uuidv4(),
1191
- title: `Sensitive File Exposed: ${endpoint.name}`,
1192
- description: `${endpoint.name} is publicly accessible`,
1193
- severity: endpoint.path.includes('.git') || endpoint.path.includes('.env') ? 'high' : 'medium',
1194
- category: 'sensitive-data',
1195
- location: { file: testUrl },
1196
- remediation: { description: `Restrict access to ${endpoint.path}`, estimatedEffort: 'trivial', automatable: true },
1197
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1198
- });
1199
- }
1200
- }
1201
- }
1202
- }
1203
- catch {
1204
- // File not accessible - this is expected/good
1205
- }
1206
- }
1207
- }
1208
- // CORS analysis
1209
- try {
1210
- const corsResponse = await fetch(targetUrl, {
1211
- method: 'OPTIONS',
1212
- headers: {
1213
- 'Origin': 'https://evil-attacker.com',
1214
- 'Access-Control-Request-Method': 'GET',
1215
- },
1216
- signal: AbortSignal.timeout(5000),
1217
- });
1218
- const allowOrigin = corsResponse.headers.get('access-control-allow-origin');
1219
- if (allowOrigin === '*' || allowOrigin === 'https://evil-attacker.com') {
1220
- vulnerabilities.push({
1221
- id: uuidv4(),
1222
- title: 'Overly Permissive CORS Policy',
1223
- description: allowOrigin === '*' ? 'CORS allows all origins' : 'CORS reflects arbitrary origin',
1224
- severity: 'medium',
1225
- category: 'access-control',
1226
- location: { file: targetUrl, snippet: `Access-Control-Allow-Origin: ${allowOrigin}` },
1227
- remediation: { description: 'Restrict CORS to specific trusted origins', estimatedEffort: 'minor', automatable: false },
1228
- references: ['https://owasp.org/www-community/attacks/CORS_OriginHeaderScrutiny'],
1229
- });
1230
- }
1231
- }
1232
- catch {
1233
- // OPTIONS request failed - CORS might be properly restricted
1234
- }
1235
- // ============================================================
1236
- // ENHANCED DAST: Link crawling, injection testing, form analysis
1237
- // ============================================================
1238
- if (options.activeScanning ?? this.config.dastActiveScanning) {
1239
- const responseText = await response.clone().text();
1240
- // 1. LINK CRAWLING - Extract and scan discovered links
1241
- const discoveredUrls = await this.extractAndCrawlLinks(responseText, parsedUrl, crawledUrls, maxDepth, vulnerabilities);
1242
- crawledUrls = discoveredUrls;
1243
- // 2. INJECTION TESTING - Test URL parameters for XSS/SQLi
1244
- if (parsedUrl.search) {
1245
- await this.testInjectionVulnerabilities(targetUrl, parsedUrl, vulnerabilities);
1246
- }
1247
- // 3. FORM DISCOVERY - Analyze forms for security issues
1248
- await this.analyzeFormsForSecurityIssues(responseText, targetUrl, vulnerabilities);
1249
- }
1250
- }
1251
- catch (fetchError) {
1252
- clearTimeout(timeoutId);
1253
- const errorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
1254
- if (errorMsg.includes('CERT') || errorMsg.includes('SSL') || errorMsg.includes('TLS') || errorMsg.includes('certificate')) {
1255
- vulnerabilities.push({
1256
- id: uuidv4(),
1257
- title: 'TLS Certificate Error',
1258
- description: `SSL/TLS error: ${errorMsg}`,
1259
- severity: 'high',
1260
- category: 'security-misconfiguration',
1261
- location: { file: targetUrl },
1262
- remediation: { description: 'Fix TLS certificate configuration', estimatedEffort: 'moderate', automatable: false },
1263
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1264
- });
1265
- }
1266
- else if (errorMsg.includes('timeout') || errorMsg.includes('abort')) {
1267
- vulnerabilities.push({
1268
- id: uuidv4(),
1269
- title: 'Connection Timeout',
1270
- description: `Target did not respond within timeout: ${errorMsg}`,
1271
- severity: 'informational',
1272
- category: 'security-misconfiguration',
1273
- location: { file: targetUrl },
1274
- remediation: { description: 'Verify target is accessible', estimatedEffort: 'trivial', automatable: false },
1275
- references: [],
1276
- });
1277
- }
1278
- }
1279
- }
1280
- catch (error) {
1281
- console.error('DAST scan error:', error);
1282
- }
1283
- return { vulnerabilities, crawledUrls };
1284
- }
1285
- /**
1286
- * Perform authenticated dynamic scanning with credentials
1287
- * Supports basic auth, bearer token, OAuth, and cookie-based authentication
1288
- */
1289
- async performAuthenticatedScan(targetUrl, credentials, options) {
1290
- const vulnerabilities = [];
1291
- let crawledUrls = 0;
1292
- try {
1293
- // Build authentication headers based on credential type
1294
- const authHeaders = {};
1295
- switch (credentials.type) {
1296
- case 'basic':
1297
- if (credentials.username && credentials.password) {
1298
- const encoded = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
1299
- authHeaders['Authorization'] = `Basic ${encoded}`;
1300
- }
1301
- break;
1302
- case 'bearer':
1303
- case 'oauth':
1304
- if (credentials.token) {
1305
- authHeaders['Authorization'] = `Bearer ${credentials.token}`;
1306
- }
1307
- break;
1308
- case 'cookie':
1309
- if (credentials.token) {
1310
- authHeaders['Cookie'] = credentials.token;
1311
- }
1312
- break;
1313
- }
1314
- // Validate and parse URL
1315
- let parsedUrl;
1316
- try {
1317
- parsedUrl = new URL(targetUrl);
1318
- }
1319
- catch {
1320
- vulnerabilities.push({
1321
- id: uuidv4(),
1322
- title: 'Invalid Target URL',
1323
- description: 'The provided target URL is not valid',
1324
- severity: 'informational',
1325
- category: 'security-misconfiguration',
1326
- location: { file: targetUrl },
1327
- remediation: { description: 'Provide a valid URL', estimatedEffort: 'trivial', automatable: false },
1328
- references: [],
1329
- });
1330
- return { vulnerabilities, crawledUrls: 0 };
1331
- }
1332
- const timeout = options.timeout ?? this.config.timeout;
1333
- const maxDepth = options.maxDepth ?? this.config.dastMaxDepth;
1334
- // Test authentication by making an authenticated request
1335
- const controller = new AbortController();
1336
- const timeoutId = setTimeout(() => controller.abort(), Math.min(timeout, 30000));
1337
- try {
1338
- const response = await fetch(targetUrl, {
1339
- method: 'GET',
1340
- headers: {
1341
- 'User-Agent': 'AgenticQE-DAST-Scanner/3.0',
1342
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
1343
- ...authHeaders,
1344
- },
1345
- signal: controller.signal,
1346
- redirect: 'follow',
1347
- });
1348
- clearTimeout(timeoutId);
1349
- crawledUrls++;
1350
- // Check if authentication was successful
1351
- if (response.status === 401 || response.status === 403) {
1352
- vulnerabilities.push({
1353
- id: uuidv4(),
1354
- title: 'Authentication Failed',
1355
- description: `Authentication returned ${response.status} status`,
1356
- severity: 'informational',
1357
- category: 'broken-auth',
1358
- location: { file: targetUrl },
1359
- remediation: { description: 'Verify credentials are correct', estimatedEffort: 'trivial', automatable: false },
1360
- references: [],
1361
- });
1362
- }
1363
- // Perform all the standard header checks from unauthenticated scan
1364
- const headers = response.headers;
1365
- // Security header analysis (same as unauthenticated)
1366
- const headerChecks = [
1367
- { header: 'strict-transport-security', title: 'Missing HSTS Header', severity: 'medium' },
1368
- { header: 'x-content-type-options', title: 'Missing X-Content-Type-Options', severity: 'low' },
1369
- { header: 'x-frame-options', title: 'Missing X-Frame-Options', severity: 'medium' },
1370
- { header: 'content-security-policy', title: 'Missing Content-Security-Policy', severity: 'medium' },
1371
- ];
1372
- for (const check of headerChecks) {
1373
- if (!headers.get(check.header)) {
1374
- vulnerabilities.push({
1375
- id: uuidv4(),
1376
- title: check.title,
1377
- description: `Security header ${check.header} is not present`,
1378
- severity: check.severity,
1379
- category: 'security-misconfiguration',
1380
- location: { file: targetUrl, snippet: `Missing: ${check.header}` },
1381
- remediation: { description: `Add ${check.header} header`, estimatedEffort: 'minor', automatable: true },
1382
- references: ['https://owasp.org/www-project-secure-headers/'],
1383
- });
1384
- }
1385
- }
1386
- // Check for session token in URL (authenticated-specific)
1387
- if (parsedUrl.search.includes('token=') || parsedUrl.search.includes('session=') || parsedUrl.search.includes('auth=')) {
1388
- vulnerabilities.push({
1389
- id: uuidv4(),
1390
- title: 'Session Token in URL',
1391
- description: 'Authentication token appears in URL query string',
1392
- severity: 'high',
1393
- category: 'sensitive-data',
1394
- location: { file: targetUrl, snippet: parsedUrl.search.substring(0, 50) },
1395
- remediation: { description: 'Send tokens in headers or request body, not URL', estimatedEffort: 'moderate', automatable: false },
1396
- references: ['https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url'],
1397
- });
1398
- }
1399
- // Test for authorization bypass on protected endpoints (authenticated-specific)
1400
- if (options.activeScanning ?? this.config.dastActiveScanning) {
1401
- const protectedEndpoints = [
1402
- '/admin',
1403
- '/dashboard',
1404
- '/api/users',
1405
- '/api/admin',
1406
- '/settings',
1407
- '/profile',
1408
- ];
1409
- for (const endpoint of protectedEndpoints) {
1410
- if (crawledUrls >= maxDepth * 15)
1411
- break;
1412
- try {
1413
- const testUrl = new URL(endpoint, parsedUrl.origin).toString();
1414
- // First, try with authentication
1415
- const authResponse = await fetch(testUrl, {
1416
- method: 'GET',
1417
- headers: { ...authHeaders },
1418
- signal: AbortSignal.timeout(5000),
1419
- });
1420
- if (authResponse.ok) {
1421
- crawledUrls++;
1422
- // Now try without authentication
1423
- const unauthResponse = await fetch(testUrl, {
1424
- method: 'GET',
1425
- signal: AbortSignal.timeout(5000),
1426
- });
1427
- // If both succeed, there might be missing authentication
1428
- if (unauthResponse.ok && unauthResponse.status === 200) {
1429
- const authText = await authResponse.text();
1430
- const unauthText = await unauthResponse.text();
1431
- // Check if the responses are similar (both returning actual content)
1432
- if (authText.length > 100 && unauthText.length > 100 &&
1433
- Math.abs(authText.length - unauthText.length) < authText.length * 0.1) {
1434
- vulnerabilities.push({
1435
- id: uuidv4(),
1436
- title: 'Missing Authentication on Protected Endpoint',
1437
- description: `Endpoint ${endpoint} is accessible without authentication`,
1438
- severity: 'high',
1439
- category: 'broken-auth',
1440
- location: { file: testUrl },
1441
- remediation: { description: 'Implement proper authentication checks', estimatedEffort: 'moderate', automatable: false },
1442
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1443
- });
1444
- }
1445
- }
1446
- }
1447
- }
1448
- catch {
1449
- // Endpoint not accessible - expected
1450
- }
1451
- }
1452
- // Test for Insecure Direct Object References (IDOR)
1453
- const idorEndpoints = [
1454
- '/api/users/1',
1455
- '/api/users/2',
1456
- '/api/orders/1',
1457
- '/profile/1',
1458
- ];
1459
- for (const endpoint of idorEndpoints.slice(0, 2)) {
1460
- if (crawledUrls >= maxDepth * 15)
1461
- break;
1462
- try {
1463
- const testUrl = new URL(endpoint, parsedUrl.origin).toString();
1464
- const response = await fetch(testUrl, {
1465
- method: 'GET',
1466
- headers: { ...authHeaders },
1467
- signal: AbortSignal.timeout(5000),
1468
- });
1469
- if (response.ok) {
1470
- crawledUrls++;
1471
- const text = await response.text();
1472
- // If we can access other users' data, that's an IDOR
1473
- if (text.includes('email') || text.includes('password') || text.includes('phone')) {
1474
- vulnerabilities.push({
1475
- id: uuidv4(),
1476
- title: 'Potential Insecure Direct Object Reference (IDOR)',
1477
- description: `Endpoint ${endpoint} may expose other users' data`,
1478
- severity: 'high',
1479
- category: 'access-control',
1480
- location: { file: testUrl },
1481
- remediation: { description: 'Implement proper authorization checks for resource access', estimatedEffort: 'moderate', automatable: false },
1482
- references: ['https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/05-Authorization_Testing/04-Testing_for_Insecure_Direct_Object_References'],
1483
- });
1484
- }
1485
- }
1486
- }
1487
- catch {
1488
- // Endpoint not accessible
1489
- }
1490
- }
1491
- }
1492
- // Cookie security checks
1493
- const setCookie = headers.get('set-cookie');
1494
- if (setCookie) {
1495
- const cookieLower = setCookie.toLowerCase();
1496
- if (!cookieLower.includes('secure')) {
1497
- vulnerabilities.push({
1498
- id: uuidv4(),
1499
- title: 'Session Cookie Missing Secure Flag',
1500
- description: 'Authenticated session cookie is not marked as Secure',
1501
- severity: 'high', // Higher severity for authenticated sessions
1502
- category: 'sensitive-data',
1503
- location: { file: targetUrl },
1504
- remediation: { description: 'Add Secure flag to session cookies', estimatedEffort: 'trivial', automatable: true },
1505
- references: ['https://owasp.org/www-community/controls/SecureCookieAttribute'],
1506
- });
1507
- }
1508
- if (!cookieLower.includes('httponly')) {
1509
- vulnerabilities.push({
1510
- id: uuidv4(),
1511
- title: 'Session Cookie Missing HttpOnly Flag',
1512
- description: 'Session cookie is accessible to JavaScript',
1513
- severity: 'high',
1514
- category: 'sensitive-data',
1515
- location: { file: targetUrl },
1516
- remediation: { description: 'Add HttpOnly flag to session cookies', estimatedEffort: 'trivial', automatable: true },
1517
- references: ['https://owasp.org/www-community/HttpOnly'],
1518
- });
1519
- }
1520
- }
1521
- }
1522
- catch (fetchError) {
1523
- clearTimeout(timeoutId);
1524
- const errorMsg = fetchError instanceof Error ? fetchError.message : String(fetchError);
1525
- if (errorMsg.includes('CERT') || errorMsg.includes('SSL') || errorMsg.includes('TLS')) {
1526
- vulnerabilities.push({
1527
- id: uuidv4(),
1528
- title: 'TLS Certificate Error',
1529
- description: `SSL/TLS error during authenticated scan: ${errorMsg}`,
1530
- severity: 'high',
1531
- category: 'security-misconfiguration',
1532
- location: { file: targetUrl },
1533
- remediation: { description: 'Fix TLS certificate configuration', estimatedEffort: 'moderate', automatable: false },
1534
- references: [],
1535
- });
1536
- }
1537
- }
1538
- }
1539
- catch (error) {
1540
- console.error('Authenticated DAST scan error:', error);
1541
- }
1542
- return { vulnerabilities, crawledUrls };
1543
- }
1544
- validateCredentials(credentials) {
1545
- switch (credentials.type) {
1546
- case 'basic':
1547
- if (!credentials.username || !credentials.password) {
1548
- return { valid: false, reason: 'Basic auth requires username and password' };
1549
- }
1550
- break;
1551
- case 'bearer':
1552
- case 'oauth':
1553
- if (!credentials.token) {
1554
- return { valid: false, reason: 'Bearer/OAuth auth requires token' };
1555
- }
1556
- break;
1557
- case 'cookie':
1558
- if (!credentials.token) {
1559
- return { valid: false, reason: 'Cookie auth requires session cookie' };
1560
- }
1561
- break;
1562
- }
1563
- return { valid: true };
1564
- }
1565
- /**
1566
- * Analyze if a vulnerability detection is a false positive using heuristics
1567
- * Future enhancement: integrate ML/AI models for improved false positive detection
1568
- */
1569
- async analyzeFalsePositive(vulnerability) {
1570
- let isFalsePositive = false;
1571
- let confidence = 0.5;
1572
- let reason = 'Manual review recommended';
1573
- // Check for common false positive patterns
1574
- if (vulnerability.severity === 'informational') {
1575
- confidence = 0.3;
1576
- reason = 'Low severity findings often require manual verification';
1577
- }
1578
- if (vulnerability.location.snippet?.includes('test') ||
1579
- vulnerability.location.file.includes('test')) {
1580
- isFalsePositive = true;
1581
- confidence = 0.8;
1582
- reason = 'Vulnerability found in test code';
1583
- }
1584
- if (vulnerability.location.snippet?.includes('// nosec')) {
1585
- isFalsePositive = true;
1586
- confidence = 0.95;
1587
- reason = 'Explicitly marked as ignored with nosec comment';
1588
- }
1589
- return { isFalsePositive, confidence, reason };
1590
- }
1591
- calculateSummary(vulnerabilities, totalFiles, scanDurationMs) {
1592
- const summary = {
1593
- critical: 0,
1594
- high: 0,
1595
- medium: 0,
1596
- low: 0,
1597
- informational: 0,
1598
- totalFiles,
1599
- scanDurationMs,
1600
- };
1601
- for (const vuln of vulnerabilities) {
1602
- summary[vuln.severity]++;
1603
- }
1604
- return summary;
1605
- }
1606
- combineSummaries(sast, dast) {
1607
- if (!dast)
1608
- return sast;
1609
- return {
1610
- critical: sast.critical + dast.critical,
1611
- high: sast.high + dast.high,
1612
- medium: sast.medium + dast.medium,
1613
- low: sast.low + dast.low,
1614
- informational: sast.informational + dast.informational,
1615
- totalFiles: sast.totalFiles + dast.totalFiles,
1616
- scanDurationMs: sast.scanDurationMs + dast.scanDurationMs,
1617
- };
1618
- }
1619
- async storeScanResults(scanId, scanType, vulnerabilities, summary) {
1620
- await this.memory.set(`security:scan:${scanId}`, {
1621
- scanId,
1622
- scanType,
1623
- vulnerabilities,
1624
- summary,
1625
- timestamp: new Date().toISOString(),
1626
- }, { namespace: 'security-compliance', ttl: 86400 * 7 } // 7 days
1627
- );
1628
- }
1629
- // ==========================================================================
1630
- // Dependency Scanning Methods (OSV API)
1631
- // ==========================================================================
1632
- /**
1633
- * Scan npm dependencies for known vulnerabilities using OSV API
1634
- */
1635
- async scanDependencies(dependencies) {
1636
- const scanId = uuidv4();
1637
- const startTime = Date.now();
1638
- try {
1639
- if (Object.keys(dependencies).length === 0) {
1640
- return err(new Error('No dependencies provided for scanning'));
1641
- }
1642
- this.activeScans.set(scanId, 'running');
1643
- // Query OSV for vulnerabilities
1644
- const osvVulns = await this.osvClient.scanNpmDependencies(dependencies);
1645
- // Convert OSV vulnerabilities to our format
1646
- const vulnerabilities = this.convertOSVVulnerabilities(osvVulns);
1647
- const scanDurationMs = Date.now() - startTime;
1648
- // Calculate unique vulnerable packages
1649
- const vulnerablePackageNames = new Set(osvVulns.map((v) => v.affectedPackage));
1650
- // Calculate summary
1651
- const summary = this.calculateSummary(vulnerabilities, Object.keys(dependencies).length, scanDurationMs);
1652
- // Store scan results
1653
- await this.storeScanResults(scanId, 'dependency', vulnerabilities, summary);
1654
- this.activeScans.set(scanId, 'completed');
1655
- return ok({
1656
- scanId,
1657
- vulnerabilities,
1658
- packagesScanned: Object.keys(dependencies).length,
1659
- vulnerablePackages: vulnerablePackageNames.size,
1660
- summary,
1661
- scanDurationMs,
1662
- });
1663
- }
1664
- catch (error) {
1665
- this.activeScans.set(scanId, 'failed');
1666
- return err(error instanceof Error ? error : new Error(String(error)));
1667
- }
1668
- }
1669
- /**
1670
- * Scan a package.json file for dependency vulnerabilities
1671
- */
1672
- async scanPackageJson(packageJsonPath) {
1673
- try {
1674
- const fs = await import('fs/promises');
1675
- const content = await fs.readFile(packageJsonPath, 'utf-8');
1676
- const packageJson = JSON.parse(content);
1677
- // Combine all dependency types
1678
- const allDependencies = {
1679
- ...(packageJson.dependencies || {}),
1680
- ...(packageJson.devDependencies || {}),
1681
- ...(packageJson.peerDependencies || {}),
1682
- ...(packageJson.optionalDependencies || {}),
1683
- };
1684
- if (Object.keys(allDependencies).length === 0) {
1685
- return err(new Error('No dependencies found in package.json'));
1686
- }
1687
- return this.scanDependencies(allDependencies);
1688
- }
1689
- catch (error) {
1690
- if (error instanceof SyntaxError) {
1691
- return err(new Error(`Invalid JSON in package.json: ${error.message}`));
1692
- }
1693
- return err(error instanceof Error ? error : new Error(String(error)));
1694
- }
1695
- }
1696
- /**
1697
- * Convert OSV vulnerabilities to our internal format
1698
- */
1699
- convertOSVVulnerabilities(osvVulns) {
1700
- return osvVulns.map((osv) => {
1701
- const location = {
1702
- file: 'package.json',
1703
- line: 1,
1704
- column: 1,
1705
- snippet: `"${osv.affectedPackage}": "..."`,
1706
- };
1707
- const remediation = {
1708
- description: osv.fixedVersions.length > 0
1709
- ? `Update to version ${osv.fixedVersions[0]} or later`
1710
- : 'No fixed version available; consider alternative packages',
1711
- fixExample: osv.fixedVersions.length > 0
1712
- ? `npm install ${osv.affectedPackage}@${osv.fixedVersions[0]}`
1713
- : undefined,
1714
- estimatedEffort: 'minor',
1715
- automatable: true,
1716
- };
1717
- return {
1718
- id: uuidv4(),
1719
- cveId: osv.cveIds[0],
1720
- title: `${osv.affectedPackage}: ${osv.summary.substring(0, 80)}`,
1721
- description: osv.details || osv.summary,
1722
- severity: this.mapOSVSeverity(osv.severity),
1723
- category: 'dependencies',
1724
- location,
1725
- remediation,
1726
- references: osv.references.slice(0, 5),
1727
- };
1728
- });
1729
- }
1730
- /**
1731
- * Map OSV severity to our severity type
1732
- */
1733
- mapOSVSeverity(osvSeverity) {
1734
- const mapping = {
1735
- critical: 'critical',
1736
- high: 'high',
1737
- medium: 'medium',
1738
- low: 'low',
1739
- unknown: 'medium',
1740
- };
1741
- return mapping[osvSeverity];
1742
- }
1743
- // ============================================================
1744
- // ENHANCED DAST METHODS
1745
- // ============================================================
1746
- /**
1747
- * Extract links from HTML and crawl discovered pages
1748
- * Implements basic web crawling within same origin
1749
- */
1750
- async extractAndCrawlLinks(html, baseUrl, currentCrawled, maxDepth, vulnerabilities) {
1751
- let crawledUrls = currentCrawled;
1752
- const maxCrawl = maxDepth * 5; // Allow more pages based on depth
1753
- // Extract links from HTML using regex (no DOM parser needed)
1754
- const linkPattern = /href=["']([^"']+)["']/gi;
1755
- const discoveredLinks = new Set();
1756
- let match;
1757
- while ((match = linkPattern.exec(html)) !== null) {
1758
- const href = match[1];
1759
- // Only follow same-origin links
1760
- try {
1761
- const linkUrl = new URL(href, baseUrl.origin);
1762
- if (linkUrl.origin === baseUrl.origin && !discoveredLinks.has(linkUrl.pathname)) {
1763
- discoveredLinks.add(linkUrl.pathname);
1764
- }
1765
- }
1766
- catch {
1767
- // Invalid URL - skip
1768
- }
1769
- }
1770
- // Crawl discovered links (limited)
1771
- const linksToCrawl = Array.from(discoveredLinks).slice(0, Math.min(10, maxCrawl - crawledUrls));
1772
- for (const path of linksToCrawl) {
1773
- if (crawledUrls >= maxCrawl)
1774
- break;
1775
- try {
1776
- const crawlUrl = new URL(path, baseUrl.origin).toString();
1777
- const crawlResponse = await fetch(crawlUrl, {
1778
- method: 'GET',
1779
- headers: { 'User-Agent': 'AgenticQE-DAST-Scanner/3.0' },
1780
- signal: AbortSignal.timeout(5000),
1781
- redirect: 'follow',
1782
- });
1783
- crawledUrls++;
1784
- // Check for security issues on crawled pages
1785
- if (crawlResponse.ok) {
1786
- // Check for sensitive data exposure in URLs
1787
- if (path.includes('password') || path.includes('token') || path.includes('api_key')) {
1788
- vulnerabilities.push({
1789
- id: uuidv4(),
1790
- title: 'Sensitive Data in URL Path',
1791
- description: `URL path may contain sensitive parameter names: ${path}`,
1792
- severity: 'medium',
1793
- category: 'sensitive-data',
1794
- location: { file: crawlUrl },
1795
- remediation: { description: 'Avoid sensitive data in URL paths', estimatedEffort: 'minor', automatable: false },
1796
- references: ['https://owasp.org/www-community/vulnerabilities/Information_exposure_through_query_strings_in_url'],
1797
- });
1798
- }
1799
- // Check for directory listing
1800
- const responseText = await crawlResponse.text();
1801
- if (responseText.includes('Index of /') || responseText.includes('Directory listing for')) {
1802
- vulnerabilities.push({
1803
- id: uuidv4(),
1804
- title: 'Directory Listing Enabled',
1805
- description: `Directory listing is enabled at: ${crawlUrl}`,
1806
- severity: 'medium',
1807
- category: 'security-misconfiguration',
1808
- location: { file: crawlUrl },
1809
- remediation: { description: 'Disable directory listing in server configuration', estimatedEffort: 'trivial', automatable: true },
1810
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1811
- });
1812
- }
1813
- }
1814
- }
1815
- catch {
1816
- // Page not accessible - expected for some links
1817
- }
1818
- }
1819
- return crawledUrls;
1820
- }
1821
- /**
1822
- * Test URL parameters for injection vulnerabilities (XSS, SQLi)
1823
- * Uses safe payloads that reveal vulnerability without exploitation
1824
- */
1825
- async testInjectionVulnerabilities(targetUrl, parsedUrl, vulnerabilities) {
1826
- const params = new URLSearchParams(parsedUrl.search);
1827
- const paramNames = Array.from(params.keys());
1828
- // Safe test payloads that reveal vulnerability without harm
1829
- const xssPayloads = [
1830
- { payload: '<script>alert(1)</script>', name: 'Basic XSS' },
1831
- { payload: '"><img src=x onerror=alert(1)>', name: 'Attribute Injection' },
1832
- { payload: "'-alert(1)-'", name: 'JavaScript Injection' },
1833
- ];
1834
- const sqliPayloads = [
1835
- { payload: "' OR '1'='1", name: 'SQL OR Injection' },
1836
- { payload: "1; DROP TABLE test--", name: 'SQL Statement Injection' },
1837
- { payload: "1' AND '1'='1", name: 'SQL AND Injection' },
1838
- ];
1839
- // Test each parameter with injection payloads
1840
- for (const paramName of paramNames.slice(0, 3)) { // Limit to first 3 params
1841
- // Test XSS
1842
- for (const xss of xssPayloads) {
1843
- try {
1844
- const testParams = new URLSearchParams(parsedUrl.search);
1845
- testParams.set(paramName, xss.payload);
1846
- const testUrl = `${parsedUrl.origin}${parsedUrl.pathname}?${testParams.toString()}`;
1847
- const response = await fetch(testUrl, {
1848
- method: 'GET',
1849
- headers: { 'User-Agent': 'AgenticQE-DAST-Scanner/3.0' },
1850
- signal: AbortSignal.timeout(5000),
1851
- });
1852
- if (response.ok) {
1853
- const text = await response.text();
1854
- const escapedPayload = xss.payload
1855
- .replace(/&/g, '&amp;')
1856
- .replace(/</g, '&lt;')
1857
- .replace(/>/g, '&gt;')
1858
- .replace(/"/g, '&quot;')
1859
- .replace(/'/g, '&#x27;');
1860
- // Check for XSS vulnerability:
1861
- // 1. Payload reflected unescaped (definite XSS)
1862
- // 2. Payload partially escaped but critical chars remain (potential bypass)
1863
- const hasUnescapedPayload = text.includes(xss.payload);
1864
- const hasEscapedPayload = text.includes(escapedPayload);
1865
- // Only flag if payload is reflected AND it's unescaped
1866
- if (hasUnescapedPayload && !hasEscapedPayload) {
1867
- vulnerabilities.push({
1868
- id: uuidv4(),
1869
- title: `Reflected XSS: ${xss.name}`,
1870
- description: `Parameter '${paramName}' reflects unsanitized input - payload executed without encoding`,
1871
- severity: 'critical',
1872
- category: 'xss',
1873
- location: { file: targetUrl, snippet: `Parameter: ${paramName}, Payload: ${xss.payload.substring(0, 30)}...` },
1874
- remediation: { description: 'HTML-encode all user input before rendering. Use framework auto-escaping.', estimatedEffort: 'moderate', automatable: false },
1875
- references: ['https://owasp.org/www-community/attacks/xss/', 'https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html'],
1876
- });
1877
- break; // One XSS finding per parameter is enough
1878
- }
1879
- // Check for partial escaping (< escaped but not > or vice versa) - potential bypass
1880
- const hasPartialEscape = (text.includes('&lt;') && text.includes('>') && text.includes(xss.payload.replace(/</g, '&lt;'))) ||
1881
- (text.includes('<') && text.includes('&gt;') && text.includes(xss.payload.replace(/>/g, '&gt;')));
1882
- if (hasPartialEscape) {
1883
- vulnerabilities.push({
1884
- id: uuidv4(),
1885
- title: `Potential XSS: Inconsistent Encoding`,
1886
- description: `Parameter '${paramName}' has inconsistent HTML encoding - some characters escaped, others not`,
1887
- severity: 'medium',
1888
- category: 'xss',
1889
- location: { file: targetUrl, snippet: `Parameter: ${paramName}` },
1890
- remediation: { description: 'Use consistent HTML encoding for all special characters', estimatedEffort: 'minor', automatable: false },
1891
- references: ['https://owasp.org/www-community/attacks/xss/'],
1892
- });
1893
- break;
1894
- }
1895
- }
1896
- }
1897
- catch {
1898
- // Request failed - target may be blocking
1899
- }
1900
- }
1901
- // Test SQLi (look for error-based indicators)
1902
- for (const sqli of sqliPayloads) {
1903
- try {
1904
- const testParams = new URLSearchParams(parsedUrl.search);
1905
- testParams.set(paramName, sqli.payload);
1906
- const testUrl = `${parsedUrl.origin}${parsedUrl.pathname}?${testParams.toString()}`;
1907
- const response = await fetch(testUrl, {
1908
- method: 'GET',
1909
- headers: { 'User-Agent': 'AgenticQE-DAST-Scanner/3.0' },
1910
- signal: AbortSignal.timeout(5000),
1911
- });
1912
- const text = await response.text();
1913
- // Look for SQL error indicators
1914
- const sqlErrorPatterns = [
1915
- /SQL syntax.*MySQL/i,
1916
- /Warning.*mysql/i,
1917
- /PostgreSQL.*ERROR/i,
1918
- /ORA-\d{5}/i,
1919
- /SQLite.*error/i,
1920
- /SQLITE_ERROR/i,
1921
- /unclosed quotation mark/i,
1922
- /quoted string not properly terminated/i,
1923
- ];
1924
- for (const pattern of sqlErrorPatterns) {
1925
- if (pattern.test(text)) {
1926
- vulnerabilities.push({
1927
- id: uuidv4(),
1928
- title: `SQL Injection: ${sqli.name}`,
1929
- description: `Parameter '${paramName}' appears vulnerable to SQL injection - database error message exposed`,
1930
- severity: 'critical',
1931
- category: 'injection',
1932
- location: { file: targetUrl, snippet: `Parameter: ${paramName}` },
1933
- remediation: { description: 'Use parameterized queries or prepared statements', estimatedEffort: 'moderate', automatable: false },
1934
- references: ['https://owasp.org/www-community/attacks/SQL_Injection'],
1935
- });
1936
- break;
1937
- }
1938
- }
1939
- }
1940
- catch {
1941
- // Request failed
1942
- }
1943
- }
1944
- }
1945
- }
1946
- /**
1947
- * Analyze HTML forms for security issues
1948
- * Checks for CSRF protection, autocomplete settings, and action targets
1949
- */
1950
- async analyzeFormsForSecurityIssues(html, baseUrl, vulnerabilities) {
1951
- // Extract forms using regex
1952
- const formPattern = /<form[^>]*>([\s\S]*?)<\/form>/gi;
1953
- let formMatch;
1954
- let formIndex = 0;
1955
- while ((formMatch = formPattern.exec(html)) !== null && formIndex < 10) {
1956
- formIndex++;
1957
- const formHtml = formMatch[0];
1958
- const formContent = formMatch[1];
1959
- // Check for CSRF token
1960
- const hasCsrfToken = /name=["']?csrf/i.test(formContent) ||
1961
- /name=["']?_token/i.test(formContent) ||
1962
- /name=["']?authenticity_token/i.test(formContent) ||
1963
- /name=["']?__RequestVerificationToken/i.test(formContent);
1964
- // Check form method
1965
- const isPostForm = /method=["']?post/i.test(formHtml);
1966
- if (isPostForm && !hasCsrfToken) {
1967
- vulnerabilities.push({
1968
- id: uuidv4(),
1969
- title: 'Missing CSRF Token',
1970
- description: `POST form #${formIndex} does not appear to have CSRF protection`,
1971
- severity: 'medium',
1972
- category: 'broken-auth',
1973
- location: { file: baseUrl, snippet: `Form #${formIndex}` },
1974
- remediation: { description: 'Add CSRF token to all state-changing forms', estimatedEffort: 'minor', automatable: false },
1975
- references: ['https://owasp.org/www-community/attacks/csrf'],
1976
- });
1977
- }
1978
- // Check for password fields without autocomplete=off
1979
- if (/type=["']?password/i.test(formContent)) {
1980
- const hasAutocompleteOff = /autocomplete=["']?(off|new-password)/i.test(formContent) ||
1981
- /autocomplete=["']?(off|new-password)/i.test(formHtml);
1982
- if (!hasAutocompleteOff) {
1983
- vulnerabilities.push({
1984
- id: uuidv4(),
1985
- title: 'Password Field Allows Autocomplete',
1986
- description: `Form #${formIndex} has password field that may be cached by browser`,
1987
- severity: 'low',
1988
- category: 'sensitive-data',
1989
- location: { file: baseUrl, snippet: `Form #${formIndex}` },
1990
- remediation: { description: 'Add autocomplete="new-password" to password fields', estimatedEffort: 'trivial', automatable: true },
1991
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
1992
- });
1993
- }
1994
- }
1995
- // Check for insecure form action
1996
- const actionMatch = /action=["']?([^"'\s>]+)/i.exec(formHtml);
1997
- if (actionMatch) {
1998
- const action = actionMatch[1];
1999
- if (action.startsWith('http://') && !action.includes('localhost') && !action.includes('127.0.0.1')) {
2000
- vulnerabilities.push({
2001
- id: uuidv4(),
2002
- title: 'Form Submits to Insecure HTTP',
2003
- description: `Form #${formIndex} submits data over insecure HTTP: ${action}`,
2004
- severity: 'high',
2005
- category: 'sensitive-data',
2006
- location: { file: baseUrl, snippet: `Action: ${action}` },
2007
- remediation: { description: 'Change form action to use HTTPS', estimatedEffort: 'trivial', automatable: true },
2008
- references: ['https://owasp.org/www-project-web-security-testing-guide/'],
2009
- });
2010
- }
2011
- }
2012
- }
2013
- }
2014
- }
2
+ * Agentic QE v3 - Security Scanner Service (Facade)
3
+ *
4
+ * This file provides backward compatibility by re-exporting from the
5
+ * refactored scanner modules. The original 2,486-line file has been
6
+ * split into focused sub-modules:
7
+ *
8
+ * - scanner-types.ts: Shared types and interfaces (~170 LOC)
9
+ * - security-patterns.ts: Vulnerability detection patterns (~430 LOC)
10
+ * - sast-scanner.ts: Static Application Security Testing (~410 LOC)
11
+ * - dast-scanner.ts: Dynamic Application Security Testing (~580 LOC)
12
+ * - dependency-scanner.ts: NPM/OSV vulnerability scanning (~160 LOC)
13
+ * - scanner-orchestrator.ts: Main service coordinating all scanners (~220 LOC)
14
+ *
15
+ * @see /v3/docs/reports/quality-analysis/milestone-1.1-complete.md
16
+ */
17
+ // =============================================================================
18
+ // Main Service Export (Backward Compatible)
19
+ // =============================================================================
20
+ export { SecurityScannerService } from './scanners/index.js';
21
+ // =============================================================================
22
+ // Pattern Exports (For Advanced Usage)
23
+ // =============================================================================
24
+ export {
25
+ // Default configuration
26
+ DEFAULT_CONFIG,
27
+ // All patterns combined
28
+ ALL_SECURITY_PATTERNS,
29
+ // Individual pattern categories
30
+ SQL_INJECTION_PATTERNS, XSS_PATTERNS, SECRET_PATTERNS, PATH_TRAVERSAL_PATTERNS, COMMAND_INJECTION_PATTERNS, MISCONFIGURATION_PATTERNS, DESERIALIZATION_PATTERNS, AUTH_PATTERNS,
31
+ // Built-in rule sets
32
+ BUILT_IN_RULE_SETS, } from './scanners/index.js';
33
+ // =============================================================================
34
+ // Individual Scanner Exports (For Direct Access)
35
+ // =============================================================================
36
+ export { SASTScanner } from './scanners/sast-scanner.js';
37
+ export { DASTScanner } from './scanners/dast-scanner.js';
38
+ export { DependencyScanner } from './scanners/dependency-scanner.js';
2015
39
  //# sourceMappingURL=security-scanner.js.map