@runa-ai/runa-cli 0.5.72 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/dist/build-V66FAQXB.js +1719 -0
  2. package/dist/cache-N7WNPEYF.js +111 -0
  3. package/dist/check-LOMVIRHX.js +12 -0
  4. package/dist/chunk-2APB25TT.js +442 -0
  5. package/dist/chunk-3FDQW524.js +544 -0
  6. package/dist/chunk-3WDV32GA.js +33 -0
  7. package/dist/chunk-5FT3F36G.js +59 -0
  8. package/dist/chunk-5NKWR4FF.js +254 -0
  9. package/dist/chunk-644FVGIQ.js +194 -0
  10. package/dist/chunk-6AALH2ED.js +121 -0
  11. package/dist/chunk-6FAU4IGR.js +63 -0
  12. package/dist/chunk-6Y3LAUGL.js +35 -0
  13. package/dist/chunk-7B5C6U2K.js +274 -0
  14. package/dist/chunk-AAIE4F2U.js +140 -0
  15. package/dist/chunk-AIP6MR42.js +12 -0
  16. package/dist/chunk-CCKG5R4Y.js +59 -0
  17. package/dist/chunk-DRSUEMAK.js +123 -0
  18. package/dist/chunk-FHG3ILE4.js +2011 -0
  19. package/dist/chunk-H2AHNI75.js +31 -0
  20. package/dist/chunk-HD74F6W2.js +460 -0
  21. package/dist/chunk-HKUWEGUX.js +36 -0
  22. package/dist/chunk-IBVVGH6X.js +33 -0
  23. package/dist/chunk-II7VYQEM.js +179 -0
  24. package/dist/chunk-JMJP4A47.js +204 -0
  25. package/dist/chunk-JQXOVCOP.js +574 -0
  26. package/dist/chunk-KE6QJBZG.js +41 -0
  27. package/dist/chunk-KWX3JHCY.js +85 -0
  28. package/dist/chunk-MXRWBNIY.js +74 -0
  29. package/dist/chunk-NPSRD26F.js +149 -0
  30. package/dist/chunk-QDF7QXBL.js +67 -0
  31. package/dist/chunk-QM53IQHM.js +209 -0
  32. package/dist/chunk-RZLYEO4U.js +219 -0
  33. package/dist/chunk-SGJG3BKD.js +351 -0
  34. package/dist/chunk-TYIAD6SB.js +74 -0
  35. package/dist/chunk-UWWSAPDR.js +31 -0
  36. package/dist/chunk-VM3IWOT5.js +458 -0
  37. package/dist/chunk-VRXHCR5K.js +42 -0
  38. package/dist/chunk-WJXC4MVY.js +75 -0
  39. package/dist/chunk-XDCHRVE3.js +215 -0
  40. package/dist/chunk-Z4Z5DNW4.js +1196 -0
  41. package/dist/chunk-ZZOXM6Q4.js +8 -0
  42. package/dist/ci-ZWRVWNFX.js +9298 -0
  43. package/dist/cli/contract-output.d.ts +1 -0
  44. package/dist/cli/index.d.ts +7 -1
  45. package/dist/cli/requested-command.d.ts +8 -0
  46. package/dist/cli-2JNBJUBB.js +704 -0
  47. package/dist/commands/build/actors/db-sync.d.ts +2 -0
  48. package/dist/commands/build/actors/static-checks.d.ts +7 -6
  49. package/dist/commands/build/actors/validate.d.ts +2 -0
  50. package/dist/commands/build/contract.d.ts +30 -30
  51. package/dist/commands/build/machine-dry-run.d.ts +3 -0
  52. package/dist/commands/build/machine-e2e-meta.d.ts +120 -0
  53. package/dist/commands/build/machine.d.ts +22 -22
  54. package/dist/commands/build/types.d.ts +2 -4
  55. package/dist/commands/check/commands/check.d.ts +8 -3
  56. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +9 -6
  57. package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +55 -0
  58. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
  59. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
  60. package/dist/commands/ci/machine/contract.d.ts +26 -26
  61. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts +1 -5
  62. package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts +5 -0
  63. package/dist/commands/ci/machine/formatters/sections/index.d.ts +2 -2
  64. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
  65. package/dist/commands/ci/machine/machine-execution-helpers.d.ts +40 -0
  66. package/dist/commands/ci/machine/machine-state-helpers.d.ts +14 -0
  67. package/dist/commands/ci/machine/machine.d.ts +12 -12
  68. package/dist/commands/ci/machine/types.d.ts +2 -5
  69. package/dist/commands/ci/utils/ci-summary.d.ts +15 -15
  70. package/dist/commands/ci/utils/execa-helpers.d.ts +2 -0
  71. package/dist/commands/db/apply/actors/idempotent-actors.d.ts +34 -0
  72. package/dist/commands/db/apply/actors/lock-actors.d.ts +16 -0
  73. package/dist/commands/db/apply/actors/pg-schema-diff-actors.d.ts +31 -0
  74. package/dist/commands/db/apply/actors/seed-actors.d.ts +11 -0
  75. package/dist/commands/db/apply/actors/shared.d.ts +9 -0
  76. package/dist/commands/db/apply/actors.d.ts +16 -65
  77. package/dist/commands/db/apply/contract.d.ts +8 -1
  78. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts +3 -4
  79. package/dist/commands/db/apply/helpers/data-integrity-verifier.d.ts +37 -0
  80. package/dist/commands/db/apply/helpers/fresh-db-handler.d.ts +34 -0
  81. package/dist/commands/db/apply/helpers/hazard-handler.d.ts +60 -0
  82. package/dist/commands/db/apply/helpers/idempotent-object-registry.d.ts +96 -0
  83. package/dist/commands/db/apply/helpers/idempotent-transaction.d.ts +20 -0
  84. package/dist/commands/db/apply/helpers/index.d.ts +6 -0
  85. package/dist/commands/db/apply/helpers/partition-validator.d.ts +2 -15
  86. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +18 -162
  87. package/dist/commands/db/apply/helpers/pg-schema-diff-patterns.d.ts +55 -0
  88. package/dist/commands/db/apply/helpers/pg-schema-diff-version.d.ts +50 -0
  89. package/dist/commands/db/apply/helpers/plan-validator.d.ts +4 -10
  90. package/dist/commands/db/apply/helpers/rbac-password-manager.d.ts +34 -0
  91. package/dist/commands/db/apply/helpers/retry-logic.d.ts +16 -2
  92. package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts +1 -1
  93. package/dist/commands/db/apply/helpers/sql-utils.d.ts +26 -0
  94. package/dist/commands/db/apply/machine.d.ts +52 -1
  95. package/dist/commands/db/commands/db-apply.d.ts +18 -0
  96. package/dist/commands/db/commands/db-sync/boundary-classifier.d.ts +21 -0
  97. package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
  98. package/dist/commands/db/commands/db-sync/plan-hazard-analyzer.d.ts +13 -0
  99. package/dist/commands/db/commands/db-sync/risk-reporter.d.ts +19 -0
  100. package/dist/commands/db/commands/db-sync/sql-parser.d.ts +25 -0
  101. package/dist/commands/db/commands/db-sync/types.d.ts +47 -0
  102. package/dist/commands/db/commands/db-sync.d.ts +14 -0
  103. package/dist/commands/db/sync/contract.d.ts +6 -2
  104. package/dist/commands/db/sync/machine.d.ts +2 -1
  105. package/dist/commands/db/types.d.ts +2 -0
  106. package/dist/commands/db/utils/boundary-policy/rule-compiler.d.ts +11 -0
  107. package/dist/commands/db/utils/boundary-policy/types.d.ts +105 -0
  108. package/dist/commands/db/utils/boundary-policy/validation.d.ts +20 -0
  109. package/dist/commands/db/utils/boundary-policy-runtime.d.ts +28 -0
  110. package/dist/commands/db/utils/boundary-policy.d.ts +5 -0
  111. package/dist/commands/db/utils/idempotent-risk-context.d.ts +29 -0
  112. package/dist/commands/db/utils/preflight-check.d.ts +14 -0
  113. package/dist/commands/db/utils/preflight-checks/domain-naming-checks.d.ts +106 -0
  114. package/dist/commands/db/utils/preflight-checks/orphan-checks.d.ts +36 -0
  115. package/dist/commands/db/utils/preflight-checks/schema-risk-checks.d.ts +22 -0
  116. package/dist/commands/db/utils/preflight-checks/supabase-checks.d.ts +55 -0
  117. package/dist/commands/db/utils/risk-detector-loader.d.ts +8 -0
  118. package/dist/commands/db/utils/schema-precheck-budget.d.ts +17 -0
  119. package/dist/commands/db/utils/sql-boundary-parser.d.ts +12 -0
  120. package/dist/commands/db/utils/sql-file-collector.d.ts +8 -0
  121. package/dist/commands/db/utils/sql-filename-parser.d.ts +20 -0
  122. package/dist/commands/db/utils/sql-table-extractor-ast.d.ts +19 -0
  123. package/dist/commands/db/utils/sql-table-extractor-regex.d.ts +50 -0
  124. package/dist/commands/db/utils/sql-table-extractor-rls.d.ts +13 -0
  125. package/dist/commands/db/utils/sql-table-extractor.d.ts +79 -1
  126. package/dist/commands/db/utils/table-registry-introspection.d.ts +68 -0
  127. package/dist/commands/db/utils/table-registry.d.ts +3 -38
  128. package/dist/commands/dev/actors/app-lifecycle.d.ts +18 -0
  129. package/dist/commands/dev/actors/index.d.ts +12 -2
  130. package/dist/commands/dev/actors/process-check.d.ts +12 -0
  131. package/dist/commands/dev/actors/shared.d.ts +15 -0
  132. package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
  133. package/dist/commands/dev/contract.d.ts +3 -3
  134. package/dist/commands/dev/guards.d.ts +24 -0
  135. package/dist/commands/dev/machine.d.ts +27 -32
  136. package/dist/commands/dev/types.d.ts +2 -0
  137. package/dist/commands/doctor.d.ts +9 -0
  138. package/dist/commands/env/commands/env-pull/auth.d.ts +13 -0
  139. package/dist/commands/env/commands/env-pull/dotenv-files.d.ts +14 -0
  140. package/dist/commands/env/commands/env-pull/security.d.ts +12 -0
  141. package/dist/commands/env/commands/env-pull/service.d.ts +8 -0
  142. package/dist/commands/env/commands/env-pull/shared.d.ts +79 -0
  143. package/dist/commands/env/commands/setup/types.d.ts +1 -1
  144. package/dist/commands/env/constants/local-supabase.d.ts +2 -0
  145. package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
  146. package/dist/commands/template-check/contract.d.ts +6 -6
  147. package/dist/commands/template-check/machine.d.ts +2 -2
  148. package/dist/commands/template-check/types.d.ts +0 -4
  149. package/dist/commands/template-check/utils/diff-analyzer.d.ts +0 -4
  150. package/dist/commands/utils/machine-state-logging.d.ts +20 -0
  151. package/dist/commands/utils/repo-root.d.ts +2 -0
  152. package/dist/config/env.d.ts +4 -4
  153. package/dist/config-loader-GT3HAQ7U.js +7 -0
  154. package/dist/db-XULCILOU.js +14137 -0
  155. package/dist/dev-5YXNPTCJ.js +992 -0
  156. package/dist/doctor-MZLOA53G.js +44 -0
  157. package/dist/env-HMMRSYCI.js +7 -0
  158. package/dist/env-SS66PZ4B.js +2623 -0
  159. package/dist/env-files-2UIUYLLR.js +8 -0
  160. package/dist/error-handler-HEXBRNVV.js +460 -0
  161. package/dist/hotfix-YA3DGLOM.js +1477 -0
  162. package/dist/index.d.ts +5 -1
  163. package/dist/index.js +48 -42995
  164. package/dist/init-ZIL6LRFO.js +631 -0
  165. package/dist/inject-test-attrs-P44BVTQS.js +23 -0
  166. package/dist/internal/machines/snapshot-helpers.d.ts +6 -0
  167. package/dist/lib/sql-comment-utils.d.ts +25 -0
  168. package/dist/license-OB7GVJQ2.js +468 -0
  169. package/dist/link-VSNDVZZD.js +59 -0
  170. package/dist/manifest-TMFLESHW.js +19 -0
  171. package/dist/prepare-32DOVHTE.js +250 -0
  172. package/dist/risk-detector-4U6ZJ2G5.js +6 -0
  173. package/dist/risk-detector-core-TK4OAI3N.js +166 -0
  174. package/dist/risk-detector-plpgsql-HWKS4OLR.js +1886 -0
  175. package/dist/sdk-XK6HQU7S.js +348 -0
  176. package/dist/services-7VK5KZTO.js +177 -0
  177. package/dist/session-SFW5QSXZ.js +142 -0
  178. package/dist/signal-handler-DO3OANW5.js +6 -0
  179. package/dist/status-UTKS63AB.js +94 -0
  180. package/dist/telemetry-P56UBLZ2.js +93 -0
  181. package/dist/template-check-3P4HZXVY.js +1944 -0
  182. package/dist/test-V4KQL574.js +650 -0
  183. package/dist/test-gen-FS4CEY3P.js +88 -0
  184. package/dist/ui-RJAMCWUI.js +331 -0
  185. package/dist/upgrade-NUK3ZBCL.js +637 -0
  186. package/dist/utils/config-loader.d.ts +0 -3
  187. package/dist/validate-CAAW4Y44.js +54 -0
  188. package/dist/validators/risk-detector-content-risks.d.ts +13 -0
  189. package/dist/validators/risk-detector-core.d.ts +25 -0
  190. package/dist/validators/risk-detector-patterns.d.ts +15 -0
  191. package/dist/validators/risk-detector-plpgsql-expression-resolver.d.ts +22 -0
  192. package/dist/validators/risk-detector-plpgsql-parser.d.ts +5 -0
  193. package/dist/validators/risk-detector-plpgsql-tokenizer.d.ts +18 -0
  194. package/dist/validators/risk-detector-plpgsql.d.ts +9 -0
  195. package/dist/validators/risk-detector-text-utils.d.ts +6 -0
  196. package/dist/validators/risk-detector-types.d.ts +16 -0
  197. package/dist/validators/risk-detector.d.ts +7 -26
  198. package/dist/vuln-check-2W7N5TA2.js +121 -0
  199. package/dist/vuln-checker-IQJ56RUV.js +3223 -0
  200. package/dist/watch-PNTKZYFB.js +911 -0
  201. package/dist/workflow-H75N4BXX.js +897 -0
  202. package/package.json +5 -2
  203. package/dist/cli/contract-mode.d.ts.map +0 -1
  204. package/dist/cli/contract-output.d.ts.map +0 -1
  205. package/dist/cli/early-flags.d.ts.map +0 -1
  206. package/dist/cli/error-handler.d.ts.map +0 -1
  207. package/dist/cli/exec.d.ts.map +0 -1
  208. package/dist/cli/index.d.ts.map +0 -1
  209. package/dist/cli/json-output.d.ts.map +0 -1
  210. package/dist/cli/non-interactive.d.ts.map +0 -1
  211. package/dist/cli/output-format.d.ts.map +0 -1
  212. package/dist/cli/signal-handler.d.ts.map +0 -1
  213. package/dist/commands/build/actors/build.d.ts.map +0 -1
  214. package/dist/commands/build/actors/clean.d.ts.map +0 -1
  215. package/dist/commands/build/actors/db-sync.d.ts.map +0 -1
  216. package/dist/commands/build/actors/index.d.ts.map +0 -1
  217. package/dist/commands/build/actors/manifest.d.ts.map +0 -1
  218. package/dist/commands/build/actors/setup.d.ts.map +0 -1
  219. package/dist/commands/build/actors/static-checks.d.ts.map +0 -1
  220. package/dist/commands/build/actors/validate.d.ts.map +0 -1
  221. package/dist/commands/build/commands/build.d.ts.map +0 -1
  222. package/dist/commands/build/contract.d.ts.map +0 -1
  223. package/dist/commands/build/guards.d.ts.map +0 -1
  224. package/dist/commands/build/index.d.ts.map +0 -1
  225. package/dist/commands/build/machine.d.ts.map +0 -1
  226. package/dist/commands/build/types.d.ts.map +0 -1
  227. package/dist/commands/cache.d.ts.map +0 -1
  228. package/dist/commands/check/commands/check.d.ts.map +0 -1
  229. package/dist/commands/check/index.d.ts.map +0 -1
  230. package/dist/commands/ci/commands/ci-checks.d.ts.map +0 -1
  231. package/dist/commands/ci/commands/ci-layer-content.d.ts.map +0 -1
  232. package/dist/commands/ci/commands/ci-pr-capabilities.d.ts.map +0 -1
  233. package/dist/commands/ci/commands/ci-prod-apply.d.ts.map +0 -1
  234. package/dist/commands/ci/commands/ci-prod-db-operations.d.ts.map +0 -1
  235. package/dist/commands/ci/commands/ci-prod-github.d.ts.map +0 -1
  236. package/dist/commands/ci/commands/ci-prod-types.d.ts.map +0 -1
  237. package/dist/commands/ci/commands/ci-prod-utils.d.ts.map +0 -1
  238. package/dist/commands/ci/commands/ci-prod-workflow.d.ts.map +0 -1
  239. package/dist/commands/ci/commands/ci-resolvers.d.ts.map +0 -1
  240. package/dist/commands/ci/commands/ci-static.d.ts.map +0 -1
  241. package/dist/commands/ci/commands/ci-supabase-local.d.ts.map +0 -1
  242. package/dist/commands/ci/index.d.ts.map +0 -1
  243. package/dist/commands/ci/machine/actors/build/app-build.d.ts.map +0 -1
  244. package/dist/commands/ci/machine/actors/build/app-start.d.ts.map +0 -1
  245. package/dist/commands/ci/machine/actors/build/build-and-playwright.d.ts.map +0 -1
  246. package/dist/commands/ci/machine/actors/build/index.d.ts.map +0 -1
  247. package/dist/commands/ci/machine/actors/build/playwright-install.d.ts.map +0 -1
  248. package/dist/commands/ci/machine/actors/build/static-checks.d.ts.map +0 -1
  249. package/dist/commands/ci/machine/actors/db/apply-seeds.d.ts.map +0 -1
  250. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts.map +0 -1
  251. package/dist/commands/ci/machine/actors/db/index.d.ts.map +0 -1
  252. package/dist/commands/ci/machine/actors/db/pgtap-install.d.ts.map +0 -1
  253. package/dist/commands/ci/machine/actors/db/production-preview.d.ts.map +0 -1
  254. package/dist/commands/ci/machine/actors/db/pull-production.d.ts.map +0 -1
  255. package/dist/commands/ci/machine/actors/db/reset.d.ts.map +0 -1
  256. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts.map +0 -1
  257. package/dist/commands/ci/machine/actors/db/setup-roles.d.ts.map +0 -1
  258. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts.map +0 -1
  259. package/dist/commands/ci/machine/actors/finalize/github.d.ts.map +0 -1
  260. package/dist/commands/ci/machine/actors/finalize/index.d.ts.map +0 -1
  261. package/dist/commands/ci/machine/actors/finalize/summary.d.ts.map +0 -1
  262. package/dist/commands/ci/machine/actors/index.d.ts.map +0 -1
  263. package/dist/commands/ci/machine/actors/setup/index.d.ts.map +0 -1
  264. package/dist/commands/ci/machine/actors/setup/local.d.ts.map +0 -1
  265. package/dist/commands/ci/machine/actors/setup/pr-common.d.ts.map +0 -1
  266. package/dist/commands/ci/machine/actors/setup/pr-local.d.ts.map +0 -1
  267. package/dist/commands/ci/machine/actors/test/capabilities.d.ts.map +0 -1
  268. package/dist/commands/ci/machine/actors/test/index.d.ts.map +0 -1
  269. package/dist/commands/ci/machine/actors/test/run-layers.d.ts.map +0 -1
  270. package/dist/commands/ci/machine/commands/ci-local.d.ts.map +0 -1
  271. package/dist/commands/ci/machine/commands/ci-pr.d.ts.map +0 -1
  272. package/dist/commands/ci/machine/commands/index.d.ts.map +0 -1
  273. package/dist/commands/ci/machine/commands/machine-runner.d.ts.map +0 -1
  274. package/dist/commands/ci/machine/commands/runtime-env.d.ts.map +0 -1
  275. package/dist/commands/ci/machine/contract.d.ts.map +0 -1
  276. package/dist/commands/ci/machine/formatters/github-comment-types.d.ts.map +0 -1
  277. package/dist/commands/ci/machine/formatters/github-comment.d.ts.map +0 -1
  278. package/dist/commands/ci/machine/formatters/index.d.ts.map +0 -1
  279. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts.map +0 -1
  280. package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts.map +0 -1
  281. package/dist/commands/ci/machine/formatters/sections/index.d.ts.map +0 -1
  282. package/dist/commands/ci/machine/formatters/sections/progress-comment.d.ts.map +0 -1
  283. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts.map +0 -1
  284. package/dist/commands/ci/machine/formatters/summary.d.ts.map +0 -1
  285. package/dist/commands/ci/machine/guards.d.ts.map +0 -1
  286. package/dist/commands/ci/machine/helpers.d.ts.map +0 -1
  287. package/dist/commands/ci/machine/index.d.ts.map +0 -1
  288. package/dist/commands/ci/machine/machine.d.ts.map +0 -1
  289. package/dist/commands/ci/machine/types.d.ts.map +0 -1
  290. package/dist/commands/ci/utils/ai-report.d.ts.map +0 -1
  291. package/dist/commands/ci/utils/app-process.d.ts.map +0 -1
  292. package/dist/commands/ci/utils/app-runtime.d.ts.map +0 -1
  293. package/dist/commands/ci/utils/ci-config.d.ts.map +0 -1
  294. package/dist/commands/ci/utils/ci-env-schema.d.ts.map +0 -1
  295. package/dist/commands/ci/utils/ci-logging.d.ts.map +0 -1
  296. package/dist/commands/ci/utils/ci-summary.d.ts.map +0 -1
  297. package/dist/commands/ci/utils/config-readers.d.ts.map +0 -1
  298. package/dist/commands/ci/utils/db-url-utils.d.ts.map +0 -1
  299. package/dist/commands/ci/utils/e2e-auth-setup.d.ts.map +0 -1
  300. package/dist/commands/ci/utils/env-security.d.ts.map +0 -1
  301. package/dist/commands/ci/utils/execa-helpers.d.ts.map +0 -1
  302. package/dist/commands/ci/utils/exit-code-computation.d.ts.map +0 -1
  303. package/dist/commands/ci/utils/github-api.d.ts.map +0 -1
  304. package/dist/commands/ci/utils/github.d.ts.map +0 -1
  305. package/dist/commands/ci/utils/index.d.ts.map +0 -1
  306. package/dist/commands/ci/utils/pgtap-installer.d.ts.map +0 -1
  307. package/dist/commands/ci/utils/rls-verification.d.ts.map +0 -1
  308. package/dist/commands/ci/utils/schema-operations.d.ts.map +0 -1
  309. package/dist/commands/ci/utils/seed-operations.d.ts.map +0 -1
  310. package/dist/commands/ci/utils/test-parallel.d.ts.map +0 -1
  311. package/dist/commands/ci/utils/timestamp-invariants.d.ts.map +0 -1
  312. package/dist/commands/ci/utils/workflow-idempotency.d.ts.map +0 -1
  313. package/dist/commands/db/apply/actors.d.ts.map +0 -1
  314. package/dist/commands/db/apply/contract.d.ts.map +0 -1
  315. package/dist/commands/db/apply/helpers/advisory-lock.d.ts.map +0 -1
  316. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts.map +0 -1
  317. package/dist/commands/db/apply/helpers/index.d.ts.map +0 -1
  318. package/dist/commands/db/apply/helpers/partition-acl-cleaner.d.ts.map +0 -1
  319. package/dist/commands/db/apply/helpers/partition-prefilter.d.ts.map +0 -1
  320. package/dist/commands/db/apply/helpers/partition-validator.d.ts.map +0 -1
  321. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts.map +0 -1
  322. package/dist/commands/db/apply/helpers/plan-validator.d.ts.map +0 -1
  323. package/dist/commands/db/apply/helpers/retry-logic.d.ts.map +0 -1
  324. package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts.map +0 -1
  325. package/dist/commands/db/apply/index.d.ts.map +0 -1
  326. package/dist/commands/db/apply/machine.d.ts.map +0 -1
  327. package/dist/commands/db/commands/db-apply.d.ts.map +0 -1
  328. package/dist/commands/db/commands/db-audit.d.ts.map +0 -1
  329. package/dist/commands/db/commands/db-backup.d.ts.map +0 -1
  330. package/dist/commands/db/commands/db-cleanup.d.ts.map +0 -1
  331. package/dist/commands/db/commands/db-derive-role-passwords.d.ts.map +0 -1
  332. package/dist/commands/db/commands/db-derive-urls.d.ts.map +0 -1
  333. package/dist/commands/db/commands/db-diagram.d.ts.map +0 -1
  334. package/dist/commands/db/commands/db-drizzle.d.ts.map +0 -1
  335. package/dist/commands/db/commands/db-extension.d.ts.map +0 -1
  336. package/dist/commands/db/commands/db-generate-password.d.ts.map +0 -1
  337. package/dist/commands/db/commands/db-lifecycle.d.ts.map +0 -1
  338. package/dist/commands/db/commands/db-rollback.d.ts.map +0 -1
  339. package/dist/commands/db/commands/db-schema.d.ts.map +0 -1
  340. package/dist/commands/db/commands/db-seed-metadata.d.ts.map +0 -1
  341. package/dist/commands/db/commands/db-seed-verify.d.ts.map +0 -1
  342. package/dist/commands/db/commands/db-seed.d.ts.map +0 -1
  343. package/dist/commands/db/commands/db-snapshot.d.ts.map +0 -1
  344. package/dist/commands/db/commands/db-stack.d.ts.map +0 -1
  345. package/dist/commands/db/commands/db-stats.d.ts.map +0 -1
  346. package/dist/commands/db/commands/db-sync.d.ts.map +0 -1
  347. package/dist/commands/db/commands/db-test.d.ts.map +0 -1
  348. package/dist/commands/db/constants.d.ts.map +0 -1
  349. package/dist/commands/db/extension-registry.d.ts.map +0 -1
  350. package/dist/commands/db/index.d.ts.map +0 -1
  351. package/dist/commands/db/preflight/actors.d.ts.map +0 -1
  352. package/dist/commands/db/preflight/contract.d.ts.map +0 -1
  353. package/dist/commands/db/preflight/index.d.ts.map +0 -1
  354. package/dist/commands/db/sync/actors.d.ts.map +0 -1
  355. package/dist/commands/db/sync/contract.d.ts.map +0 -1
  356. package/dist/commands/db/sync/index.d.ts.map +0 -1
  357. package/dist/commands/db/sync/machine.d.ts.map +0 -1
  358. package/dist/commands/db/types.d.ts.map +0 -1
  359. package/dist/commands/db/utils/db-target.d.ts.map +0 -1
  360. package/dist/commands/db/utils/db-url-builder.d.ts.map +0 -1
  361. package/dist/commands/db/utils/error-handlers.d.ts.map +0 -1
  362. package/dist/commands/db/utils/import-impact-analyzer.d.ts.map +0 -1
  363. package/dist/commands/db/utils/preflight-check.d.ts.map +0 -1
  364. package/dist/commands/db/utils/psql.d.ts.map +0 -1
  365. package/dist/commands/db/utils/schema-detector.d.ts.map +0 -1
  366. package/dist/commands/db/utils/schema-sync.d.ts.map +0 -1
  367. package/dist/commands/db/utils/script-runner.d.ts.map +0 -1
  368. package/dist/commands/db/utils/seed-manager.d.ts.map +0 -1
  369. package/dist/commands/db/utils/semantic-mapper.d.ts.map +0 -1
  370. package/dist/commands/db/utils/sql-table-extractor.d.ts.map +0 -1
  371. package/dist/commands/db/utils/stack-detector.d.ts.map +0 -1
  372. package/dist/commands/db/utils/table-registry.d.ts.map +0 -1
  373. package/dist/commands/db/utils/table-source-classifier.d.ts.map +0 -1
  374. package/dist/commands/dev/actors/index.d.ts.map +0 -1
  375. package/dist/commands/dev/commands/dev.d.ts.map +0 -1
  376. package/dist/commands/dev/contract.d.ts.map +0 -1
  377. package/dist/commands/dev/guards.d.ts.map +0 -1
  378. package/dist/commands/dev/helpers/stale-process-detector.d.ts.map +0 -1
  379. package/dist/commands/dev/machine.d.ts.map +0 -1
  380. package/dist/commands/dev/types.d.ts.map +0 -1
  381. package/dist/commands/env/commands/env-check.d.ts.map +0 -1
  382. package/dist/commands/env/commands/env-encrypt.d.ts.map +0 -1
  383. package/dist/commands/env/commands/env-pull.d.ts.map +0 -1
  384. package/dist/commands/env/commands/env-setup.d.ts.map +0 -1
  385. package/dist/commands/env/commands/env-sync.d.ts.map +0 -1
  386. package/dist/commands/env/commands/setup/action.d.ts.map +0 -1
  387. package/dist/commands/env/commands/setup/auth.d.ts.map +0 -1
  388. package/dist/commands/env/commands/setup/file-export.d.ts.map +0 -1
  389. package/dist/commands/env/commands/setup/github-api.d.ts.map +0 -1
  390. package/dist/commands/env/commands/setup/helpers.d.ts.map +0 -1
  391. package/dist/commands/env/commands/setup/index.d.ts.map +0 -1
  392. package/dist/commands/env/commands/setup/parsers.d.ts.map +0 -1
  393. package/dist/commands/env/commands/setup/prompts.d.ts.map +0 -1
  394. package/dist/commands/env/commands/setup/supabase-api.d.ts.map +0 -1
  395. package/dist/commands/env/commands/setup/types.d.ts.map +0 -1
  396. package/dist/commands/env/commands/setup/vercel-api.d.ts.map +0 -1
  397. package/dist/commands/env/constants/local-supabase.d.ts.map +0 -1
  398. package/dist/commands/env/index.d.ts.map +0 -1
  399. package/dist/commands/hotfix/actors.d.ts.map +0 -1
  400. package/dist/commands/hotfix/commands/hotfix-complete.d.ts.map +0 -1
  401. package/dist/commands/hotfix/commands/hotfix-create.d.ts.map +0 -1
  402. package/dist/commands/hotfix/commands/hotfix-deploy.d.ts.map +0 -1
  403. package/dist/commands/hotfix/commands/hotfix-status.d.ts.map +0 -1
  404. package/dist/commands/hotfix/contract.d.ts.map +0 -1
  405. package/dist/commands/hotfix/index.d.ts.map +0 -1
  406. package/dist/commands/hotfix/machine.d.ts.map +0 -1
  407. package/dist/commands/hotfix/metadata.d.ts.map +0 -1
  408. package/dist/commands/hotfix/utils/hotfix-machine-helper.d.ts.map +0 -1
  409. package/dist/commands/init.d.ts.map +0 -1
  410. package/dist/commands/inject-test-attrs/action.d.ts.map +0 -1
  411. package/dist/commands/inject-test-attrs/commands/inject-test-attrs.d.ts.map +0 -1
  412. package/dist/commands/inject-test-attrs/contract.d.ts.map +0 -1
  413. package/dist/commands/inject-test-attrs/detection-diagnostics.d.ts.map +0 -1
  414. package/dist/commands/inject-test-attrs/formatter.d.ts.map +0 -1
  415. package/dist/commands/inject-test-attrs/index.d.ts.map +0 -1
  416. package/dist/commands/inject-test-attrs/manifest-generator.d.ts.map +0 -1
  417. package/dist/commands/inject-test-attrs/processor-utils.d.ts.map +0 -1
  418. package/dist/commands/inject-test-attrs/processor.d.ts.map +0 -1
  419. package/dist/commands/inject-test-attrs/types.d.ts.map +0 -1
  420. package/dist/commands/link.d.ts.map +0 -1
  421. package/dist/commands/manifest/index.d.ts.map +0 -1
  422. package/dist/commands/prepare/commands/prepare.d.ts.map +0 -1
  423. package/dist/commands/prepare/index.d.ts.map +0 -1
  424. package/dist/commands/sdk/commands/publish.d.ts.map +0 -1
  425. package/dist/commands/sdk/index.d.ts.map +0 -1
  426. package/dist/commands/services/index.d.ts.map +0 -1
  427. package/dist/commands/session/index.d.ts.map +0 -1
  428. package/dist/commands/status.d.ts.map +0 -1
  429. package/dist/commands/telemetry.d.ts.map +0 -1
  430. package/dist/commands/template-check/actors/compare.d.ts.map +0 -1
  431. package/dist/commands/template-check/actors/discover.d.ts.map +0 -1
  432. package/dist/commands/template-check/actors/index.d.ts.map +0 -1
  433. package/dist/commands/template-check/actors/report.d.ts.map +0 -1
  434. package/dist/commands/template-check/commands/template-check.d.ts.map +0 -1
  435. package/dist/commands/template-check/config.d.ts.map +0 -1
  436. package/dist/commands/template-check/contract.d.ts.map +0 -1
  437. package/dist/commands/template-check/index.d.ts.map +0 -1
  438. package/dist/commands/template-check/machine.d.ts.map +0 -1
  439. package/dist/commands/template-check/types.d.ts.map +0 -1
  440. package/dist/commands/template-check/utils/diff-analyzer.d.ts.map +0 -1
  441. package/dist/commands/template-check/utils/normalizer.d.ts.map +0 -1
  442. package/dist/commands/template-check/utils/path-mapping.d.ts.map +0 -1
  443. package/dist/commands/test/commands/test-db.d.ts.map +0 -1
  444. package/dist/commands/test/commands/test-e2e.d.ts.map +0 -1
  445. package/dist/commands/test/commands/test-fast.d.ts.map +0 -1
  446. package/dist/commands/test/commands/test-integration.d.ts.map +0 -1
  447. package/dist/commands/test/commands/test-layer.d.ts.map +0 -1
  448. package/dist/commands/test/commands/test-owasp-generate.d.ts.map +0 -1
  449. package/dist/commands/test/commands/test-service.d.ts.map +0 -1
  450. package/dist/commands/test/commands/test-static.d.ts.map +0 -1
  451. package/dist/commands/test/commands/test.d.ts.map +0 -1
  452. package/dist/commands/test/index.d.ts.map +0 -1
  453. package/dist/commands/test-gen.d.ts.map +0 -1
  454. package/dist/commands/ui.d.ts.map +0 -1
  455. package/dist/commands/upgrade.d.ts.map +0 -1
  456. package/dist/commands/validate.d.ts.map +0 -1
  457. package/dist/commands/vuln-check.d.ts.map +0 -1
  458. package/dist/commands/watch.d.ts.map +0 -1
  459. package/dist/commands/workflow/commands/deploy-production.d.ts.map +0 -1
  460. package/dist/commands/workflow/commands/final-status.d.ts.map +0 -1
  461. package/dist/commands/workflow/commands/log.d.ts.map +0 -1
  462. package/dist/commands/workflow/commands/notify.d.ts.map +0 -1
  463. package/dist/commands/workflow/commands/paths.d.ts.map +0 -1
  464. package/dist/commands/workflow/commands/sync.d.ts.map +0 -1
  465. package/dist/commands/workflow/commands/validate.d.ts.map +0 -1
  466. package/dist/commands/workflow/commands/verify-credentials.d.ts.map +0 -1
  467. package/dist/commands/workflow/index.d.ts.map +0 -1
  468. package/dist/commands/workflow/types.d.ts.map +0 -1
  469. package/dist/config/env-files.d.ts.map +0 -1
  470. package/dist/config/env.d.ts.map +0 -1
  471. package/dist/constants/versions.d.ts.map +0 -1
  472. package/dist/contracts/envelope.d.ts.map +0 -1
  473. package/dist/errors/catalog.d.ts.map +0 -1
  474. package/dist/errors/exit-codes.d.ts.map +0 -1
  475. package/dist/errors/index.d.ts.map +0 -1
  476. package/dist/incremental/affected-tests.d.ts.map +0 -1
  477. package/dist/index.d.ts.map +0 -1
  478. package/dist/internal/machines/index.d.ts.map +0 -1
  479. package/dist/internal/machines/machine-runner.d.ts.map +0 -1
  480. package/dist/internal/machines/snapshot-helpers.d.ts.map +0 -1
  481. package/dist/internal/machines/types.d.ts.map +0 -1
  482. package/dist/internal/vuln-checker/analyzers/dependency-analyzer.d.ts.map +0 -1
  483. package/dist/internal/vuln-checker/analyzers/rls-analyzer.d.ts.map +0 -1
  484. package/dist/internal/vuln-checker/analyzers/secret-analyzer.d.ts.map +0 -1
  485. package/dist/internal/vuln-checker/analyzers/typescript-analyzer.d.ts.map +0 -1
  486. package/dist/internal/vuln-checker/config/loader.d.ts.map +0 -1
  487. package/dist/internal/vuln-checker/constants.d.ts.map +0 -1
  488. package/dist/internal/vuln-checker/ignore/matcher.d.ts.map +0 -1
  489. package/dist/internal/vuln-checker/index.d.ts.map +0 -1
  490. package/dist/internal/vuln-checker/reporters/console-reporter.d.ts.map +0 -1
  491. package/dist/internal/vuln-checker/reporters/json-reporter.d.ts.map +0 -1
  492. package/dist/internal/vuln-checker/reporters/markdown-reporter.d.ts.map +0 -1
  493. package/dist/internal/vuln-checker/reporters/sarif-reporter.d.ts.map +0 -1
  494. package/dist/internal/vuln-checker/security/path-validation.d.ts.map +0 -1
  495. package/dist/internal/vuln-checker/types.d.ts.map +0 -1
  496. package/dist/notifiers/desktop-notifier.d.ts.map +0 -1
  497. package/dist/ui/components/db-panel.d.ts.map +0 -1
  498. package/dist/ui/components/status-bar.d.ts.map +0 -1
  499. package/dist/ui/components/test-panel.d.ts.map +0 -1
  500. package/dist/ui/dashboard.d.ts.map +0 -1
  501. package/dist/ui/index.d.ts.map +0 -1
  502. package/dist/utils/config-loader.d.ts.map +0 -1
  503. package/dist/utils/config-updater.d.ts.map +0 -1
  504. package/dist/utils/diagnostics.d.ts.map +0 -1
  505. package/dist/utils/dotenvx.d.ts.map +0 -1
  506. package/dist/utils/env-local-bridge.d.ts.map +0 -1
  507. package/dist/utils/execution-plan.d.ts.map +0 -1
  508. package/dist/utils/github-output-security.d.ts.map +0 -1
  509. package/dist/utils/help-system.d.ts.map +0 -1
  510. package/dist/utils/license/admin-auth.d.ts.map +0 -1
  511. package/dist/utils/license/allowlist-checker.d.ts.map +0 -1
  512. package/dist/utils/license/ci-detector.d.ts.map +0 -1
  513. package/dist/utils/license/index.d.ts.map +0 -1
  514. package/dist/utils/license/owner-resolver.d.ts.map +0 -1
  515. package/dist/utils/license/types.d.ts.map +0 -1
  516. package/dist/utils/license/validate-owner.d.ts.map +0 -1
  517. package/dist/utils/path-security.d.ts.map +0 -1
  518. package/dist/utils/port-allocator.d.ts.map +0 -1
  519. package/dist/utils/secure-exec.d.ts.map +0 -1
  520. package/dist/utils/template-fetcher.d.ts.map +0 -1
  521. package/dist/utils/type-guards.d.ts.map +0 -1
  522. package/dist/utils/vercel-project.d.ts.map +0 -1
  523. package/dist/utils/workspace-detector.d.ts.map +0 -1
  524. package/dist/validators/risk-detector.d.ts.map +0 -1
  525. package/dist/validators/schema-validator.d.ts.map +0 -1
  526. package/dist/version.d.ts.map +0 -1
  527. package/dist/watchers/schema-watcher.d.ts.map +0 -1
  528. package/dist/watchers/test-watcher.d.ts.map +0 -1
@@ -0,0 +1,3223 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { CLI_VERSION } from './chunk-AIP6MR42.js';
4
+ import { init_esm_shims } from './chunk-VRXHCR5K.js';
5
+ import { glob } from 'glob';
6
+ import { exec } from 'child_process';
7
+ import fs from 'fs/promises';
8
+ import path2 from 'path';
9
+ import { promisify } from 'util';
10
+ import { Project, Node, SyntaxKind } from 'ts-morph';
11
+ import yaml from 'js-yaml';
12
+ import 'fs';
13
+ import { minimatch } from 'minimatch';
14
+
15
+ createRequire(import.meta.url);
16
+
17
+ // src/internal/vuln-checker/index.ts
18
+ init_esm_shims();
19
+
20
+ // src/internal/vuln-checker/analyzers/dependency-analyzer.ts
21
+ init_esm_shims();
22
+
23
+ // src/internal/vuln-checker/constants.ts
24
+ init_esm_shims();
25
+ var OWASP_2021 = {
26
+ /** A01:2021 - Broken Access Control */
27
+ BROKEN_ACCESS_CONTROL: "A01:2021",
28
+ /** A02:2021 - Cryptographic Failures */
29
+ CRYPTO_FAILURES: "A02:2021",
30
+ /** A03:2021 - Injection */
31
+ INJECTION: "A03:2021",
32
+ /** A06:2021 - Vulnerable and Outdated Components */
33
+ VULNERABLE_COMPONENTS: "A06:2021",
34
+ /** A07:2021 - Identification and Authentication Failures */
35
+ AUTH_FAILURES: "A07:2021",
36
+ /** A08:2021 - Software and Data Integrity Failures */
37
+ INTEGRITY_FAILURES: "A08:2021",
38
+ /** A09:2021 - Security Logging and Monitoring Failures */
39
+ LOGGING_FAILURES: "A09:2021"};
40
+ var CWE = {
41
+ /** CWE-78: OS Command Injection */
42
+ COMMAND_INJECTION: "CWE-78",
43
+ /** CWE-79: Cross-site Scripting (XSS) */
44
+ XSS: "CWE-79",
45
+ /** CWE-89: SQL Injection */
46
+ SQL_INJECTION: "CWE-89",
47
+ /** CWE-95: Eval Injection */
48
+ EVAL_INJECTION: "CWE-95",
49
+ /** CWE-284: Improper Access Control */
50
+ IMPROPER_ACCESS_CONTROL: "CWE-284",
51
+ /** CWE-306: Missing Authentication */
52
+ MISSING_AUTH: "CWE-306",
53
+ /** CWE-321: Use of Hard-coded Cryptographic Key */
54
+ HARDCODED_CRYPTO_KEY: "CWE-321",
55
+ /** CWE-330: Use of Insufficiently Random Values */
56
+ WEAK_RANDOM: "CWE-330",
57
+ /** CWE-532: Insertion of Sensitive Information into Log File */
58
+ SENSITIVE_LOG: "CWE-532",
59
+ /** CWE-798: Use of Hard-coded Credentials */
60
+ HARDCODED_CREDENTIALS: "CWE-798",
61
+ /** CWE-1104: Use of Unmaintained Third Party Components */
62
+ UNMAINTAINED_COMPONENTS: "CWE-1104",
63
+ /** CWE-494: Download of Code Without Integrity Check */
64
+ UNTRUSTED_DOWNLOAD: "CWE-494"
65
+ };
66
+ var SEVERITY_SCORE = {
67
+ critical: 10,
68
+ high: 8,
69
+ medium: 5,
70
+ low: 3,
71
+ info: 1
72
+ };
73
+ var TOOL = {
74
+ NAME: "runa-vuln-checker",
75
+ VERSION: CLI_VERSION,
76
+ REPO_URL: "https://github.com/r06-dev/runa"
77
+ };
78
+
79
+ // src/internal/vuln-checker/analyzers/dependency-analyzer.ts
80
+ var execAsync = promisify(exec);
81
+ function levenshteinDistance(a, b) {
82
+ const matrix = [];
83
+ for (let i = 0; i <= a.length; i++) {
84
+ matrix[i] = [i];
85
+ }
86
+ for (let j = 0; j <= b.length; j++) {
87
+ matrix[0][j] = j;
88
+ }
89
+ for (let i = 1; i <= a.length; i++) {
90
+ for (let j = 1; j <= b.length; j++) {
91
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
92
+ matrix[i][j] = Math.min(
93
+ matrix[i - 1][j] + 1,
94
+ // deletion
95
+ matrix[i][j - 1] + 1,
96
+ // insertion
97
+ matrix[i - 1][j - 1] + cost
98
+ // substitution
99
+ );
100
+ }
101
+ }
102
+ return matrix[a.length][b.length];
103
+ }
104
+ var POPULAR_PACKAGES = [
105
+ // Web frameworks
106
+ "express",
107
+ "fastify",
108
+ "koa",
109
+ "hono",
110
+ "next",
111
+ "nuxt",
112
+ // React ecosystem
113
+ "react",
114
+ "react-dom",
115
+ "react-router",
116
+ "redux",
117
+ "zustand",
118
+ // Vue ecosystem
119
+ "vue",
120
+ "vuex",
121
+ "pinia",
122
+ // Utilities
123
+ "lodash",
124
+ "underscore",
125
+ "ramda",
126
+ "axios",
127
+ "node-fetch",
128
+ "got",
129
+ "moment",
130
+ "dayjs",
131
+ "date-fns",
132
+ // Build tools
133
+ "webpack",
134
+ "vite",
135
+ "esbuild",
136
+ "rollup",
137
+ "parcel",
138
+ "turbo",
139
+ // Testing
140
+ "jest",
141
+ "mocha",
142
+ "vitest",
143
+ "playwright",
144
+ "cypress",
145
+ // Type/schema
146
+ "typescript",
147
+ "zod",
148
+ "yup",
149
+ "joi",
150
+ // Database
151
+ "mongoose",
152
+ "sequelize",
153
+ "prisma",
154
+ "drizzle-orm",
155
+ "knex",
156
+ // CLI
157
+ "commander",
158
+ "yargs",
159
+ "inquirer",
160
+ "chalk",
161
+ "ora",
162
+ // Config
163
+ "dotenv",
164
+ "config",
165
+ "convict",
166
+ // Security
167
+ "jsonwebtoken",
168
+ "bcrypt",
169
+ "helmet",
170
+ "cors",
171
+ // Other popular
172
+ "uuid",
173
+ "nanoid",
174
+ "debug",
175
+ "winston",
176
+ "pino",
177
+ "socket.io",
178
+ "graphql",
179
+ "apollo-server"
180
+ ];
181
+ var KNOWN_TYPOSQUATS = {
182
+ // lodash variants
183
+ lodsh: "lodash",
184
+ loadash: "lodash",
185
+ lodahs: "lodash",
186
+ "lodash-js": "lodash",
187
+ // express variants
188
+ expres: "express",
189
+ expresss: "express",
190
+ exress: "express",
191
+ // react variants
192
+ reakt: "react",
193
+ reactt: "react",
194
+ raect: "react",
195
+ // axios variants
196
+ axois: "axios",
197
+ axioss: "axios",
198
+ axio: "axios",
199
+ // moment variants
200
+ momnet: "moment",
201
+ momet: "moment",
202
+ momment: "moment",
203
+ // commander variants
204
+ comander: "commander",
205
+ comandr: "commander",
206
+ // mongoose variants
207
+ mongose: "mongoose",
208
+ mongoos: "mongoose",
209
+ // dotenv variants
210
+ dotenve: "dotenv",
211
+ // webpack variants
212
+ webpck: "webpack",
213
+ weback: "webpack",
214
+ // typescript variants
215
+ typescrip: "typescript",
216
+ typescrit: "typescript",
217
+ // npm variants
218
+ npmm: "npm"
219
+ };
220
+ function mapSeverity(npmSeverity) {
221
+ switch (npmSeverity) {
222
+ case "critical":
223
+ return "critical";
224
+ case "high":
225
+ return "high";
226
+ case "moderate":
227
+ return "medium";
228
+ case "low":
229
+ return "low";
230
+ default:
231
+ return "info";
232
+ }
233
+ }
234
+ function extractVulnDetails(pkgName, vuln) {
235
+ let title = `Vulnerable dependency: ${pkgName}`;
236
+ let description = `Package ${pkgName} has known vulnerabilities.`;
237
+ let cveUrl;
238
+ if (Array.isArray(vuln.via)) {
239
+ for (const via of vuln.via) {
240
+ if (typeof via === "object" && "title" in via) {
241
+ title = via.title;
242
+ description = `${via.title} in ${pkgName}`;
243
+ cveUrl = via.url;
244
+ break;
245
+ }
246
+ }
247
+ }
248
+ return { title, description, cveUrl };
249
+ }
250
+ function buildFixDescription(vuln) {
251
+ if (!vuln.fixAvailable || typeof vuln.fixAvailable !== "object") {
252
+ return "Update to a patched version";
253
+ }
254
+ const fix = vuln.fixAvailable;
255
+ let desc = `Update to ${fix.name}@${fix.version}`;
256
+ if (fix.isSemVerMajor) {
257
+ desc += " (major version update required)";
258
+ }
259
+ return desc;
260
+ }
261
+ function createVulnFinding(pkgName, vuln, packageJsonPath) {
262
+ const severity = mapSeverity(vuln.severity);
263
+ const { title, description, cveUrl } = extractVulnDetails(pkgName, vuln);
264
+ const depType = vuln.isDirect ? " (direct dependency)" : " (transitive dependency)";
265
+ return {
266
+ ruleId: "dependency/cve",
267
+ severity,
268
+ title,
269
+ description: `${description}${depType}`,
270
+ location: { file: packageJsonPath, line: 1, column: 1 },
271
+ fix: { description: buildFixDescription(vuln) },
272
+ metadata: {
273
+ package: pkgName,
274
+ range: vuln.range,
275
+ isDirect: vuln.isDirect,
276
+ url: cveUrl
277
+ },
278
+ cweId: CWE.UNMAINTAINED_COMPONENTS,
279
+ owaspCategory: OWASP_2021.VULNERABLE_COMPONENTS,
280
+ confidence: 0.95
281
+ };
282
+ }
283
+ function createKnownTyposquatFinding(depName, legitimate, packageJsonPath) {
284
+ return {
285
+ ruleId: "dependency/typosquat",
286
+ severity: "high",
287
+ title: "Known Typosquatting Package",
288
+ description: `Package "${depName}" is a known typosquatting attempt of "${legitimate}"`,
289
+ location: { file: packageJsonPath, line: 1, column: 1 },
290
+ fix: { description: `Replace with the legitimate package "${legitimate}"` },
291
+ metadata: { suspicious: depName, legitimate },
292
+ cweId: CWE.UNTRUSTED_DOWNLOAD,
293
+ owaspCategory: OWASP_2021.INTEGRITY_FAILURES,
294
+ confidence: 0.95
295
+ };
296
+ }
297
+ function createSimilarityTyposquatFinding(depName, popular, distance, packageJsonPath) {
298
+ return {
299
+ ruleId: "dependency/typosquat",
300
+ severity: "medium",
301
+ title: "Potential Typosquatting Package",
302
+ description: `Package "${depName}" is very similar to popular package "${popular}" (distance: ${distance})`,
303
+ location: { file: packageJsonPath, line: 1, column: 1 },
304
+ fix: { description: `Verify this is the correct package. Did you mean "${popular}"?` },
305
+ metadata: { suspicious: depName, legitimate: popular, levenshteinDistance: distance },
306
+ cweId: CWE.UNTRUSTED_DOWNLOAD,
307
+ owaspCategory: OWASP_2021.INTEGRITY_FAILURES,
308
+ confidence: 0.6
309
+ };
310
+ }
311
+ function isLikelyPackageVariant(name, baseName) {
312
+ return name.startsWith(`${baseName}-`) || name.endsWith(`-${baseName}`) || name.includes(`${baseName}-`) || name.includes(`-${baseName}`);
313
+ }
314
+ function getMaxTyposquatDistance(length) {
315
+ if (length <= 4) return 1;
316
+ if (length <= 8) return 2;
317
+ return 3;
318
+ }
319
+ function checkTyposquat(depName, packageJsonPath) {
320
+ const nameWithoutScope = depName.replace(/^@[^/]+\//, "").toLowerCase();
321
+ const knownLegitimate = KNOWN_TYPOSQUATS[nameWithoutScope];
322
+ if (knownLegitimate) {
323
+ return createKnownTyposquatFinding(depName, knownLegitimate, packageJsonPath);
324
+ }
325
+ for (const popular of POPULAR_PACKAGES) {
326
+ if (nameWithoutScope === popular.toLowerCase()) continue;
327
+ const maxDistance = getMaxTyposquatDistance(popular.length);
328
+ const distance = levenshteinDistance(nameWithoutScope, popular.toLowerCase());
329
+ if (distance > 0 && distance <= maxDistance && !isLikelyPackageVariant(nameWithoutScope, popular)) {
330
+ return createSimilarityTyposquatFinding(depName, popular, distance, packageJsonPath);
331
+ }
332
+ }
333
+ return null;
334
+ }
335
+ var SUSPICIOUS_NAME_PATTERNS = [
336
+ // Single character variants (e.g., "lodash-a", "react-x")
337
+ {
338
+ pattern: /^[^@].*-[a-zA-Z]$/,
339
+ description: "Single letter suffix",
340
+ confidence: 0.5
341
+ },
342
+ // Random number suffix (e.g., "express-123", "react-42")
343
+ // But not version-like (e.g., "es6-promise" is OK)
344
+ {
345
+ pattern: /-[0-9]{2,}$/,
346
+ description: "Random number suffix",
347
+ confidence: 0.4
348
+ },
349
+ // Single letter prefix (e.g., "a-lodash", "x-react")
350
+ {
351
+ pattern: /^[a-zA-Z]-[a-zA-Z]{2,}/,
352
+ description: "Single letter prefix",
353
+ confidence: 0.5
354
+ },
355
+ // Homoglyph characters (common attack vector)
356
+ {
357
+ pattern: /[а-яА-Я]/,
358
+ // Cyrillic characters that look like Latin
359
+ description: "Contains non-ASCII characters that may be homoglyphs",
360
+ confidence: 0.9
361
+ },
362
+ // Very short scoped packages (e.g., "@x/y")
363
+ {
364
+ pattern: /^@[a-zA-Z]\/[a-zA-Z]$/,
365
+ description: "Very short scoped package name",
366
+ confidence: 0.6
367
+ },
368
+ // Package names that look like they're trying to impersonate orgs
369
+ {
370
+ pattern: /^(?:react|vue|angular|node|npm|yarn|pnpm|vercel|next|nuxt)-(?:official|org|team|core|dev)$/i,
371
+ description: "Potentially impersonating official package",
372
+ confidence: 0.7
373
+ }
374
+ ];
375
+ function checkSuspiciousName(depName, packageJsonPath) {
376
+ for (const { pattern, description, confidence } of SUSPICIOUS_NAME_PATTERNS) {
377
+ if (pattern.test(depName)) {
378
+ return {
379
+ ruleId: "dependency/suspicious",
380
+ severity: confidence >= 0.7 ? "high" : "medium",
381
+ title: "Suspicious Package Name",
382
+ description: `Package "${depName}" has a suspicious naming pattern: ${description}`,
383
+ location: { file: packageJsonPath, line: 1, column: 1 },
384
+ fix: { description: "Verify this package is legitimate before using" },
385
+ metadata: { package: depName, reason: description },
386
+ cweId: CWE.UNTRUSTED_DOWNLOAD,
387
+ owaspCategory: OWASP_2021.INTEGRITY_FAILURES,
388
+ confidence
389
+ };
390
+ }
391
+ }
392
+ return null;
393
+ }
394
+ var DependencyAnalyzer = class {
395
+ name = "DependencyAnalyzer";
396
+ categories = ["dependency"];
397
+ async analyze(options) {
398
+ const packageJsonPath = path2.join(options.rootDir, "package.json");
399
+ try {
400
+ await fs.access(packageJsonPath);
401
+ } catch {
402
+ return [];
403
+ }
404
+ const auditFindings = await this.runNpmAudit(options.rootDir, packageJsonPath);
405
+ const typosquatFindings = await this.checkTyposquatting(packageJsonPath);
406
+ return [...auditFindings, ...typosquatFindings];
407
+ }
408
+ async runNpmAudit(rootDir, packageJsonPath) {
409
+ try {
410
+ const stdout = await this.executeNpmAudit(rootDir);
411
+ const auditResult = JSON.parse(stdout);
412
+ return Object.entries(auditResult.vulnerabilities).map(
413
+ ([pkgName, vuln]) => createVulnFinding(pkgName, vuln, packageJsonPath)
414
+ );
415
+ } catch {
416
+ return [];
417
+ }
418
+ }
419
+ async executeNpmAudit(rootDir) {
420
+ try {
421
+ const { stdout } = await execAsync("npm audit --json", { cwd: rootDir });
422
+ return stdout;
423
+ } catch (error) {
424
+ const execError = error;
425
+ if (execError.stdout) {
426
+ return execError.stdout;
427
+ }
428
+ throw error;
429
+ }
430
+ }
431
+ async checkTyposquatting(packageJsonPath) {
432
+ try {
433
+ const content = await fs.readFile(packageJsonPath, "utf-8");
434
+ const packageJson = JSON.parse(content);
435
+ const allDeps = {
436
+ ...packageJson.dependencies || {},
437
+ ...packageJson.devDependencies || {}
438
+ };
439
+ const findings = [];
440
+ for (const depName of Object.keys(allDeps)) {
441
+ const typosquatFinding = checkTyposquat(depName, packageJsonPath);
442
+ if (typosquatFinding) findings.push(typosquatFinding);
443
+ const suspiciousFinding = checkSuspiciousName(depName, packageJsonPath);
444
+ if (suspiciousFinding) findings.push(suspiciousFinding);
445
+ }
446
+ return findings;
447
+ } catch {
448
+ return [];
449
+ }
450
+ }
451
+ };
452
+
453
+ // src/internal/vuln-checker/analyzers/rls-analyzer.ts
454
+ init_esm_shims();
455
+ var TENANT_COLUMN_PATTERNS = [
456
+ "tenant_id",
457
+ "client_id",
458
+ "org_id",
459
+ "organization_id",
460
+ "scope_id",
461
+ "account_id",
462
+ "workspace_id",
463
+ "team_id",
464
+ "company_id"
465
+ ];
466
+ var TENANT_ISOLATION_PATTERNS = [
467
+ // Direct comparisons with tenant columns (various formats)
468
+ // Handles: tenant_id = (auth.jwt() ->> 'tenant_id')::uuid
469
+ // Handles: client_id = auth.jwt()->>'client_id'
470
+ // Handles: scope_id = current_setting('app.scope_id')
471
+ /(?:tenant_id|client_id|org_id|organization_id|scope_id|account_id|workspace_id|team_id|company_id)\s*=\s*.*(?:jwt|auth|current_setting)/i,
472
+ // Schema-qualified column comparisons
473
+ // Handles: accounts.clients.tenant_id = ...
474
+ /[\w]+\.[\w]+\.(?:tenant_id|client_id|org_id|scope_id)\s*=\s*.*(?:jwt|auth)/i,
475
+ // Helper function patterns (common naming conventions)
476
+ // Handles: get_current_tenant(), get_my_tenant_id(), current_tenant_id(), etc.
477
+ /(?:get_)?(?:current_|my_)?(?:tenant|client|org|scope|account)(?:_id)?\s*\(\)/i,
478
+ // Supabase auth functions
479
+ /auth\.uid\s*\(\)/i,
480
+ /auth\.jwt\s*\(\)/i,
481
+ /auth\.role\s*\(\)/i,
482
+ // IN clause with tenant subquery
483
+ // Handles: tenant_id IN (SELECT id FROM get_user_tenants())
484
+ /(?:tenant_id|client_id|org_id|scope_id)\s+IN\s*\(/i,
485
+ // EXISTS with tenant check
486
+ // Handles: EXISTS (SELECT 1 FROM ... WHERE tenant_id = ...)
487
+ /EXISTS\s*\(\s*SELECT.*(?:tenant_id|client_id|scope_id)/i,
488
+ // CTE reference patterns
489
+ // Handles: WITH tenant AS (...) ... WHERE tenant_id = tenant.id
490
+ /WITH\s+(?:tenant|current_tenant|user_tenant)/i,
491
+ // PostgreSQL current_setting for tenant context
492
+ // Handles: current_setting('app.tenant_id', true)
493
+ /current_setting\s*\(\s*['"](?:app\.|rls\.)?(?:tenant|client|org|scope)/i,
494
+ // Request header / session patterns (some frameworks)
495
+ // Handles: request.header('x-tenant-id')
496
+ /request\.(?:header|cookie|session)\s*\(\s*['"].*(?:tenant|client|org)/i,
497
+ // Type-casted JWT claim extraction
498
+ // Handles: ((auth.jwt() ->> 'tenant_id')::uuid)
499
+ /\(\s*auth\.jwt\s*\(\)\s*->>?\s*['"][^'"]+['"]\s*\)\s*::/i
500
+ ];
501
+ var SYSTEM_TABLE_PATTERNS = ["_", "schema_migrations", "drizzle_migrations"];
502
+ var SUPABASE_STORAGE_TABLES = ["storage.objects", "storage.buckets"];
503
+ function isStorageTable(tableName) {
504
+ return SUPABASE_STORAGE_TABLES.includes(tableName.toLowerCase());
505
+ }
506
+ function isPgTableCall(callExpr) {
507
+ const expression = callExpr.getExpression();
508
+ if (Node.isIdentifier(expression)) {
509
+ const name = expression.getText();
510
+ return name === "pgTable" || name === "table";
511
+ }
512
+ if (Node.isPropertyAccessExpression(expression)) {
513
+ const propName = expression.getName();
514
+ return propName === "table";
515
+ }
516
+ return false;
517
+ }
518
+ function extractTableName(callExpr) {
519
+ const args = callExpr.getArguments();
520
+ if (args.length === 0) return null;
521
+ const firstArg = args[0];
522
+ if (Node.isStringLiteral(firstArg)) {
523
+ return firstArg.getLiteralValue();
524
+ }
525
+ return null;
526
+ }
527
+ function resolveSchemaFromDeclaration(sourceFile, schemaVarName) {
528
+ for (const decl of sourceFile.getVariableDeclarations()) {
529
+ if (decl.getName() !== schemaVarName) continue;
530
+ const init = decl.getInitializer();
531
+ if (!init || !Node.isCallExpression(init)) continue;
532
+ const callee = init.getExpression();
533
+ if (!Node.isIdentifier(callee) || callee.getText() !== "pgSchema") continue;
534
+ const schemaArgs = init.getArguments();
535
+ if (schemaArgs.length > 0 && Node.isStringLiteral(schemaArgs[0])) {
536
+ return schemaArgs[0].getLiteralValue();
537
+ }
538
+ }
539
+ return null;
540
+ }
541
+ function extractSchemaName(callExpr) {
542
+ const expression = callExpr.getExpression();
543
+ if (!Node.isPropertyAccessExpression(expression)) {
544
+ return "public";
545
+ }
546
+ const object = expression.getExpression();
547
+ if (!Node.isIdentifier(object)) {
548
+ return "public";
549
+ }
550
+ const schemaVarName = object.getText();
551
+ const sourceFile = callExpr.getSourceFile();
552
+ return resolveSchemaFromDeclaration(sourceFile, schemaVarName) ?? "public";
553
+ }
554
+ function isTenantColumnMatch(propName, initText) {
555
+ for (const tenantPattern of TENANT_COLUMN_PATTERNS) {
556
+ const matchesByName = propName === tenantPattern || propName.endsWith("Id");
557
+ const matchesByText = initText.includes(`'${tenantPattern}'`) || initText.includes(`"${tenantPattern}"`);
558
+ if (!matchesByName && !matchesByText) continue;
559
+ if (initText.includes("uuid(") && initText.includes(tenantPattern)) {
560
+ return propName;
561
+ }
562
+ }
563
+ return null;
564
+ }
565
+ function extractTenantColumnFromTable(callExpr) {
566
+ const args = callExpr.getArguments();
567
+ if (args.length < 2) return { hasTenantColumn: false };
568
+ const columnsArg = args[1];
569
+ if (!Node.isObjectLiteralExpression(columnsArg)) return { hasTenantColumn: false };
570
+ for (const prop of columnsArg.getProperties()) {
571
+ if (!Node.isPropertyAssignment(prop)) continue;
572
+ const propName = prop.getName();
573
+ const initializer = prop.getInitializer();
574
+ if (!initializer) continue;
575
+ const matchedName = isTenantColumnMatch(propName, initializer.getText());
576
+ if (matchedName) {
577
+ return { hasTenantColumn: true, tenantColumnName: matchedName };
578
+ }
579
+ }
580
+ return { hasTenantColumn: false };
581
+ }
582
+ function parseSchemaFileAST(sourceFile, filePath) {
583
+ const tables = [];
584
+ for (const varDecl of sourceFile.getVariableDeclarations()) {
585
+ const parent = varDecl.getParent()?.getParent();
586
+ if (!parent) continue;
587
+ const isExported = Node.isVariableStatement(parent) && parent.getModifiers()?.some((m) => m.getKind() === SyntaxKind.ExportKeyword);
588
+ if (!isExported) continue;
589
+ const initializer = varDecl.getInitializer();
590
+ if (!initializer || !Node.isCallExpression(initializer)) continue;
591
+ if (!isPgTableCall(initializer)) continue;
592
+ const tableName = extractTableName(initializer);
593
+ if (!tableName) continue;
594
+ const schemaName = extractSchemaName(initializer);
595
+ const { hasTenantColumn, tenantColumnName } = extractTenantColumnFromTable(initializer);
596
+ tables.push({
597
+ name: tableName,
598
+ schema: schemaName,
599
+ file: filePath,
600
+ line: varDecl.getStartLineNumber(),
601
+ hasRLS: false,
602
+ hasTenantColumn,
603
+ tenantColumnName
604
+ });
605
+ }
606
+ return tables;
607
+ }
608
+ async function parseSchemaFiles(rootDir) {
609
+ const schemaFiles = await glob(["**/schema/*.ts", "**/schema/**/*.ts"], {
610
+ cwd: rootDir,
611
+ ignore: ["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/*.spec.ts"],
612
+ absolute: true
613
+ });
614
+ const project = new Project({
615
+ skipAddingFilesFromTsConfig: true,
616
+ skipFileDependencyResolution: true
617
+ });
618
+ const tables = [];
619
+ for (const filePath of schemaFiles) {
620
+ try {
621
+ const sourceFile = project.addSourceFileAtPath(filePath);
622
+ const fileTables = parseSchemaFileAST(sourceFile, filePath);
623
+ tables.push(...fileTables);
624
+ } catch {
625
+ }
626
+ }
627
+ return tables;
628
+ }
629
+ function getLineNumber(content, matchIndex) {
630
+ return content.substring(0, matchIndex).split("\n").length;
631
+ }
632
+ function findPolicyMatches(content) {
633
+ const policyRegex = /CREATE\s+POLICY\s+(?:IF\s+NOT\s+EXISTS\s+)?["']?(\w+)["']?\s+ON\s+(?:["']?(\w+)["']?\.)?["']?(\w+)["']?\s+(?:FOR\s+(SELECT|INSERT|UPDATE|DELETE|ALL)\s+)?/gi;
634
+ const matches = [];
635
+ let match = policyRegex.exec(content);
636
+ while (match !== null) {
637
+ matches.push(match);
638
+ match = policyRegex.exec(content);
639
+ }
640
+ return matches;
641
+ }
642
+ function extractPolicyClauses(content, matchIndex) {
643
+ const remainder = content.substring(matchIndex);
644
+ const usingMatch = remainder.match(/USING\s*\(([\s\S]*?)(?:\)\s*(?:WITH\s+CHECK|;)|$)/i);
645
+ const using = usingMatch ? usingMatch[1].trim() : void 0;
646
+ const withCheckMatch = remainder.match(/WITH\s+CHECK\s*\(([\s\S]*?)\)\s*;/i);
647
+ const withCheck = withCheckMatch ? withCheckMatch[1].trim() : void 0;
648
+ return { using, withCheck };
649
+ }
650
+ function createPolicyFromMatch(match, content, filePath) {
651
+ const { using, withCheck } = extractPolicyClauses(content, match.index);
652
+ return {
653
+ name: match[1],
654
+ table: `${match[2] || "public"}.${match[3]}`,
655
+ operation: match[4]?.toUpperCase() || "ALL",
656
+ using,
657
+ withCheck,
658
+ file: filePath,
659
+ line: getLineNumber(content, match.index)
660
+ };
661
+ }
662
+ async function parseSqlFileForPolicies(filePath) {
663
+ const content = await fs.readFile(filePath, "utf-8");
664
+ const matches = findPolicyMatches(content);
665
+ return matches.map((match) => createPolicyFromMatch(match, content, filePath));
666
+ }
667
+ async function parseRLSPolicies(rootDir) {
668
+ const sqlFiles = await glob(["**/sql/**/*.sql", "**/rls/**/*.sql", "**/schemas/**/*.sql"], {
669
+ cwd: rootDir,
670
+ ignore: ["**/node_modules/**", "**/dist/**"],
671
+ absolute: true
672
+ });
673
+ const policies = [];
674
+ for (const filePath of sqlFiles) {
675
+ try {
676
+ const filePolicies = await parseSqlFileForPolicies(filePath);
677
+ policies.push(...filePolicies);
678
+ } catch {
679
+ }
680
+ }
681
+ return policies;
682
+ }
683
+ function normalizeClause(clause) {
684
+ return clause.replace(/--[^\n]*/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\s+/g, " ").trim();
685
+ }
686
+ function hasTenantIsolation(policy) {
687
+ const usingClause = normalizeClause(policy.using || "");
688
+ const withCheckClause = normalizeClause(policy.withCheck || "");
689
+ const hasUsingIsolation = TENANT_ISOLATION_PATTERNS.some((pattern) => pattern.test(usingClause));
690
+ if (policy.operation === "SELECT") {
691
+ return hasUsingIsolation;
692
+ }
693
+ if (policy.operation === "INSERT") {
694
+ const hasWithCheckIsolation = TENANT_ISOLATION_PATTERNS.some(
695
+ (pattern) => pattern.test(withCheckClause)
696
+ );
697
+ return hasWithCheckIsolation || hasUsingIsolation;
698
+ }
699
+ return hasUsingIsolation;
700
+ }
701
+ function isSystemTable(tableName) {
702
+ return SYSTEM_TABLE_PATTERNS.some(
703
+ (pattern) => pattern === "_" ? tableName.startsWith("_") : tableName === pattern
704
+ );
705
+ }
706
+ function createMissingRLSFinding(table, fullTableName) {
707
+ return {
708
+ ruleId: "rls/missing-policy",
709
+ severity: "critical",
710
+ title: "Missing RLS Policy",
711
+ description: `Table "${fullTableName}" does not have Row Level Security enabled. All data is accessible without restrictions.`,
712
+ location: { file: table.file, line: table.line, column: 1 },
713
+ fix: {
714
+ description: `Enable RLS and create policies for table "${fullTableName}"`,
715
+ replacement: `ALTER TABLE ${fullTableName} ENABLE ROW LEVEL SECURITY;
716
+ CREATE POLICY tenant_isolation ON ${fullTableName} USING (tenant_id = auth.jwt() ->> 'tenant_id');`
717
+ },
718
+ cweId: CWE.IMPROPER_ACCESS_CONTROL,
719
+ owaspCategory: OWASP_2021.BROKEN_ACCESS_CONTROL,
720
+ confidence: 0.9
721
+ };
722
+ }
723
+ function createWeakTenantFinding(table, fullTableName) {
724
+ return {
725
+ ruleId: "rls/weak-tenant",
726
+ severity: "high",
727
+ title: "Weak Tenant Isolation",
728
+ description: `Table "${fullTableName}" has a tenant column (${table.tenantColumnName}) but RLS policies don't enforce tenant isolation.`,
729
+ location: { file: table.file, line: table.line, column: 1 },
730
+ fix: {
731
+ description: "Add tenant isolation to RLS policy USING clause",
732
+ replacement: `USING (${table.tenantColumnName} = (auth.jwt() ->> 'tenant_id')::uuid)`
733
+ },
734
+ cweId: CWE.IMPROPER_ACCESS_CONTROL,
735
+ owaspCategory: OWASP_2021.BROKEN_ACCESS_CONTROL,
736
+ confidence: 0.8
737
+ };
738
+ }
739
+ function createPermissiveFinding(policy) {
740
+ return {
741
+ ruleId: "rls/permissive",
742
+ severity: "high",
743
+ title: "Overly Permissive RLS Policy",
744
+ description: `Policy "${policy.name}" on "${policy.table}" allows unrestricted access (USING true or empty).`,
745
+ location: { file: policy.file, line: policy.line, column: 1 },
746
+ fix: { description: "Add proper restrictions to the USING clause" },
747
+ cweId: CWE.IMPROPER_ACCESS_CONTROL,
748
+ owaspCategory: OWASP_2021.BROKEN_ACCESS_CONTROL,
749
+ confidence: 0.95
750
+ };
751
+ }
752
+ function isPermissivePolicy(policy) {
753
+ return policy.using === "true" || policy.using === "1=1" || policy.using === "";
754
+ }
755
+ function checkTableForIssues(table, tablesWithPolicies, policies) {
756
+ const findings = [];
757
+ const fullTableName = `${table.schema}.${table.name}`;
758
+ if (isSystemTable(table.name)) return findings;
759
+ if (!tablesWithPolicies.has(fullTableName)) {
760
+ if (isStorageTable(fullTableName)) {
761
+ findings.push({
762
+ ...createMissingRLSFinding(table, fullTableName),
763
+ severity: "medium",
764
+ description: `Storage table "${fullTableName}" \u2014 Supabase provides default storage policies. Custom RLS may still be needed.`,
765
+ confidence: 0.4
766
+ });
767
+ return findings;
768
+ }
769
+ findings.push(createMissingRLSFinding(table, fullTableName));
770
+ return findings;
771
+ }
772
+ if (table.hasTenantColumn) {
773
+ const tablePolicies = policies.filter((p) => p.table === fullTableName);
774
+ const hasIsolation = tablePolicies.some(hasTenantIsolation);
775
+ if (!hasIsolation && tablePolicies.length > 0) {
776
+ findings.push(createWeakTenantFinding(table, fullTableName));
777
+ }
778
+ }
779
+ return findings;
780
+ }
781
+ var RLSAnalyzer = class {
782
+ name = "RLSAnalyzer";
783
+ categories = ["rls"];
784
+ async analyze(options) {
785
+ const tables = await parseSchemaFiles(options.rootDir);
786
+ const policies = await parseRLSPolicies(options.rootDir);
787
+ const tablesWithPolicies = new Set(policies.map((p) => p.table));
788
+ const tableFindings = tables.flatMap(
789
+ (table) => checkTableForIssues(table, tablesWithPolicies, policies)
790
+ );
791
+ const policyFindings = policies.filter(isPermissivePolicy).map((policy) => {
792
+ if (isStorageTable(policy.table) && /SELECT/i.test(policy.operation)) {
793
+ return {
794
+ ...createPermissiveFinding(policy),
795
+ severity: "info",
796
+ description: `Policy "${policy.name}" on "${policy.table}" allows public SELECT. This is common for public file downloads. Verify this is intentional.`,
797
+ confidence: 0.3
798
+ };
799
+ }
800
+ return createPermissiveFinding(policy);
801
+ });
802
+ return [...tableFindings, ...policyFindings];
803
+ }
804
+ };
805
+
806
+ // src/internal/vuln-checker/analyzers/secret-analyzer.ts
807
+ init_esm_shims();
808
+ var MAX_LINE_LENGTH = 2e3;
809
+ var MAX_FILE_SIZE = 1024 * 1024;
810
+ var SECRET_SCAN_SKIPPED_RULE_ID = "secret/scan-skipped";
811
+ var SECRET_PATTERNS = [
812
+ // AWS
813
+ {
814
+ id: "aws-access-key",
815
+ name: "AWS Access Key",
816
+ // AWS Access Key IDs always start with AKIA (user) or ASIA (temporary)
817
+ pattern: /(?:AKIA|ASIA)[0-9A-Z]{16}/g,
818
+ severity: "critical",
819
+ description: "AWS Access Key ID detected",
820
+ cweId: CWE.HARDCODED_CREDENTIALS
821
+ },
822
+ {
823
+ id: "aws-secret-key",
824
+ name: "AWS Secret Key",
825
+ // AWS Secret Keys are 40 chars, but we require context clues to reduce false positives
826
+ // Look for assignment context: aws_secret, secret_key, AWS_SECRET, etc.
827
+ // SECURITY (Issue #463): Simplified pattern to avoid nested quantifiers
828
+ // Changed from nested optional groups to explicit alternation
829
+ pattern: /(?:aws_secret|aws-secret|awssecret|secret_key|secret-key|secretkey|secret_access_key|AWS_SECRET)\s{0,5}[:=]\s{0,5}['"]?([A-Za-z0-9/+=]{40})['"]?/gi,
830
+ severity: "critical",
831
+ description: "AWS Secret Access Key detected",
832
+ cweId: CWE.HARDCODED_CREDENTIALS
833
+ },
834
+ // GitHub
835
+ {
836
+ id: "github-token",
837
+ name: "GitHub Token",
838
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
839
+ pattern: /gh[pousr]_[A-Za-z0-9_]{36,100}/g,
840
+ severity: "critical",
841
+ description: "GitHub Personal Access Token detected",
842
+ cweId: CWE.HARDCODED_CREDENTIALS
843
+ },
844
+ {
845
+ id: "github-oauth",
846
+ name: "GitHub OAuth",
847
+ pattern: /gho_[A-Za-z0-9]{36}/g,
848
+ severity: "critical",
849
+ description: "GitHub OAuth Token detected",
850
+ cweId: CWE.HARDCODED_CREDENTIALS
851
+ },
852
+ // Google / Gemini
853
+ {
854
+ id: "google-api-key",
855
+ name: "Google / Gemini API Key",
856
+ pattern: /AIza[0-9A-Za-z\-_]{35}/g,
857
+ severity: "critical",
858
+ description: "Google API Key detected (used by Gemini, Vertex AI, Maps, etc.)",
859
+ cweId: CWE.HARDCODED_CREDENTIALS
860
+ },
861
+ {
862
+ id: "google-oauth",
863
+ name: "Google OAuth",
864
+ pattern: /[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com/g,
865
+ severity: "high",
866
+ description: "Google OAuth Client ID detected",
867
+ cweId: CWE.HARDCODED_CREDENTIALS
868
+ },
869
+ // Stripe
870
+ {
871
+ id: "stripe-secret",
872
+ name: "Stripe Secret Key",
873
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
874
+ pattern: /sk_live_[0-9a-zA-Z]{24,100}/g,
875
+ severity: "critical",
876
+ description: "Stripe Live Secret Key detected",
877
+ cweId: CWE.HARDCODED_CREDENTIALS
878
+ },
879
+ {
880
+ id: "stripe-test",
881
+ name: "Stripe Test Key",
882
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
883
+ pattern: /sk_test_[0-9a-zA-Z]{24,100}/g,
884
+ severity: "medium",
885
+ description: "Stripe Test Secret Key detected",
886
+ cweId: CWE.HARDCODED_CREDENTIALS
887
+ },
888
+ // Supabase
889
+ {
890
+ id: "supabase-service-role",
891
+ name: "Supabase Service Role Key",
892
+ // Supabase JWT: header.payload.signature
893
+ // Header: {"alg":"HS256","typ":"JWT"} (fixed)
894
+ // Payload prefix: {"iss":"supabase","ref": (24 bytes, 3-byte aligned = stable base64url)
895
+ // Service role marker: role":"service_role" = cm9sZSI6InNlcnZpY2Vfcm9sZSI (stable)
896
+ // Variable content between prefix and marker covers the encoded project ref
897
+ pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6[A-Za-z0-9_-]+cm9sZSI6InNlcnZpY2Vfcm9sZSI[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+/g,
898
+ severity: "critical",
899
+ description: "Supabase Service Role Key detected",
900
+ cweId: CWE.HARDCODED_CREDENTIALS
901
+ },
902
+ // Private Keys
903
+ // NOTE: Patterns are split to avoid triggering publish-safety checks when bundled
904
+ {
905
+ id: "private-key-rsa",
906
+ name: "RSA Private Key",
907
+ pattern: new RegExp("-----BEGIN RSA PRIVATE KEY-----", "g"),
908
+ severity: "critical",
909
+ description: "RSA Private Key detected",
910
+ cweId: CWE.HARDCODED_CRYPTO_KEY
911
+ },
912
+ {
913
+ id: "private-key-openssh",
914
+ name: "OpenSSH Private Key",
915
+ pattern: new RegExp("-----BEGIN OPENSSH PRIVATE KEY-----", "g"),
916
+ severity: "critical",
917
+ description: "OpenSSH Private Key detected",
918
+ cweId: CWE.HARDCODED_CRYPTO_KEY
919
+ },
920
+ {
921
+ id: "private-key-ec",
922
+ name: "EC Private Key",
923
+ pattern: new RegExp("-----BEGIN EC PRIVATE KEY-----", "g"),
924
+ severity: "critical",
925
+ description: "EC Private Key detected",
926
+ cweId: CWE.HARDCODED_CRYPTO_KEY
927
+ },
928
+ // Generic patterns
929
+ // SECURITY (Issue #463): All generic patterns use bounded quantifiers to prevent ReDoS
930
+ {
931
+ id: "password-assignment",
932
+ name: "Password Assignment",
933
+ // Limited whitespace to max 5 chars, limited value length to max 200 chars
934
+ pattern: /(?:password|passwd|pwd)\s{0,5}[:=]\s{0,5}['"][^'"]{8,200}['"]/gi,
935
+ severity: "high",
936
+ description: "Hardcoded password detected",
937
+ cweId: CWE.HARDCODED_CREDENTIALS
938
+ },
939
+ {
940
+ id: "api-key-assignment",
941
+ name: "API Key Assignment",
942
+ // Explicit alternation instead of nested optional groups
943
+ pattern: /(?:api_key|api-key|apikey)\s{0,5}[:=]\s{0,5}['"][^'"]{16,200}['"]/gi,
944
+ severity: "high",
945
+ description: "Hardcoded API key detected",
946
+ cweId: CWE.HARDCODED_CREDENTIALS
947
+ },
948
+ {
949
+ id: "secret-assignment",
950
+ name: "Secret Assignment",
951
+ pattern: /(?:secret|token)\s{0,5}[:=]\s{0,5}['"][^'"]{16,200}['"]/gi,
952
+ severity: "high",
953
+ description: "Hardcoded secret/token detected",
954
+ cweId: CWE.HARDCODED_CREDENTIALS
955
+ },
956
+ // Connection strings
957
+ {
958
+ id: "database-url",
959
+ name: "Database Connection String",
960
+ // Require actual credentials (not user:password placeholder patterns)
961
+ // Exclude common placeholder usernames: user, username, admin, root with simple passwords
962
+ // SECURITY (Issue #463): Bounded character classes to prevent ReDoS
963
+ // Limited each segment to max 200 chars
964
+ pattern: /(?:postgresql|mysql|mongodb|redis):\/\/(?!(?:user|username|admin|root):(?:password|pass|secret)@)[^\s'"]{1,200}:[^\s'"]{1,200}@[^\s'"]{1,200}/gi,
965
+ severity: "critical",
966
+ description: "Database connection string with credentials detected",
967
+ cweId: CWE.HARDCODED_CREDENTIALS
968
+ },
969
+ // JWT - Lower severity because JWTs in code are often:
970
+ // 1. Test tokens with known secrets
971
+ // 2. Example tokens from documentation
972
+ // 3. Expired tokens used for testing
973
+ //
974
+ // Pattern notes:
975
+ // - Header (eyJ...): Base64-encoded {"alg":...,"typ":"JWT"}
976
+ // - Payload (eyJ...): Base64-encoded claims
977
+ // - Signature: Variable length (32-512+ chars depending on algorithm)
978
+ // HS256: ~43 chars, RS256: ~342 chars, ES256: ~86 chars
979
+ {
980
+ id: "jwt-token",
981
+ name: "JWT Token",
982
+ // Improved: Require minimum signature length of 20 chars to reduce false positives
983
+ // from partial JWT-like strings in code
984
+ // SECURITY (Issue #463): Bounded quantifiers to prevent ReDoS
985
+ pattern: /eyJ[A-Za-z0-9_-]{10,500}\.eyJ[A-Za-z0-9_-]{10,1000}\.[A-Za-z0-9_-]{20,600}/g,
986
+ severity: "low",
987
+ // Changed from medium - often false positive
988
+ description: "JWT token detected (verify if production token)",
989
+ cweId: CWE.HARDCODED_CREDENTIALS
990
+ },
991
+ // Slack
992
+ {
993
+ id: "slack-token",
994
+ name: "Slack Token",
995
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
996
+ pattern: /xox[baprs]-[0-9A-Za-z-]{10,100}/g,
997
+ severity: "high",
998
+ description: "Slack token detected",
999
+ cweId: CWE.HARDCODED_CREDENTIALS
1000
+ },
1001
+ // Discord
1002
+ {
1003
+ id: "discord-token",
1004
+ name: "Discord Token",
1005
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
1006
+ pattern: /[MN][A-Za-z\d]{23,100}\.[\w-]{6}\.[\w-]{27}/g,
1007
+ severity: "high",
1008
+ description: "Discord bot token detected",
1009
+ cweId: CWE.HARDCODED_CREDENTIALS
1010
+ },
1011
+ // SendGrid
1012
+ {
1013
+ id: "sendgrid-api-key",
1014
+ name: "SendGrid API Key",
1015
+ pattern: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
1016
+ severity: "high",
1017
+ description: "SendGrid API key detected",
1018
+ cweId: CWE.HARDCODED_CREDENTIALS
1019
+ },
1020
+ // Twilio
1021
+ {
1022
+ id: "twilio-api-key",
1023
+ name: "Twilio API Key",
1024
+ pattern: /SK[a-f0-9]{32}/g,
1025
+ severity: "high",
1026
+ description: "Twilio API Key detected",
1027
+ cweId: CWE.HARDCODED_CREDENTIALS
1028
+ },
1029
+ // Mailchimp
1030
+ // Format: {32-char-hex}-us{datacenter}
1031
+ // Datacenter can be 1-3 digits (us1 through us999 for future-proofing)
1032
+ {
1033
+ id: "mailchimp-api-key",
1034
+ name: "Mailchimp API Key",
1035
+ pattern: /[a-f0-9]{32}-us[0-9]{1,3}/g,
1036
+ severity: "high",
1037
+ description: "Mailchimp API key detected",
1038
+ cweId: CWE.HARDCODED_CREDENTIALS
1039
+ },
1040
+ // npm
1041
+ {
1042
+ id: "npm-token",
1043
+ name: "npm Token",
1044
+ pattern: /npm_[A-Za-z0-9]{36}/g,
1045
+ severity: "high",
1046
+ description: "npm access token detected",
1047
+ cweId: CWE.HARDCODED_CREDENTIALS
1048
+ },
1049
+ // Vercel
1050
+ {
1051
+ id: "vercel-token",
1052
+ name: "Vercel Token",
1053
+ pattern: /vercel_[A-Za-z0-9]{24}/g,
1054
+ severity: "high",
1055
+ description: "Vercel token detected",
1056
+ cweId: CWE.HARDCODED_CREDENTIALS
1057
+ },
1058
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1059
+ // AI Service API Keys (2024-2026 patterns)
1060
+ // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1061
+ {
1062
+ id: "openai-api-key",
1063
+ name: "OpenAI API Key",
1064
+ // Format: sk-{48+ alphanumeric chars} (real keys are typically 51 chars)
1065
+ // SECURITY: Bounded quantifier to prevent ReDoS
1066
+ pattern: /sk-[a-zA-Z0-9]{48,200}/g,
1067
+ severity: "critical",
1068
+ description: "OpenAI API key detected",
1069
+ cweId: CWE.HARDCODED_CREDENTIALS
1070
+ },
1071
+ {
1072
+ id: "openai-project-key",
1073
+ name: "OpenAI Project Key",
1074
+ // Format: sk-proj-{48+ alphanumeric/dash/underscore chars}
1075
+ // SECURITY: Bounded quantifier to prevent ReDoS
1076
+ pattern: /sk-proj-[a-zA-Z0-9_-]{48,200}/g,
1077
+ severity: "critical",
1078
+ description: "OpenAI Project API key detected",
1079
+ cweId: CWE.HARDCODED_CREDENTIALS
1080
+ },
1081
+ {
1082
+ id: "anthropic-api-key",
1083
+ name: "Anthropic API Key",
1084
+ // Format: sk-ant-api03-{base64-like string}
1085
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
1086
+ pattern: /sk-ant-api\d{2}-[A-Za-z0-9_-]{40,200}/g,
1087
+ severity: "critical",
1088
+ description: "Anthropic (Claude) API key detected",
1089
+ cweId: CWE.HARDCODED_CREDENTIALS
1090
+ },
1091
+ {
1092
+ id: "cohere-api-key",
1093
+ name: "Cohere API Key",
1094
+ // Format: varies, but often starts with identifiable prefix
1095
+ // SECURITY (Issue #463): Explicit alternation instead of nested optional groups
1096
+ pattern: /(?:cohere_api_key|cohere-api-key|cohere_key|cohere-key|coherekey|COHERE_API_KEY|COHERE_KEY)\s{0,5}[:=]\s{0,5}['"][A-Za-z0-9]{40,200}['"]/g,
1097
+ severity: "high",
1098
+ description: "Cohere API key detected",
1099
+ cweId: CWE.HARDCODED_CREDENTIALS
1100
+ },
1101
+ {
1102
+ id: "mistral-api-key",
1103
+ name: "Mistral API Key",
1104
+ // Mistral keys have specific format
1105
+ // SECURITY (Issue #463): Explicit alternation instead of nested optional groups
1106
+ pattern: /(?:mistral_api_key|mistral-api-key|mistral_key|mistral-key|mistralkey|MISTRAL_API_KEY|MISTRAL_KEY)\s{0,5}[:=]\s{0,5}['"][A-Za-z0-9]{32,200}['"]/g,
1107
+ severity: "high",
1108
+ description: "Mistral AI API key detected",
1109
+ cweId: CWE.HARDCODED_CREDENTIALS
1110
+ },
1111
+ {
1112
+ id: "openrouter-api-key",
1113
+ name: "OpenRouter API Key",
1114
+ // Format: sk-or-v1-{64 chars}
1115
+ pattern: /sk-or-v1-[A-Za-z0-9]{64}/g,
1116
+ severity: "critical",
1117
+ description: "OpenRouter API key detected",
1118
+ cweId: CWE.HARDCODED_CREDENTIALS
1119
+ },
1120
+ {
1121
+ id: "replicate-api-key",
1122
+ name: "Replicate API Key",
1123
+ // Format: r8_...
1124
+ pattern: /r8_[A-Za-z0-9]{40}/g,
1125
+ severity: "high",
1126
+ description: "Replicate API key detected",
1127
+ cweId: CWE.HARDCODED_CREDENTIALS
1128
+ },
1129
+ {
1130
+ id: "huggingface-token",
1131
+ name: "HuggingFace Token",
1132
+ // Format: hf_...
1133
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
1134
+ pattern: /hf_[A-Za-z0-9]{34,100}/g,
1135
+ severity: "high",
1136
+ description: "HuggingFace API token detected",
1137
+ cweId: CWE.HARDCODED_CREDENTIALS
1138
+ },
1139
+ {
1140
+ id: "groq-api-key",
1141
+ name: "Groq API Key",
1142
+ // Format: gsk_...
1143
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
1144
+ pattern: /gsk_[A-Za-z0-9]{52,100}/g,
1145
+ severity: "critical",
1146
+ description: "Groq API key detected",
1147
+ cweId: CWE.HARDCODED_CREDENTIALS
1148
+ },
1149
+ {
1150
+ id: "together-api-key",
1151
+ name: "Together AI API Key",
1152
+ // Context-based detection
1153
+ // SECURITY (Issue #463): Explicit alternation instead of nested optional groups
1154
+ pattern: /(?:together_api_key|together-api-key|together_key|together-key|togetherkey|TOGETHER_API_KEY|TOGETHER_KEY)\s{0,5}[:=]\s{0,5}['"][A-Za-z0-9]{64,200}['"]/g,
1155
+ severity: "high",
1156
+ description: "Together AI API key detected",
1157
+ cweId: CWE.HARDCODED_CREDENTIALS
1158
+ },
1159
+ {
1160
+ id: "fireworks-api-key",
1161
+ name: "Fireworks AI API Key",
1162
+ // Format: fw_...
1163
+ // SECURITY (Issue #463): Bounded quantifier to prevent ReDoS
1164
+ pattern: /fw_[A-Za-z0-9]{40,100}/g,
1165
+ severity: "high",
1166
+ description: "Fireworks AI API key detected",
1167
+ cweId: CWE.HARDCODED_CREDENTIALS
1168
+ }
1169
+ ];
1170
+ var BASE64_SECRET_INDICATORS = [
1171
+ "sk-",
1172
+ // OpenAI, Stripe
1173
+ "ghp_",
1174
+ // GitHub PAT
1175
+ "gho_",
1176
+ // GitHub OAuth
1177
+ "AKIA",
1178
+ // AWS Access Key
1179
+ "eyJ",
1180
+ // JWT (already base64, double-encoded)
1181
+ "vercel_",
1182
+ // Vercel
1183
+ "sbp_"
1184
+ // Supabase
1185
+ ];
1186
+ function checkBase64ForSecrets(base64String) {
1187
+ try {
1188
+ if (!/^[A-Za-z0-9+/]+=*$/.test(base64String)) {
1189
+ return { found: false };
1190
+ }
1191
+ if (base64String.length < 20) {
1192
+ return { found: false };
1193
+ }
1194
+ const decoded = Buffer.from(base64String, "base64").toString("utf-8");
1195
+ for (const indicator of BASE64_SECRET_INDICATORS) {
1196
+ if (decoded.startsWith(indicator)) {
1197
+ const secretType = getSecretTypeFromIndicator(indicator);
1198
+ return { found: true, secretType };
1199
+ }
1200
+ }
1201
+ return { found: false };
1202
+ } catch {
1203
+ return { found: false };
1204
+ }
1205
+ }
1206
+ function getSecretTypeFromIndicator(indicator) {
1207
+ const indicatorMap = {
1208
+ "sk-": "OpenAI/Stripe API key",
1209
+ ghp_: "GitHub Personal Access Token",
1210
+ gho_: "GitHub OAuth Token",
1211
+ AKIA: "AWS Access Key",
1212
+ eyJ: "JWT Token",
1213
+ vercel_: "Vercel Token",
1214
+ sbp_: "Supabase Token"
1215
+ };
1216
+ return indicatorMap[indicator] ?? "Unknown secret type";
1217
+ }
1218
+ var EXCLUDED_PATTERNS = [
1219
+ /\.env\.example$/,
1220
+ /\.env\.sample$/,
1221
+ /\.env\.template$/,
1222
+ /node_modules/,
1223
+ /\.git\//,
1224
+ /dist\//,
1225
+ /\.next\//,
1226
+ /coverage\//,
1227
+ /\.lock$/,
1228
+ /lock\.json$/,
1229
+ /\.test\./,
1230
+ /\.spec\./,
1231
+ /__tests__/,
1232
+ /fixtures/,
1233
+ /mocks/
1234
+ ];
1235
+ function isCommentLine(line) {
1236
+ const trimmed = line.trim();
1237
+ return trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("*") || trimmed.startsWith("/*") || trimmed.startsWith("<!--") || trimmed.startsWith("--");
1238
+ }
1239
+ function isInDocumentationContext(line) {
1240
+ const lineLower = line.toLowerCase();
1241
+ const docPatterns = [
1242
+ "format:",
1243
+ "example:",
1244
+ "e.g.",
1245
+ "e.g:",
1246
+ "i.e.",
1247
+ "sample:",
1248
+ "template:",
1249
+ "pattern:",
1250
+ "@example",
1251
+ "@param",
1252
+ "@returns",
1253
+ "@see",
1254
+ "usage:",
1255
+ "syntax:",
1256
+ "like:",
1257
+ "such as",
1258
+ "documentation",
1259
+ "readme",
1260
+ "docs/"
1261
+ ];
1262
+ return docPatterns.some((p) => lineLower.includes(p));
1263
+ }
1264
+ function isInCodeExample(line) {
1265
+ const trimmed = line.trim();
1266
+ if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
1267
+ return true;
1268
+ }
1269
+ const exampleIndicators = [
1270
+ "const example",
1271
+ "let example",
1272
+ "var example",
1273
+ "// example",
1274
+ "# example",
1275
+ "example =",
1276
+ "example:"
1277
+ ];
1278
+ return exampleIndicators.some((p) => line.toLowerCase().includes(p));
1279
+ }
1280
+ function calculateEntropy(str) {
1281
+ if (str.length === 0) return 0;
1282
+ const freq = /* @__PURE__ */ new Map();
1283
+ for (const char of str) {
1284
+ freq.set(char, (freq.get(char) || 0) + 1);
1285
+ }
1286
+ let entropy = 0;
1287
+ const counts = Array.from(freq.values());
1288
+ for (const count of counts) {
1289
+ const p = count / str.length;
1290
+ entropy -= p * Math.log2(p);
1291
+ }
1292
+ return entropy;
1293
+ }
1294
+ function looksLikePlaceholder(match) {
1295
+ if (match.length > 500) {
1296
+ return false;
1297
+ }
1298
+ const matchLower = match.toLowerCase();
1299
+ const placeholders = [
1300
+ "your_",
1301
+ "your-",
1302
+ "yourkeyhere",
1303
+ "your_key",
1304
+ "replace_",
1305
+ "replace-",
1306
+ "replaceme",
1307
+ "xxx",
1308
+ "yyy",
1309
+ "zzz",
1310
+ "placeholder",
1311
+ "example",
1312
+ "sample",
1313
+ "test",
1314
+ "demo",
1315
+ "fake",
1316
+ "mock",
1317
+ "dummy",
1318
+ "todo",
1319
+ "fixme",
1320
+ "changeme",
1321
+ "<your",
1322
+ "[your",
1323
+ "{your",
1324
+ "****",
1325
+ "....",
1326
+ "----"
1327
+ ];
1328
+ if (placeholders.some((p) => matchLower.includes(p))) {
1329
+ return true;
1330
+ }
1331
+ if (match.length >= 8) {
1332
+ const uniqueChars = new Set(match).size;
1333
+ const uniqueRatio = uniqueChars / match.length;
1334
+ if (uniqueRatio < 0.15) {
1335
+ return true;
1336
+ }
1337
+ }
1338
+ if (/^[a-z]+$/.test(match) || /^[A-Z]+$/.test(match) || /^[0-9]+$/.test(match)) {
1339
+ return true;
1340
+ }
1341
+ return false;
1342
+ }
1343
+ function isCodeDefinitionContext(lineLower) {
1344
+ const codePatterns = [
1345
+ "type ",
1346
+ "interface ",
1347
+ ": string",
1348
+ "z.string()",
1349
+ "zod.",
1350
+ ".parse(",
1351
+ "regexp",
1352
+ "regex",
1353
+ "/g,",
1354
+ "/gi,"
1355
+ ];
1356
+ return codePatterns.some((p) => lineLower.includes(p));
1357
+ }
1358
+ function isTestContext(lineLower) {
1359
+ const testPatterns = ["expect(", "assert", "should."];
1360
+ return testPatterns.some((p) => lineLower.includes(p));
1361
+ }
1362
+ function isEnvReference(lineLower) {
1363
+ const envPatterns = ["process.env", "env.", "getenv"];
1364
+ return envPatterns.some((p) => lineLower.includes(p));
1365
+ }
1366
+ function isLikelyFalsePositive(line, match, patternId) {
1367
+ const lineLower = line.toLowerCase();
1368
+ if (isCommentLine(line)) return true;
1369
+ if (isInDocumentationContext(line)) return true;
1370
+ if (isInCodeExample(line)) return true;
1371
+ if (isEnvReference(lineLower)) return true;
1372
+ if (looksLikePlaceholder(match)) return true;
1373
+ if (isCodeDefinitionContext(lineLower)) return true;
1374
+ if (isTestContext(lineLower)) return true;
1375
+ if (patternId === "aws-secret-key" && calculateEntropy(match) < 3.5) {
1376
+ return true;
1377
+ }
1378
+ return false;
1379
+ }
1380
+ function maskSecret(secret) {
1381
+ if (secret.length > 10) {
1382
+ return `${secret.substring(0, 5)}...${secret.substring(secret.length - 3)}`;
1383
+ }
1384
+ return "***";
1385
+ }
1386
+ function calculateConfidence(pattern) {
1387
+ const highConfidencePatterns = [
1388
+ "aws-access-key",
1389
+ // AKIA/ASIA prefix is unique to AWS
1390
+ "github-token",
1391
+ // ghp_/gho_/etc prefix is unique
1392
+ "github-oauth",
1393
+ "openai-api-key",
1394
+ // sk- prefix with length constraint
1395
+ "openai-project-key",
1396
+ // sk-proj- prefix is unique
1397
+ "anthropic-api-key",
1398
+ // sk-ant-api prefix is unique
1399
+ "google-api-key",
1400
+ // AIza prefix is unique to Google (Gemini, Vertex AI, etc.)
1401
+ "stripe-secret",
1402
+ // sk_live_ prefix is unique
1403
+ "stripe-test",
1404
+ // sk_test_ prefix is unique
1405
+ "sendgrid-api-key",
1406
+ // SG. prefix is unique
1407
+ "npm-token",
1408
+ // npm_ prefix is unique
1409
+ "vercel-token",
1410
+ // vercel_ prefix is unique
1411
+ "private-key-rsa",
1412
+ // BEGIN RSA PRIVATE KEY is unique
1413
+ "private-key-openssh",
1414
+ "private-key-ec",
1415
+ "slack-token"
1416
+ // xox prefix is unique
1417
+ ];
1418
+ if (highConfidencePatterns.includes(pattern.id)) {
1419
+ return 0.95;
1420
+ }
1421
+ const mediumConfidencePatterns = [
1422
+ "google-oauth",
1423
+ "database-url",
1424
+ // Has structure but could be example
1425
+ "aws-secret-key"
1426
+ // Requires context clues
1427
+ ];
1428
+ if (mediumConfidencePatterns.includes(pattern.id)) {
1429
+ return 0.7;
1430
+ }
1431
+ return 0.5;
1432
+ }
1433
+ function createSecretFinding(pattern, filePath, lineNumber, line, match) {
1434
+ const matchText = match[0];
1435
+ const maskedSecret = maskSecret(matchText);
1436
+ const confidence = calculateConfidence(pattern);
1437
+ return {
1438
+ ruleId: `secret/${pattern.id}`,
1439
+ severity: pattern.severity,
1440
+ title: pattern.name,
1441
+ description: `${pattern.description}. Value: ${maskedSecret}`,
1442
+ location: {
1443
+ file: filePath,
1444
+ line: lineNumber,
1445
+ column: match.index
1446
+ },
1447
+ snippet: {
1448
+ text: line.length > 100 ? `${line.substring(0, 100)}...` : line,
1449
+ highlightStart: match.index,
1450
+ highlightEnd: match.index + matchText.length
1451
+ },
1452
+ fix: {
1453
+ description: "Move secret to environment variable",
1454
+ replacement: `process.env.${pattern.id.toUpperCase().replace(/-/g, "_")}`
1455
+ },
1456
+ cweId: pattern.cweId,
1457
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
1458
+ confidence
1459
+ };
1460
+ }
1461
+ function findPatternMatches(pattern, line) {
1462
+ pattern.pattern.lastIndex = 0;
1463
+ const matches = [];
1464
+ let match = pattern.pattern.exec(line);
1465
+ while (match !== null) {
1466
+ matches.push(match);
1467
+ match = pattern.pattern.exec(line);
1468
+ }
1469
+ return matches;
1470
+ }
1471
+ function collectPatternSecretFindings(line, lineNumber, filePath) {
1472
+ const findings = [];
1473
+ for (const pattern of SECRET_PATTERNS) {
1474
+ const matches = findPatternMatches(pattern, line);
1475
+ for (const match of matches) {
1476
+ if (isLikelyFalsePositive(line, match[0], pattern.id)) continue;
1477
+ findings.push(createSecretFinding(pattern, filePath, lineNumber, line, match));
1478
+ }
1479
+ }
1480
+ return findings;
1481
+ }
1482
+ var BASE64_SECRET_PATTERN = /['"`]([A-Za-z0-9+/]{40,500}=*)['"`]/g;
1483
+ function createBase64SecretFinding(line, lineNumber, filePath, base64Match, secretType) {
1484
+ const start = base64Match.index ?? 0;
1485
+ const fullMatch = base64Match[0] ?? "";
1486
+ return {
1487
+ ruleId: "secret/base64-encoded",
1488
+ severity: "high",
1489
+ title: "Base64-Encoded Secret",
1490
+ description: `Base64-encoded secret detected. Type: ${secretType}`,
1491
+ location: {
1492
+ file: filePath,
1493
+ line: lineNumber,
1494
+ column: start
1495
+ },
1496
+ snippet: {
1497
+ text: line.length > 100 ? `${line.substring(0, 100)}...` : line,
1498
+ highlightStart: start,
1499
+ highlightEnd: start + fullMatch.length
1500
+ },
1501
+ fix: {
1502
+ description: "Remove encoded secret and use environment variable",
1503
+ replacement: "process.env.SECRET_KEY"
1504
+ },
1505
+ cweId: CWE.HARDCODED_CREDENTIALS,
1506
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
1507
+ confidence: 0.85
1508
+ };
1509
+ }
1510
+ function createSkippedLineScanFinding(filePath, firstLineNumber, skippedLineCount) {
1511
+ return {
1512
+ ruleId: SECRET_SCAN_SKIPPED_RULE_ID,
1513
+ severity: "low",
1514
+ title: "Secret Scan Partially Skipped",
1515
+ description: `Secret scan skipped ${skippedLineCount} overlong line(s) exceeding ${MAX_LINE_LENGTH} characters.`,
1516
+ location: {
1517
+ file: filePath,
1518
+ line: firstLineNumber,
1519
+ column: 0
1520
+ },
1521
+ fix: {
1522
+ description: "Split minified or generated content so the scanner can inspect each line safely"
1523
+ },
1524
+ metadata: {
1525
+ skippedLineCount,
1526
+ maxLineLength: MAX_LINE_LENGTH
1527
+ },
1528
+ cweId: CWE.HARDCODED_CREDENTIALS,
1529
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
1530
+ confidence: 1
1531
+ };
1532
+ }
1533
+ function createSkippedFileScanFinding(filePath, sizeBytes) {
1534
+ return {
1535
+ ruleId: SECRET_SCAN_SKIPPED_RULE_ID,
1536
+ severity: "low",
1537
+ title: "Secret Scan Skipped Large File",
1538
+ description: `Secret scan skipped a file larger than ${MAX_FILE_SIZE} bytes.`,
1539
+ location: {
1540
+ file: filePath,
1541
+ line: 1,
1542
+ column: 0
1543
+ },
1544
+ fix: {
1545
+ description: "Reduce file size or split generated artifacts so secret scanning can inspect the content"
1546
+ },
1547
+ metadata: {
1548
+ sizeBytes,
1549
+ maxFileSize: MAX_FILE_SIZE
1550
+ },
1551
+ cweId: CWE.HARDCODED_CREDENTIALS,
1552
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
1553
+ confidence: 1
1554
+ };
1555
+ }
1556
+ function collectBase64SecretFindings(line, lineNumber, filePath) {
1557
+ const findings = [];
1558
+ for (const base64Match of line.matchAll(BASE64_SECRET_PATTERN)) {
1559
+ const base64String = base64Match[1];
1560
+ if (!base64String) continue;
1561
+ const result = checkBase64ForSecrets(base64String);
1562
+ if (!result.found) continue;
1563
+ if (isLikelyFalsePositive(line, base64String, "base64-secret")) continue;
1564
+ const secretType = result.secretType ?? "Unknown secret";
1565
+ findings.push(createBase64SecretFinding(line, lineNumber, filePath, base64Match, secretType));
1566
+ }
1567
+ return findings;
1568
+ }
1569
+ function scanLineForSecrets(line, lineNumber, filePath) {
1570
+ if (line.length > MAX_LINE_LENGTH) {
1571
+ return [];
1572
+ }
1573
+ return [
1574
+ ...collectPatternSecretFindings(line, lineNumber, filePath),
1575
+ ...collectBase64SecretFindings(line, lineNumber, filePath)
1576
+ ];
1577
+ }
1578
+ async function scanFileForSecrets(filePath) {
1579
+ const stats = await fs.stat(filePath);
1580
+ if (stats.size > MAX_FILE_SIZE) {
1581
+ return [createSkippedFileScanFinding(filePath, stats.size)];
1582
+ }
1583
+ const content = await fs.readFile(filePath, "utf-8");
1584
+ const lines = content.split("\n");
1585
+ const findings = [];
1586
+ let skippedLineCount = 0;
1587
+ let firstSkippedLineNumber = null;
1588
+ for (let i = 0; i < lines.length; i++) {
1589
+ const lineNumber = i + 1;
1590
+ if (lines[i].length > MAX_LINE_LENGTH) {
1591
+ skippedLineCount += 1;
1592
+ firstSkippedLineNumber ??= lineNumber;
1593
+ continue;
1594
+ }
1595
+ const lineFindings = scanLineForSecrets(lines[i], lineNumber, filePath);
1596
+ findings.push(...lineFindings);
1597
+ }
1598
+ if (firstSkippedLineNumber !== null) {
1599
+ findings.push(createSkippedLineScanFinding(filePath, firstSkippedLineNumber, skippedLineCount));
1600
+ }
1601
+ const multiLineFindings = scanMultiLineSecrets(content, filePath);
1602
+ findings.push(...multiLineFindings);
1603
+ return findings;
1604
+ }
1605
+ var MULTILINE_PATTERNS = [
1606
+ {
1607
+ id: "private-key-block",
1608
+ name: "Private Key Block",
1609
+ startPattern: new RegExp(
1610
+ "-----BEGIN\\s+(?:RSA\\s+|EC\\s+|DSA\\s+|OPENSSH\\s+)?PRIVATE\\s+KEY-----"
1611
+ ),
1612
+ endPattern: new RegExp(
1613
+ "-----END\\s+(?:RSA\\s+|EC\\s+|DSA\\s+|OPENSSH\\s+)?PRIVATE\\s+KEY-----"
1614
+ ),
1615
+ severity: "critical",
1616
+ description: "Private key block detected"
1617
+ },
1618
+ {
1619
+ id: "pgp-private-key-block",
1620
+ name: "PGP Private Key Block",
1621
+ startPattern: new RegExp("-----BEGIN\\s+PGP\\s+PRIVATE\\s+KEY\\s+BLOCK-----"),
1622
+ endPattern: new RegExp("-----END\\s+PGP\\s+PRIVATE\\s+KEY\\s+BLOCK-----"),
1623
+ severity: "critical",
1624
+ description: "PGP private key block detected"
1625
+ },
1626
+ {
1627
+ id: "certificate-block",
1628
+ name: "Certificate Block",
1629
+ startPattern: /-----BEGIN\s+CERTIFICATE-----/,
1630
+ endPattern: /-----END\s+CERTIFICATE-----/,
1631
+ severity: "medium",
1632
+ description: "Certificate block detected (verify if private)"
1633
+ }
1634
+ ];
1635
+ function scanMultiLineSecrets(content, filePath) {
1636
+ const findings = [];
1637
+ const lines = content.split("\n");
1638
+ for (const pattern of MULTILINE_PATTERNS) {
1639
+ let startLine = -1;
1640
+ let endLine = -1;
1641
+ for (let i = 0; i < lines.length; i++) {
1642
+ if (startLine === -1 && pattern.startPattern.test(lines[i])) {
1643
+ startLine = i;
1644
+ } else if (startLine !== -1 && pattern.endPattern.test(lines[i])) {
1645
+ endLine = i;
1646
+ const blockContent = lines.slice(startLine, endLine + 1).join("\n");
1647
+ if (isMultiLineFalsePositive(blockContent, lines, startLine)) {
1648
+ startLine = -1;
1649
+ endLine = -1;
1650
+ continue;
1651
+ }
1652
+ findings.push({
1653
+ ruleId: `secret/${pattern.id}`,
1654
+ severity: pattern.severity,
1655
+ title: pattern.name,
1656
+ description: `${pattern.description} (${endLine - startLine + 1} lines)`,
1657
+ location: {
1658
+ file: filePath,
1659
+ line: startLine + 1,
1660
+ column: 0
1661
+ },
1662
+ snippet: {
1663
+ text: `${lines[startLine].substring(0, 60)}...`,
1664
+ highlightStart: 0,
1665
+ highlightEnd: lines[startLine].length
1666
+ },
1667
+ fix: {
1668
+ description: "Remove private key and use environment variable or secret manager",
1669
+ replacement: "process.env.PRIVATE_KEY"
1670
+ },
1671
+ cweId: CWE.HARDCODED_CRYPTO_KEY,
1672
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
1673
+ confidence: 0.9
1674
+ });
1675
+ startLine = -1;
1676
+ endLine = -1;
1677
+ }
1678
+ }
1679
+ }
1680
+ return findings;
1681
+ }
1682
+ function isMultiLineFalsePositive(blockContent, lines, startLine) {
1683
+ const contentLower = blockContent.toLowerCase();
1684
+ if (/placeholder|example|sample|test|demo|fake|mock|dummy/.test(contentLower)) {
1685
+ return true;
1686
+ }
1687
+ const blockLines = blockContent.split("\n");
1688
+ if (blockLines.length <= 3) {
1689
+ return true;
1690
+ }
1691
+ if (startLine > 0) {
1692
+ const prevLine = lines[startLine - 1].toLowerCase();
1693
+ if (prevLine.includes("example") || prevLine.includes("template") || prevLine.includes("placeholder") || prevLine.includes("format:") || prevLine.trim().startsWith("//")) {
1694
+ return true;
1695
+ }
1696
+ }
1697
+ return false;
1698
+ }
1699
+ var SecretAnalyzer = class {
1700
+ name = "SecretAnalyzer";
1701
+ categories = ["secret"];
1702
+ async analyze(options) {
1703
+ const files = await this.getFilesToScan(options);
1704
+ const findings = [];
1705
+ for (const filePath of files) {
1706
+ if (this.isExcluded(filePath)) continue;
1707
+ try {
1708
+ const fileFindings = await scanFileForSecrets(filePath);
1709
+ findings.push(...fileFindings);
1710
+ } catch {
1711
+ }
1712
+ }
1713
+ return findings;
1714
+ }
1715
+ async getFilesToScan(options) {
1716
+ return glob(
1717
+ ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.json", "**/*.yml", "**/*.yaml", "**/*.env*"],
1718
+ {
1719
+ cwd: options.rootDir,
1720
+ ignore: options.exclude || ["**/node_modules/**", "**/dist/**"],
1721
+ absolute: true,
1722
+ dot: true
1723
+ }
1724
+ );
1725
+ }
1726
+ isExcluded(filePath) {
1727
+ return EXCLUDED_PATTERNS.some((pattern) => pattern.test(filePath));
1728
+ }
1729
+ };
1730
+
1731
+ // src/internal/vuln-checker/analyzers/typescript-analyzer.ts
1732
+ init_esm_shims();
1733
+ function getLocation(node, filePath) {
1734
+ const startLine = node.getStartLineNumber();
1735
+ const startCol = node.getStartLinePos();
1736
+ const endLine = node.getEndLineNumber();
1737
+ return {
1738
+ file: filePath,
1739
+ line: startLine,
1740
+ column: startCol,
1741
+ endLine
1742
+ };
1743
+ }
1744
+ function getSnippet(node) {
1745
+ const text = node.getText();
1746
+ return {
1747
+ text: text.length > 200 ? `${text.substring(0, 200)}...` : text
1748
+ };
1749
+ }
1750
+ var USER_INPUT_PATTERNS = [
1751
+ /\breq(?:uest)?\.(?:params|query|body|headers|cookies)/i,
1752
+ /\bparams\./i,
1753
+ /\bquery\./i,
1754
+ /\bbody\./i,
1755
+ /\buser(?:Input|Data|Id)?\b/i,
1756
+ /\binput\b/i,
1757
+ /\bargs?\b/i,
1758
+ /\bdata\./i,
1759
+ /\bctx\.(?:params|query|body)/i
1760
+ // Koa/Hono context
1761
+ ];
1762
+ var SAFE_VARIABLE_PATTERNS = [
1763
+ /\.parse\s*\(/,
1764
+ // Zod .parse() result
1765
+ /\.safeParse\s*\(/,
1766
+ // Zod .safeParse() result
1767
+ /validated\w*/i,
1768
+ // validatedInput, validatedData
1769
+ /sanitized\w*/i,
1770
+ // sanitizedInput
1771
+ /parsed\w*/i,
1772
+ // parsedBody, parsedParams
1773
+ /\bconst\s+\w+\s*:\s*(string|number|boolean)\b/i
1774
+ // Typed constants
1775
+ ];
1776
+ function hasUserInputInterpolation(text) {
1777
+ if (USER_INPUT_PATTERNS.some((p) => p.test(text))) return true;
1778
+ if (text.includes("${")) {
1779
+ const interpolations = text.match(/\$\{([^}]+)\}/g);
1780
+ if (!interpolations) return false;
1781
+ for (const expr of interpolations) {
1782
+ const innerExpr = expr.slice(2, -1).trim();
1783
+ if (/^\d+$/.test(innerExpr)) continue;
1784
+ if (SAFE_VARIABLE_PATTERNS.some((p) => p.test(text))) continue;
1785
+ if (USER_INPUT_PATTERNS.some((p) => p.test(innerExpr))) {
1786
+ return true;
1787
+ }
1788
+ }
1789
+ return false;
1790
+ }
1791
+ return false;
1792
+ }
1793
+ function hasArrayBasedSqlBuilding(text) {
1794
+ const arrayBuildPatterns = [
1795
+ /\.push\s*\(\s*`[^`]*\$\{/i,
1796
+ // conditions.push(`field = ${value}`)
1797
+ /\.push\s*\(\s*['"][^'"]*\s*\+/i,
1798
+ // conditions.push('field = ' + value)
1799
+ /\.join\s*\(\s*['"](?:\s*AND\s*|\s*OR\s*|\s*,\s*)['"]\s*\)/i
1800
+ // .join(' AND ')
1801
+ ];
1802
+ return arrayBuildPatterns.some((p) => p.test(text));
1803
+ }
1804
+ function buildDefinitionPositionsFromNodes(nodes) {
1805
+ return new Set(nodes.map((node) => `${node.getSourceFile().getFilePath()}:${node.getPos()}`));
1806
+ }
1807
+ function hasDefinitionMatch(node, definitionPositions) {
1808
+ if (!Node.isIdentifier(node)) return false;
1809
+ const nodeDefinitions = node.getDefinitions().map((definition) => definition.getNode());
1810
+ return nodeDefinitions.some(
1811
+ (definitionNode) => definitionPositions.has(
1812
+ `${definitionNode.getSourceFile().getFilePath()}:${definitionNode.getPos()}`
1813
+ )
1814
+ );
1815
+ }
1816
+ function buildJoinedArrayBuilderRisk(node, sourceFile) {
1817
+ const joinTarget = getIdentifierFromJoinCall(node);
1818
+ if (!joinTarget) return null;
1819
+ const scopeNode = node.getFirstAncestor((ancestor) => Node.isBlock(ancestor) || Node.isSourceFile(ancestor)) ?? sourceFile;
1820
+ if (isDynamicArrayPushInScope(scopeNode.getText(), joinTarget.getText())) {
1821
+ return {
1822
+ confidence: 0.6,
1823
+ description: "SQL query variable may be built dynamically from array concatenation."
1824
+ };
1825
+ }
1826
+ const arrayDefinitionPositions = getDefinitionPositions(joinTarget);
1827
+ if (arrayDefinitionPositions.size === 0) return null;
1828
+ if (hasDynamicPushArgumentFromDefinition(
1829
+ sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression),
1830
+ arrayDefinitionPositions
1831
+ )) {
1832
+ return {
1833
+ confidence: 0.6,
1834
+ description: "SQL query variable may be built dynamically from array concatenation."
1835
+ };
1836
+ }
1837
+ return null;
1838
+ }
1839
+ function getIdentifierFromJoinCall(node) {
1840
+ if (!Node.isCallExpression(node)) return null;
1841
+ const expression = node.getExpression();
1842
+ if (!Node.isPropertyAccessExpression(expression) || expression.getName() !== "join") return null;
1843
+ const joinTarget = expression.getExpression();
1844
+ return Node.isIdentifier(joinTarget) ? joinTarget : null;
1845
+ }
1846
+ function isDynamicArrayPushInScope(scopedText, arrayName) {
1847
+ return hasDynamicArrayPush(scopedText, arrayName);
1848
+ }
1849
+ function getDefinitionPositions(identifier) {
1850
+ return buildDefinitionPositionsFromNodes(
1851
+ identifier.getDefinitions().map((definition) => definition.getNode()).filter((node) => node !== void 0)
1852
+ );
1853
+ }
1854
+ function hasDynamicPushArgumentFromDefinition(callExpressions, definitionPositions) {
1855
+ for (const call of callExpressions) {
1856
+ const pushExpression = call.getExpression();
1857
+ if (!Node.isPropertyAccessExpression(pushExpression) || pushExpression.getName() !== "push") {
1858
+ continue;
1859
+ }
1860
+ const pushTarget = pushExpression.getExpression();
1861
+ if (!Node.isIdentifier(pushTarget) || !hasDefinitionMatch(pushTarget, definitionPositions)) {
1862
+ continue;
1863
+ }
1864
+ const pushArg = call.getArguments()[0];
1865
+ if (!pushArg) continue;
1866
+ if ((Node.isTemplateExpression(pushArg) || Node.isBinaryExpression(pushArg)) && hasUserInputInterpolation(pushArg.getText())) {
1867
+ return true;
1868
+ }
1869
+ }
1870
+ return false;
1871
+ }
1872
+ function hasDynamicArrayPush(scopedText, arrayName) {
1873
+ return new RegExp(`\\b${arrayName}\\.push\\s*\\(\\s*\`[^\\\`]*\\$\\{`, "m").test(scopedText) || new RegExp(`\\b${arrayName}\\.push\\s*\\(\\s*['"][^'"]*['"]\\s*\\+`, "m").test(scopedText);
1874
+ }
1875
+ function collectIdentifierDefinitionRisk(definitionNode, sourceFile) {
1876
+ if (!Node.isVariableDeclaration(definitionNode)) return null;
1877
+ const initializer = definitionNode.getInitializer();
1878
+ if (!initializer) return null;
1879
+ const risk = classifyDynamicSqlNode(initializer);
1880
+ if (risk) return risk;
1881
+ return buildJoinedArrayBuilderRisk(initializer, sourceFile);
1882
+ }
1883
+ function detectAssignmentRisk(sourceFile, identifierName, definitionPositions) {
1884
+ for (const expression of sourceFile.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
1885
+ if (expression.getOperatorToken().getKind() !== SyntaxKind.EqualsToken) continue;
1886
+ const left = expression.getLeft();
1887
+ if (!Node.isIdentifier(left) || left.getText() !== identifierName) continue;
1888
+ if (!hasDefinitionMatch(left, definitionPositions)) continue;
1889
+ const risk = classifyDynamicSqlNode(expression.getRight());
1890
+ if (risk) return risk;
1891
+ const joinRisk = buildJoinedArrayBuilderRisk(expression.getRight(), sourceFile);
1892
+ if (joinRisk) return joinRisk;
1893
+ }
1894
+ return null;
1895
+ }
1896
+ function detectIdentifierJoinPattern(firstArg, identifierName, sourceFile) {
1897
+ const scopeNode = firstArg.getFirstAncestor(
1898
+ (ancestor) => Node.isBlock(ancestor) || Node.isSourceFile(ancestor)
1899
+ ) ?? sourceFile;
1900
+ const scopedText = scopeNode.getText();
1901
+ const identifierJoinPattern = new RegExp(
1902
+ `(?:const|let|var)\\s+${identifierName}\\s*=\\s*(\\w+)\\.join\\s*\\(`,
1903
+ "m"
1904
+ );
1905
+ const joinMatch = scopedText.match(identifierJoinPattern);
1906
+ if (!joinMatch?.[1]) return null;
1907
+ const arrayName = joinMatch[1];
1908
+ if (!hasDynamicArrayPush(scopedText, arrayName)) return null;
1909
+ return {
1910
+ confidence: 0.6,
1911
+ description: "SQL query variable may be built dynamically from array concatenation."
1912
+ };
1913
+ }
1914
+ function isDynamicQueryBuilder(text) {
1915
+ const builderPatterns = [
1916
+ /buildQuery\s*\(/i,
1917
+ /buildSql\s*\(/i,
1918
+ /createQuery\s*\(/i,
1919
+ /makeQuery\s*\(/i,
1920
+ /generateSql\s*\(/i,
1921
+ /formatQuery\s*\(/i
1922
+ ];
1923
+ return builderPatterns.some((p) => p.test(text));
1924
+ }
1925
+ var SQL_EXECUTION_METHODS = ["query", "execute", "raw", "unsafe", "sql", "runQuery"];
1926
+ var DB_CALLER_PATTERN = /\b(?:db|client|pool|connection|knex|prisma|drizzle|supabase|pg|postgres|conn|database)\b/i;
1927
+ var SQL_SAFE_PATTERNS = [/^sql`/, /^sql\.raw\(sql`/, /\$\d+/, /\?/, /:[\w]+/];
1928
+ var CLIENT_FILE_PATTERNS = [/\/hooks\//i, /\/components\//i, /use[A-Z]\w*\.tsx?$/];
1929
+ var ROUTE_METHODS = [
1930
+ "get",
1931
+ "post",
1932
+ "put",
1933
+ "patch",
1934
+ "delete",
1935
+ "app.get",
1936
+ "app.post",
1937
+ "app.put",
1938
+ "app.patch",
1939
+ "app.delete",
1940
+ "router.get",
1941
+ "router.post"
1942
+ ];
1943
+ var AUTH_MIDDLEWARE_PATTERNS = [
1944
+ /\bauth(?:enticate)?(?:Middleware)?\b/i,
1945
+ /\bisAuthenticated\b/i,
1946
+ /\brequireAuth\b/i,
1947
+ /\bprotect(?:ed)?(?:Route)?\b/i,
1948
+ /\bverify(?:Token|JWT|Session)\b/i,
1949
+ /\bcheck(?:Auth|Token|Session)\b/i,
1950
+ /\bguard\b/i,
1951
+ /\bmiddleware\(/i,
1952
+ /\buse\s*\(\s*auth/i
1953
+ ];
1954
+ var PUBLIC_ROUTE_PATTERNS = [
1955
+ /\/public\//i,
1956
+ /\/health(?:check)?(?:\/|$)/i,
1957
+ /\/(?:login|logout|signin|signout)(?:\/|$)/i,
1958
+ /\/(?:signup|register)(?:\/|$)/i,
1959
+ /\/(?:forgot|reset)-?password/i,
1960
+ /\/callback(?:\/|$)/i,
1961
+ /\/webhook(?:s)?(?:\/|$)/i,
1962
+ /\/api\/v\d+\/public\//i,
1963
+ /\/\.well-known\//i,
1964
+ /\/robots\.txt/i,
1965
+ /\/favicon/i,
1966
+ /\/status(?:\/|$)/i,
1967
+ /\/ping(?:\/|$)/i,
1968
+ /\/version(?:\/|$)/i
1969
+ ];
1970
+ function isSqlExecutionCall(expressionText) {
1971
+ const isSqlMethod = SQL_EXECUTION_METHODS.some(
1972
+ (method) => expressionText.endsWith(`.${method}`) || expressionText === method
1973
+ );
1974
+ const isDbExec = expressionText.endsWith(".exec") && DB_CALLER_PATTERN.test(expressionText);
1975
+ return isSqlMethod || isDbExec;
1976
+ }
1977
+ function isSafeSqlArgument(argText) {
1978
+ return SQL_SAFE_PATTERNS.some((pattern) => pattern.test(argText));
1979
+ }
1980
+ function classifyDynamicSqlNode(node) {
1981
+ const text = node.getText();
1982
+ if (Node.isTemplateExpression(node) && hasUserInputInterpolation(text)) {
1983
+ return {
1984
+ confidence: 0.9,
1985
+ description: "Template literal with user input in SQL query."
1986
+ };
1987
+ }
1988
+ if (Node.isBinaryExpression(node) && hasUserInputInterpolation(text)) {
1989
+ return {
1990
+ confidence: 0.85,
1991
+ description: "String concatenation with user input in SQL query."
1992
+ };
1993
+ }
1994
+ if (isDynamicQueryBuilder(text)) {
1995
+ return {
1996
+ confidence: 0.7,
1997
+ description: "SQL query built by function that may use string concatenation."
1998
+ };
1999
+ }
2000
+ if (hasArrayBasedSqlBuilding(text)) {
2001
+ return {
2002
+ confidence: 0.6,
2003
+ description: "SQL query variable may be built dynamically from array concatenation."
2004
+ };
2005
+ }
2006
+ return null;
2007
+ }
2008
+ function findIdentifierSqlConstruction(firstArg) {
2009
+ if (!Node.isIdentifier(firstArg)) return null;
2010
+ const identifierName = firstArg.getText();
2011
+ const definitionNodes = firstArg.getDefinitions().map((definition) => definition.getNode()).filter((node) => node !== void 0);
2012
+ const sourceFile = firstArg.getSourceFile();
2013
+ const definitionPositions = buildDefinitionPositionsFromNodes(definitionNodes);
2014
+ for (const definitionNode of definitionNodes) {
2015
+ const risk = collectIdentifierDefinitionRisk(definitionNode, sourceFile);
2016
+ if (risk) return risk;
2017
+ }
2018
+ const assignmentRisk = detectAssignmentRisk(sourceFile, identifierName, definitionPositions);
2019
+ if (assignmentRisk) return assignmentRisk;
2020
+ const directMatchRisk = detectIdentifierJoinPattern(firstArg, identifierName, sourceFile);
2021
+ if (directMatchRisk) {
2022
+ return directMatchRisk;
2023
+ }
2024
+ return null;
2025
+ }
2026
+ function evaluateSqlInjectionRisk(firstArg) {
2027
+ const argText = firstArg.getText();
2028
+ if (isSafeSqlArgument(argText)) {
2029
+ return { isDangerous: false, confidence: 0, description: "" };
2030
+ }
2031
+ const directRisk = classifyDynamicSqlNode(firstArg);
2032
+ if (directRisk) {
2033
+ return {
2034
+ isDangerous: true,
2035
+ confidence: directRisk.confidence,
2036
+ description: directRisk.description
2037
+ };
2038
+ }
2039
+ const identifierRisk = findIdentifierSqlConstruction(firstArg);
2040
+ if (identifierRisk) {
2041
+ return {
2042
+ isDangerous: true,
2043
+ confidence: identifierRisk.confidence,
2044
+ description: identifierRisk.description
2045
+ };
2046
+ }
2047
+ return { isDangerous: false, confidence: 0, description: "" };
2048
+ }
2049
+ function isClientSideRouteFile(filePath) {
2050
+ return CLIENT_FILE_PATTERNS.some((pattern) => pattern.test(filePath));
2051
+ }
2052
+ function isRouteMethodCall(expressionText) {
2053
+ return ROUTE_METHODS.some(
2054
+ (method) => expressionText === method || expressionText.endsWith(`.${method.split(".").pop()}`)
2055
+ );
2056
+ }
2057
+ function hasHookOrEventWrapper(node) {
2058
+ let current = node.getParent();
2059
+ let depth = 0;
2060
+ while (current && depth < 10) {
2061
+ if (Node.isCallExpression(current)) {
2062
+ const callee = current.getExpression().getText();
2063
+ if (/^use[A-Z]/.test(callee)) return true;
2064
+ }
2065
+ if (Node.isPropertyAssignment(current)) {
2066
+ const name = current.getName();
2067
+ if (/^on[A-Z]/.test(name)) return true;
2068
+ }
2069
+ current = current.getParent();
2070
+ depth += 1;
2071
+ }
2072
+ return false;
2073
+ }
2074
+ function hasAuthMiddlewareArg(args) {
2075
+ const middlewareArgs = args.slice(1, -1);
2076
+ return middlewareArgs.some((arg) => {
2077
+ const argText = arg.getText();
2078
+ return AUTH_MIDDLEWARE_PATTERNS.some((pattern) => pattern.test(argText));
2079
+ });
2080
+ }
2081
+ function hasInlineAuthCheck(lastArg, fullText) {
2082
+ const handlerText = lastArg.getText();
2083
+ const handlerPatterns = [
2084
+ /headers\.authorization/i,
2085
+ /getToken\s*\(/i,
2086
+ /session\./i,
2087
+ /req\.user\b/i,
2088
+ /ctx\.(?:user|auth|session)\b/i,
2089
+ /throw.*(?:Unauthorized|401)/i
2090
+ ];
2091
+ return handlerPatterns.some((pattern) => pattern.test(handlerText)) || AUTH_MIDDLEWARE_PATTERNS.some((pattern) => pattern.test(fullText));
2092
+ }
2093
+ function isPublicRoute(fullText) {
2094
+ return PUBLIC_ROUTE_PATTERNS.some((pattern) => pattern.test(fullText));
2095
+ }
2096
+ function buildAuthConfidence(expressionText, fullText) {
2097
+ let confidence = 0.4;
2098
+ if (/\/(?:api|admin|user|account|setting|profile)/i.test(fullText)) confidence = 0.6;
2099
+ if (/\.(post|put|patch|delete)\s*\(/i.test(expressionText)) confidence += 0.1;
2100
+ return confidence;
2101
+ }
2102
+ var TS_RULES = [
2103
+ // SQL Injection Detection
2104
+ {
2105
+ id: "injection/sql",
2106
+ name: "SQL Injection",
2107
+ detect: (node, context) => {
2108
+ if (!Node.isCallExpression(node)) return null;
2109
+ const expression = node.getExpression();
2110
+ const expressionText = expression.getText();
2111
+ if (!isSqlExecutionCall(expressionText)) return null;
2112
+ const args = node.getArguments();
2113
+ if (args.length === 0) return null;
2114
+ const firstArg = args[0];
2115
+ const risk = evaluateSqlInjectionRisk(firstArg);
2116
+ if (!risk.isDangerous) return null;
2117
+ return {
2118
+ ruleId: "injection/sql",
2119
+ severity: "critical",
2120
+ title: "Potential SQL Injection",
2121
+ description: `${risk.description} Use parameterized queries to prevent SQL injection.`,
2122
+ location: getLocation(node, context.filePath),
2123
+ snippet: getSnippet(node),
2124
+ fix: {
2125
+ description: "Use parameterized queries or Drizzle ORM",
2126
+ replacement: "Use db.select().from(table).where(eq(column, value))"
2127
+ },
2128
+ cweId: CWE.SQL_INJECTION,
2129
+ owaspCategory: OWASP_2021.INJECTION,
2130
+ confidence: risk.confidence
2131
+ };
2132
+ }
2133
+ },
2134
+ // XSS Detection
2135
+ {
2136
+ id: "injection/xss",
2137
+ name: "Cross-Site Scripting (XSS)",
2138
+ detect: (node, context) => {
2139
+ if (!Node.isJsxAttribute(node)) return null;
2140
+ const nameNode = node.getNameNode();
2141
+ const name = nameNode.getText();
2142
+ if (name !== "dangerouslySetInnerHTML") return null;
2143
+ const initializer = node.getInitializer();
2144
+ if (initializer) {
2145
+ const text = initializer.getText();
2146
+ if (text.includes("DOMPurify") || text.includes("sanitize") || text.includes("escape")) {
2147
+ return null;
2148
+ }
2149
+ }
2150
+ return {
2151
+ ruleId: "injection/xss",
2152
+ severity: "high",
2153
+ title: "Potential XSS Vulnerability",
2154
+ description: "dangerouslySetInnerHTML used without sanitization. Use DOMPurify or similar library.",
2155
+ location: getLocation(node, context.filePath),
2156
+ snippet: getSnippet(node),
2157
+ fix: {
2158
+ description: "Sanitize HTML before rendering",
2159
+ replacement: "dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}"
2160
+ },
2161
+ cweId: CWE.XSS,
2162
+ owaspCategory: OWASP_2021.INJECTION,
2163
+ confidence: 0.9
2164
+ };
2165
+ }
2166
+ },
2167
+ // Command Injection Detection
2168
+ {
2169
+ id: "injection/command",
2170
+ name: "Command Injection",
2171
+ detect: (node, context) => {
2172
+ const NON_RUNTIME_PATH_PATTERNS = [
2173
+ /\/scripts\//i,
2174
+ /\/tools\//i,
2175
+ /\/bin\//i,
2176
+ /\.config\./i,
2177
+ /\/__tests__\//i,
2178
+ /\.test\./i,
2179
+ /\.spec\./i,
2180
+ /\/migrations?\//i,
2181
+ /\/seeds?\//i
2182
+ ];
2183
+ if (!Node.isCallExpression(node)) return null;
2184
+ const expression = node.getExpression();
2185
+ const expressionText = expression.getText();
2186
+ const dangerousMethods = [
2187
+ "exec",
2188
+ "execSync",
2189
+ "spawn",
2190
+ "spawnSync",
2191
+ "execFile",
2192
+ "execFileSync"
2193
+ ];
2194
+ const isDangerous = dangerousMethods.some(
2195
+ (m) => expressionText === m || expressionText.endsWith(`.${m}`)
2196
+ );
2197
+ if (!isDangerous) return null;
2198
+ const args = node.getArguments();
2199
+ if (args.length === 0) return null;
2200
+ const firstArg = args[0];
2201
+ if (Node.isTemplateExpression(firstArg) || Node.isBinaryExpression(firstArg)) {
2202
+ const isNonRuntime = NON_RUNTIME_PATH_PATTERNS.some((p) => p.test(context.filePath));
2203
+ return {
2204
+ ruleId: "injection/command",
2205
+ severity: isNonRuntime ? "medium" : "critical",
2206
+ title: "Potential Command Injection",
2207
+ description: "User input in command execution detected. Use parameterized commands or input validation.",
2208
+ location: getLocation(node, context.filePath),
2209
+ snippet: getSnippet(node),
2210
+ fix: {
2211
+ description: "Use spawn with array arguments instead of exec with string"
2212
+ },
2213
+ cweId: CWE.COMMAND_INJECTION,
2214
+ owaspCategory: OWASP_2021.INJECTION,
2215
+ confidence: isNonRuntime ? 0.5 : 0.7
2216
+ };
2217
+ }
2218
+ return null;
2219
+ }
2220
+ },
2221
+ // Insecure Eval Detection
2222
+ {
2223
+ id: "injection/eval",
2224
+ name: "Insecure Eval Usage",
2225
+ detect: (node, context) => {
2226
+ if (!Node.isCallExpression(node)) return null;
2227
+ const expression = node.getExpression();
2228
+ const expressionText = expression.getText();
2229
+ const evalFunctions = ["eval", "Function", "setTimeout", "setInterval"];
2230
+ if (!evalFunctions.includes(expressionText)) return null;
2231
+ if (expressionText === "setTimeout" || expressionText === "setInterval") {
2232
+ const args = node.getArguments();
2233
+ if (args.length > 0 && !Node.isStringLiteral(args[0])) {
2234
+ return null;
2235
+ }
2236
+ }
2237
+ return {
2238
+ ruleId: "injection/eval",
2239
+ severity: "high",
2240
+ title: "Insecure Eval Usage",
2241
+ description: `${expressionText} can execute arbitrary code. Avoid using eval-like functions.`,
2242
+ location: getLocation(node, context.filePath),
2243
+ snippet: getSnippet(node),
2244
+ fix: {
2245
+ description: "Use safer alternatives like JSON.parse or explicit logic"
2246
+ },
2247
+ cweId: CWE.EVAL_INJECTION,
2248
+ owaspCategory: OWASP_2021.INJECTION,
2249
+ confidence: 0.85
2250
+ };
2251
+ }
2252
+ },
2253
+ // Missing Auth Check Detection
2254
+ {
2255
+ id: "auth/missing-check",
2256
+ name: "Missing Authentication Check",
2257
+ detect: (node, context) => {
2258
+ if (!Node.isCallExpression(node)) return null;
2259
+ if (isClientSideRouteFile(context.filePath)) return null;
2260
+ const expression = node.getExpression();
2261
+ const expressionText = expression.getText();
2262
+ if (!isRouteMethodCall(expressionText)) return null;
2263
+ if (hasHookOrEventWrapper(node)) return null;
2264
+ const args = node.getArguments();
2265
+ if (args.length < 2) return null;
2266
+ const fullText = node.getText();
2267
+ const hasAuthMiddleware = hasAuthMiddlewareArg(args);
2268
+ const lastArg = args[args.length - 1];
2269
+ const inlineAuthDetected = hasInlineAuthCheck(lastArg, fullText);
2270
+ const hasAuth = hasAuthMiddleware || inlineAuthDetected;
2271
+ const isPublic = isPublicRoute(fullText);
2272
+ if (!hasAuth && !isPublic) {
2273
+ const confidence = buildAuthConfidence(expressionText, fullText);
2274
+ return {
2275
+ ruleId: "auth/missing-check",
2276
+ severity: "medium",
2277
+ title: "Potentially Missing Authentication",
2278
+ description: "Route handler may be missing authentication middleware. Verify if this endpoint should be protected.",
2279
+ location: getLocation(node, context.filePath),
2280
+ snippet: getSnippet(node),
2281
+ fix: {
2282
+ description: "Add authentication middleware to protected routes"
2283
+ },
2284
+ cweId: CWE.MISSING_AUTH,
2285
+ owaspCategory: OWASP_2021.AUTH_FAILURES,
2286
+ confidence
2287
+ };
2288
+ }
2289
+ return null;
2290
+ }
2291
+ },
2292
+ // Sensitive Data Logging Detection
2293
+ {
2294
+ id: "secret/logging",
2295
+ name: "Sensitive Data Logging",
2296
+ detect: (node, context) => {
2297
+ if (!Node.isCallExpression(node)) return null;
2298
+ const expression = node.getExpression();
2299
+ const expressionText = expression.getText();
2300
+ const logMethods = [
2301
+ "console.log",
2302
+ "console.info",
2303
+ "console.debug",
2304
+ "console.warn",
2305
+ "console.error",
2306
+ "logger.log",
2307
+ "logger.info",
2308
+ "logger.debug"
2309
+ ];
2310
+ if (!logMethods.includes(expressionText)) return null;
2311
+ const args = node.getArguments();
2312
+ for (const arg of args) {
2313
+ const text = arg.getText().toLowerCase();
2314
+ const sensitivePatterns = [
2315
+ "password",
2316
+ "secret",
2317
+ "token",
2318
+ "apikey",
2319
+ "api_key",
2320
+ "authorization",
2321
+ "bearer",
2322
+ "credential",
2323
+ "private"
2324
+ ];
2325
+ if (sensitivePatterns.some((p) => text.includes(p))) {
2326
+ return {
2327
+ ruleId: "secret/logging",
2328
+ severity: "high",
2329
+ title: "Sensitive Data in Logs",
2330
+ description: "Logging statement may contain sensitive data.",
2331
+ location: getLocation(node, context.filePath),
2332
+ snippet: getSnippet(node),
2333
+ fix: {
2334
+ description: "Remove sensitive data from log statements or redact"
2335
+ },
2336
+ cweId: CWE.SENSITIVE_LOG,
2337
+ owaspCategory: OWASP_2021.LOGGING_FAILURES,
2338
+ confidence: 0.7
2339
+ };
2340
+ }
2341
+ }
2342
+ return null;
2343
+ }
2344
+ },
2345
+ // Insecure Randomness Detection
2346
+ {
2347
+ id: "crypto/weak-random",
2348
+ name: "Insecure Randomness",
2349
+ detect: (node, context) => {
2350
+ if (!Node.isCallExpression(node)) return null;
2351
+ const expression = node.getExpression();
2352
+ const expressionText = expression.getText();
2353
+ if (expressionText !== "Math.random") return null;
2354
+ const parent = node.getParent();
2355
+ const grandparent = parent?.getParent();
2356
+ const contextText = (grandparent?.getText() || parent?.getText() || "").toLowerCase();
2357
+ const securityContexts = [
2358
+ "token",
2359
+ "secret",
2360
+ "password",
2361
+ "session",
2362
+ "id",
2363
+ "key",
2364
+ "nonce",
2365
+ "salt"
2366
+ ];
2367
+ if (securityContexts.some((c) => contextText.includes(c))) {
2368
+ return {
2369
+ ruleId: "crypto/weak-random",
2370
+ severity: "medium",
2371
+ title: "Insecure Randomness for Security Context",
2372
+ description: "Math.random() is not cryptographically secure. Use crypto.randomBytes() or crypto.randomUUID().",
2373
+ location: getLocation(node, context.filePath),
2374
+ snippet: getSnippet(node),
2375
+ fix: {
2376
+ description: "Use crypto.randomBytes() or crypto.randomUUID()",
2377
+ replacement: "crypto.randomUUID() or crypto.randomBytes(32).toString('hex')"
2378
+ },
2379
+ cweId: CWE.WEAK_RANDOM,
2380
+ owaspCategory: OWASP_2021.CRYPTO_FAILURES,
2381
+ confidence: 0.6
2382
+ };
2383
+ }
2384
+ return null;
2385
+ }
2386
+ }
2387
+ ];
2388
+ var TypeScriptAnalyzer = class {
2389
+ name = "TypeScriptAnalyzer";
2390
+ categories = ["injection", "auth", "crypto", "secret"];
2391
+ async analyze(options) {
2392
+ const findings = [];
2393
+ const files = await glob(["**/*.ts", "**/*.tsx"], {
2394
+ cwd: options.rootDir,
2395
+ ignore: options.exclude || ["**/node_modules/**", "**/dist/**"],
2396
+ absolute: true
2397
+ });
2398
+ const project = new Project({
2399
+ skipAddingFilesFromTsConfig: true,
2400
+ compilerOptions: {
2401
+ allowJs: true,
2402
+ checkJs: false,
2403
+ noEmit: true,
2404
+ skipLibCheck: true
2405
+ }
2406
+ });
2407
+ for (const filePath of files) {
2408
+ try {
2409
+ const sourceFile = project.addSourceFileAtPath(filePath);
2410
+ const context = { filePath, project };
2411
+ sourceFile.forEachDescendant((node) => {
2412
+ for (const rule of TS_RULES) {
2413
+ const finding = rule.detect(node, context);
2414
+ if (finding) {
2415
+ findings.push(finding);
2416
+ }
2417
+ }
2418
+ });
2419
+ project.removeSourceFile(sourceFile);
2420
+ } catch {
2421
+ }
2422
+ }
2423
+ return findings;
2424
+ }
2425
+ };
2426
+
2427
+ // src/internal/vuln-checker/config/loader.ts
2428
+ init_esm_shims();
2429
+
2430
+ // src/internal/vuln-checker/security/path-validation.ts
2431
+ init_esm_shims();
2432
+ function containsPathTraversal(inputPath) {
2433
+ const normalized = path2.normalize(inputPath);
2434
+ if (normalized.includes("..")) return true;
2435
+ if (inputPath.includes("\0")) return true;
2436
+ return false;
2437
+ }
2438
+ function isPathWithinBoundary(filePath, boundaryDir) {
2439
+ try {
2440
+ const resolvedFile = path2.resolve(filePath);
2441
+ const resolvedBoundary = path2.resolve(boundaryDir);
2442
+ const relative = path2.relative(resolvedBoundary, resolvedFile);
2443
+ return !relative.startsWith("..") && !path2.isAbsolute(relative);
2444
+ } catch {
2445
+ return false;
2446
+ }
2447
+ }
2448
+ function resolvePathWithinBoundary(inputPath, boundaryDir) {
2449
+ if (containsPathTraversal(inputPath)) {
2450
+ throw new Error(`Path contains traversal patterns: ${inputPath}`);
2451
+ }
2452
+ const resolvedPath = path2.isAbsolute(inputPath) ? inputPath : path2.resolve(boundaryDir, inputPath);
2453
+ if (!isPathWithinBoundary(resolvedPath, boundaryDir)) {
2454
+ throw new Error(`Path is outside allowed boundary: ${inputPath}`);
2455
+ }
2456
+ return resolvedPath;
2457
+ }
2458
+ function filterPathsWithinBoundary(files, boundaryDir) {
2459
+ const resolvedBoundary = path2.resolve(boundaryDir);
2460
+ return files.filter((file) => isPathWithinBoundary(file, resolvedBoundary));
2461
+ }
2462
+ function validateGlobPatterns(patterns) {
2463
+ for (const pattern of patterns) {
2464
+ if (containsPathTraversal(pattern)) {
2465
+ throw new Error(`Glob pattern contains path traversal: ${pattern}`);
2466
+ }
2467
+ }
2468
+ }
2469
+
2470
+ // src/internal/vuln-checker/config/loader.ts
2471
+ async function loadConfig(configPath, rootDir) {
2472
+ const fullPath = resolvePathWithinBoundary(configPath, rootDir);
2473
+ try {
2474
+ const content = await fs.readFile(fullPath, "utf-8");
2475
+ if (fullPath.endsWith(".yml") || fullPath.endsWith(".yaml")) {
2476
+ return yaml.load(content, { schema: yaml.JSON_SCHEMA });
2477
+ }
2478
+ return JSON.parse(content);
2479
+ } catch (error) {
2480
+ if (error.code === "ENOENT") {
2481
+ return { version: 1 };
2482
+ }
2483
+ throw error;
2484
+ }
2485
+ }
2486
+ async function loadIgnores(ignorePath, rootDir) {
2487
+ const fullPath = resolvePathWithinBoundary(ignorePath, rootDir);
2488
+ try {
2489
+ const content = await fs.readFile(fullPath, "utf-8");
2490
+ let parsed;
2491
+ if (fullPath.endsWith(".yml") || fullPath.endsWith(".yaml")) {
2492
+ parsed = yaml.load(content, { schema: yaml.JSON_SCHEMA });
2493
+ } else {
2494
+ parsed = JSON.parse(content);
2495
+ }
2496
+ const ignores = parsed?.ignores || [];
2497
+ const now = /* @__PURE__ */ new Date();
2498
+ return ignores.filter((ignore) => {
2499
+ if (!ignore.expires) return true;
2500
+ const expiresDate = new Date(ignore.expires);
2501
+ return expiresDate > now;
2502
+ });
2503
+ } catch (error) {
2504
+ if (error.code === "ENOENT") {
2505
+ return [];
2506
+ }
2507
+ throw error;
2508
+ }
2509
+ }
2510
+
2511
+ // src/internal/vuln-checker/ignore/matcher.ts
2512
+ init_esm_shims();
2513
+ function normalizePath(filePath) {
2514
+ const normalized = path2.normalize(filePath);
2515
+ return normalized.replace(/\\/g, "/");
2516
+ }
2517
+ function normalizeRuleId(ruleId) {
2518
+ return ruleId.toLowerCase().replace(/_/g, "-");
2519
+ }
2520
+ function matchIgnoreRule(rule, finding) {
2521
+ if (rule.rule !== "*") {
2522
+ const normalizedRule = normalizeRuleId(rule.rule);
2523
+ const normalizedFinding = normalizeRuleId(finding.ruleId);
2524
+ if (normalizedRule !== normalizedFinding) {
2525
+ return false;
2526
+ }
2527
+ }
2528
+ if (rule.path) {
2529
+ const normalizedPath = normalizePath(finding.location.file);
2530
+ const normalizedPattern = normalizePath(rule.path);
2531
+ if (!minimatch(normalizedPath, normalizedPattern, { nocase: true })) {
2532
+ return false;
2533
+ }
2534
+ }
2535
+ return true;
2536
+ }
2537
+ var RULE_ID_PATTERN = "[a-zA-Z0-9\\-_\\/\\*]+";
2538
+ function createAnnotationPatterns() {
2539
+ const commentPrefixes = [
2540
+ "\\/\\/",
2541
+ // JS single-line: //
2542
+ "\\/\\*\\s*",
2543
+ // JS block: /*
2544
+ "#",
2545
+ // Shell/YAML/SQL: #
2546
+ "--"
2547
+ // SQL: --
2548
+ ].join("|");
2549
+ const basePattern = (type) => new RegExp(
2550
+ `(?:${commentPrefixes})\\s*@vuln-ignore${type}:(${RULE_ID_PATTERN})(?:\\s*-\\s*(.+?))?(?:\\s*\\*\\/)?$`,
2551
+ "i"
2552
+ );
2553
+ return {
2554
+ line: basePattern(""),
2555
+ nextLine: basePattern("-next-line"),
2556
+ file: basePattern("-file")
2557
+ };
2558
+ }
2559
+ var ANNOTATION_PATTERNS = createAnnotationPatterns();
2560
+ async function parseInlineAnnotations(filePath) {
2561
+ const annotations = [];
2562
+ try {
2563
+ const content = await fs.readFile(filePath, "utf-8");
2564
+ const lines = content.split("\n");
2565
+ for (let i = 0; i < lines.length; i++) {
2566
+ const line = lines[i];
2567
+ const lineNumber = i + 1;
2568
+ const fileMatch = line.match(ANNOTATION_PATTERNS.file);
2569
+ if (fileMatch) {
2570
+ annotations.push({
2571
+ // Normalize rule ID for consistent matching
2572
+ ruleId: normalizeRuleId(fileMatch[1]),
2573
+ reason: fileMatch[2]?.trim(),
2574
+ scope: "file",
2575
+ line: lineNumber
2576
+ });
2577
+ continue;
2578
+ }
2579
+ const nextLineMatch = line.match(ANNOTATION_PATTERNS.nextLine);
2580
+ if (nextLineMatch) {
2581
+ annotations.push({
2582
+ ruleId: normalizeRuleId(nextLineMatch[1]),
2583
+ reason: nextLineMatch[2]?.trim(),
2584
+ scope: "next-line",
2585
+ line: lineNumber
2586
+ });
2587
+ continue;
2588
+ }
2589
+ const lineMatch = line.match(ANNOTATION_PATTERNS.line);
2590
+ if (lineMatch) {
2591
+ annotations.push({
2592
+ ruleId: normalizeRuleId(lineMatch[1]),
2593
+ reason: lineMatch[2]?.trim(),
2594
+ scope: "line",
2595
+ line: lineNumber
2596
+ });
2597
+ }
2598
+ }
2599
+ } catch {
2600
+ }
2601
+ return annotations;
2602
+ }
2603
+
2604
+ // src/internal/vuln-checker/reporters/console-reporter.ts
2605
+ init_esm_shims();
2606
+ var COLORS = {
2607
+ reset: "\x1B[0m",
2608
+ bold: "\x1B[1m",
2609
+ dim: "\x1B[2m",
2610
+ red: "\x1B[31m",
2611
+ green: "\x1B[32m",
2612
+ yellow: "\x1B[33m",
2613
+ blue: "\x1B[34m",
2614
+ magenta: "\x1B[35m",
2615
+ cyan: "\x1B[36m",
2616
+ white: "\x1B[37m",
2617
+ bgRed: "\x1B[41m"};
2618
+ function getSeverityColor(severity) {
2619
+ switch (severity) {
2620
+ case "critical":
2621
+ return COLORS.bgRed + COLORS.white;
2622
+ case "high":
2623
+ return COLORS.red;
2624
+ case "medium":
2625
+ return COLORS.yellow;
2626
+ case "low":
2627
+ return COLORS.cyan;
2628
+ case "info":
2629
+ return COLORS.blue;
2630
+ }
2631
+ }
2632
+ function formatSeverity(severity) {
2633
+ const color = getSeverityColor(severity);
2634
+ const label = severity.toUpperCase().padEnd(8);
2635
+ return `${color}${label}${COLORS.reset}`;
2636
+ }
2637
+ function formatFinding(finding, index) {
2638
+ const lines = [];
2639
+ lines.push(`
2640
+ ${COLORS.bold}${index}. ${finding.title}${COLORS.reset}`);
2641
+ lines.push(
2642
+ ` ${formatSeverity(finding.severity)} ${COLORS.dim}${finding.ruleId}${COLORS.reset}`
2643
+ );
2644
+ const shortPath = finding.location.file.split("/").slice(-3).join("/");
2645
+ lines.push(
2646
+ ` ${COLORS.cyan}${shortPath}:${finding.location.line}:${finding.location.column}${COLORS.reset}`
2647
+ );
2648
+ lines.push(` ${finding.description}`);
2649
+ if (finding.snippet) {
2650
+ lines.push(` ${COLORS.dim}\u2502${COLORS.reset}`);
2651
+ const snippetLines = finding.snippet.text.split("\n");
2652
+ for (const snippetLine of snippetLines.slice(0, 3)) {
2653
+ lines.push(` ${COLORS.dim}\u2502${COLORS.reset} ${snippetLine}`);
2654
+ }
2655
+ if (snippetLines.length > 3) {
2656
+ lines.push(` ${COLORS.dim}\u2502 ... (${snippetLines.length - 3} more lines)${COLORS.reset}`);
2657
+ }
2658
+ }
2659
+ if (finding.fix) {
2660
+ lines.push(` ${COLORS.green}Fix:${COLORS.reset} ${finding.fix.description}`);
2661
+ }
2662
+ return lines.join("\n");
2663
+ }
2664
+ var ConsoleReporter = class {
2665
+ name = "console";
2666
+ format(result) {
2667
+ const lines = [];
2668
+ lines.push(
2669
+ `
2670
+ ${COLORS.bold}${COLORS.magenta}\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550${COLORS.reset}`
2671
+ );
2672
+ lines.push(
2673
+ `${COLORS.bold}${COLORS.magenta} VULNERABILITY SCAN RESULTS ${COLORS.reset}`
2674
+ );
2675
+ lines.push(
2676
+ `${COLORS.bold}${COLORS.magenta}\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550${COLORS.reset}
2677
+ `
2678
+ );
2679
+ lines.push(`${COLORS.bold}Summary${COLORS.reset}`);
2680
+ lines.push(`${"\u2500".repeat(40)}`);
2681
+ if (result.summary.critical > 0) {
2682
+ lines.push(` ${formatSeverity("critical")} ${result.summary.critical}`);
2683
+ }
2684
+ if (result.summary.high > 0) {
2685
+ lines.push(` ${formatSeverity("high")} ${result.summary.high}`);
2686
+ }
2687
+ if (result.summary.medium > 0) {
2688
+ lines.push(` ${formatSeverity("medium")} ${result.summary.medium}`);
2689
+ }
2690
+ if (result.summary.low > 0) {
2691
+ lines.push(` ${formatSeverity("low")} ${result.summary.low}`);
2692
+ }
2693
+ if (result.summary.info > 0) {
2694
+ lines.push(` ${formatSeverity("info")} ${result.summary.info}`);
2695
+ }
2696
+ lines.push(`${"\u2500".repeat(40)}`);
2697
+ lines.push(` ${COLORS.bold}Total:${COLORS.reset} ${result.summary.total} findings`);
2698
+ if (result.summary.ignored > 0) {
2699
+ lines.push(` ${COLORS.dim}Ignored: ${result.summary.ignored}${COLORS.reset}`);
2700
+ }
2701
+ lines.push(
2702
+ ` ${COLORS.dim}Scanned ${result.metadata.filesScanned} files in ${result.metadata.duration}ms${COLORS.reset}`
2703
+ );
2704
+ if (result.findings.length > 0) {
2705
+ lines.push(`
2706
+ ${COLORS.bold}Findings${COLORS.reset}`);
2707
+ lines.push(`${"\u2500".repeat(40)}`);
2708
+ const sorted = [...result.findings].sort((a, b) => {
2709
+ const order = ["critical", "high", "medium", "low", "info"];
2710
+ return order.indexOf(a.severity) - order.indexOf(b.severity);
2711
+ });
2712
+ let index = 1;
2713
+ for (const finding of sorted) {
2714
+ lines.push(formatFinding(finding, index));
2715
+ index++;
2716
+ }
2717
+ } else {
2718
+ lines.push(`
2719
+ ${COLORS.green}${COLORS.bold}\u2713 No vulnerabilities found!${COLORS.reset}`);
2720
+ }
2721
+ lines.push(`
2722
+ ${COLORS.dim}${"\u2500".repeat(40)}${COLORS.reset}`);
2723
+ lines.push(`${COLORS.dim}${TOOL.NAME} v${TOOL.VERSION}${COLORS.reset}
2724
+ `);
2725
+ return lines.join("\n");
2726
+ }
2727
+ };
2728
+
2729
+ // src/internal/vuln-checker/reporters/json-reporter.ts
2730
+ init_esm_shims();
2731
+ var JsonReporter = class {
2732
+ name = "json";
2733
+ format(result) {
2734
+ return JSON.stringify(result, null, 2);
2735
+ }
2736
+ };
2737
+
2738
+ // src/internal/vuln-checker/reporters/markdown-reporter.ts
2739
+ init_esm_shims();
2740
+ function escapeMarkdown(text) {
2741
+ let escaped = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2742
+ escaped = escaped.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\*/g, "\\*").replace(/_/g, "\\_").replace(/\{/g, "\\{").replace(/\}/g, "\\}").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/#/g, "\\#").replace(/\+/g, "\\+").replace(/-/g, "\\-").replace(/\./g, "\\.").replace(/!/g, "\\!").replace(/\|/g, "\\|");
2743
+ return escaped;
2744
+ }
2745
+ function sanitizeCodeBlock(text) {
2746
+ return text.replace(/```/g, "\\`\\`\\`");
2747
+ }
2748
+ var SEVERITY_EMOJI = {
2749
+ critical: "\u{1F534}",
2750
+ high: "\u{1F7E0}",
2751
+ medium: "\u{1F7E1}",
2752
+ low: "\u{1F7E2}",
2753
+ info: "\u{1F535}"
2754
+ };
2755
+ var SEVERITY_ORDER = ["critical", "high", "medium", "low", "info"];
2756
+ function groupByCategory(findings) {
2757
+ const byCategory = /* @__PURE__ */ new Map();
2758
+ for (const finding of findings) {
2759
+ const category = finding.ruleId.split("/")[0];
2760
+ const existing = byCategory.get(category) || [];
2761
+ existing.push(finding);
2762
+ byCategory.set(category, existing);
2763
+ }
2764
+ return byCategory;
2765
+ }
2766
+ function sortBySeverity(findings) {
2767
+ return [...findings].sort(
2768
+ (a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity)
2769
+ );
2770
+ }
2771
+ function formatFilePath(filePath) {
2772
+ return filePath.split("/").slice(-3).join("/");
2773
+ }
2774
+ function formatMetadata(finding) {
2775
+ const lines = [];
2776
+ if (finding.cweId) {
2777
+ lines.push(`- **CWE**: ${finding.cweId}`);
2778
+ }
2779
+ if (finding.owaspCategory) {
2780
+ lines.push(`- **OWASP**: ${finding.owaspCategory}`);
2781
+ }
2782
+ return lines;
2783
+ }
2784
+ function formatSnippet(finding) {
2785
+ if (!finding.snippet) return [];
2786
+ return ["```", sanitizeCodeBlock(finding.snippet.text), "```\n"];
2787
+ }
2788
+ function formatFix(finding) {
2789
+ if (!finding.fix) return [];
2790
+ const lines = [`**Fix**: ${escapeMarkdown(finding.fix.description)}
2791
+ `];
2792
+ if (finding.fix.replacement) {
2793
+ lines.push("```", sanitizeCodeBlock(finding.fix.replacement), "```\n");
2794
+ }
2795
+ return lines;
2796
+ }
2797
+ function formatFinding2(finding) {
2798
+ const emoji = SEVERITY_EMOJI[finding.severity];
2799
+ const location = `${formatFilePath(finding.location.file)}:${finding.location.line}`;
2800
+ const safeTitle = escapeMarkdown(finding.title);
2801
+ const safeDescription = escapeMarkdown(finding.description);
2802
+ return [
2803
+ `#### ${emoji} ${safeTitle}
2804
+ `,
2805
+ `- **Severity**: ${finding.severity}`,
2806
+ `- **Location**: \`${location}\``,
2807
+ `- **Rule**: \`${finding.ruleId}\``,
2808
+ ...formatMetadata(finding),
2809
+ `
2810
+ ${safeDescription}
2811
+ `,
2812
+ ...formatSnippet(finding),
2813
+ ...formatFix(finding),
2814
+ "---\n"
2815
+ ];
2816
+ }
2817
+ function formatCategorySection(category, findings) {
2818
+ const title = category.charAt(0).toUpperCase() + category.slice(1);
2819
+ const sorted = sortBySeverity(findings);
2820
+ return [`
2821
+ ### ${title}
2822
+ `, ...sorted.flatMap(formatFinding2)];
2823
+ }
2824
+ function formatFindingsByCategory(findings) {
2825
+ const byCategory = groupByCategory(findings);
2826
+ const sections = Array.from(byCategory.entries()).flatMap(
2827
+ ([category, categoryFindings]) => formatCategorySection(category, categoryFindings)
2828
+ );
2829
+ return sections.join("\n");
2830
+ }
2831
+ var MarkdownReporter = class {
2832
+ name = "markdown";
2833
+ format(result) {
2834
+ const lines = [];
2835
+ lines.push("# Security Vulnerability Report\n");
2836
+ lines.push(`Generated: ${result.timestamp}
2837
+ `);
2838
+ lines.push("## Summary\n");
2839
+ lines.push("| Severity | Count |");
2840
+ lines.push("|----------|-------|");
2841
+ lines.push(`| \u{1F534} Critical | ${result.summary.critical} |`);
2842
+ lines.push(`| \u{1F7E0} High | ${result.summary.high} |`);
2843
+ lines.push(`| \u{1F7E1} Medium | ${result.summary.medium} |`);
2844
+ lines.push(`| \u{1F7E2} Low | ${result.summary.low} |`);
2845
+ lines.push(`| \u{1F535} Info | ${result.summary.info} |`);
2846
+ lines.push(`| **Total** | **${result.summary.total}** |`);
2847
+ lines.push(`| Ignored | ${result.summary.ignored} |
2848
+ `);
2849
+ lines.push("## Scan Details\n");
2850
+ lines.push(`- **Duration**: ${result.metadata.duration}ms`);
2851
+ lines.push(`- **Files Scanned**: ${result.metadata.filesScanned}`);
2852
+ lines.push(`- **Root Directory**: ${result.metadata.rootDir}
2853
+ `);
2854
+ if (result.findings.length > 0) {
2855
+ lines.push("## Findings\n");
2856
+ lines.push(formatFindingsByCategory(result.findings));
2857
+ } else {
2858
+ lines.push("## Findings\n");
2859
+ lines.push("No vulnerabilities found! \u{1F389}\n");
2860
+ }
2861
+ if (result.ignoredFindings && result.ignoredFindings.length > 0) {
2862
+ lines.push("## Ignored Findings\n");
2863
+ lines.push("The following findings were ignored based on configuration:\n");
2864
+ for (const finding of result.ignoredFindings) {
2865
+ const emoji = SEVERITY_EMOJI[finding.severity];
2866
+ const safeTitle = escapeMarkdown(finding.title);
2867
+ lines.push(
2868
+ `- ${emoji} **${safeTitle}** at \`${finding.location.file}:${finding.location.line}\``
2869
+ );
2870
+ }
2871
+ lines.push("");
2872
+ }
2873
+ lines.push("---");
2874
+ lines.push(`*Generated by [${TOOL.NAME}](${TOOL.REPO_URL})*`);
2875
+ return lines.join("\n");
2876
+ }
2877
+ };
2878
+
2879
+ // src/internal/vuln-checker/reporters/sarif-reporter.ts
2880
+ init_esm_shims();
2881
+ function mapToSarifLevel(severity) {
2882
+ switch (severity) {
2883
+ case "critical":
2884
+ case "high":
2885
+ return "error";
2886
+ case "medium":
2887
+ return "warning";
2888
+ case "low":
2889
+ return "note";
2890
+ case "info":
2891
+ return "none";
2892
+ }
2893
+ }
2894
+ function buildRule(finding) {
2895
+ return {
2896
+ id: finding.ruleId,
2897
+ name: finding.title,
2898
+ shortDescription: {
2899
+ text: finding.title
2900
+ },
2901
+ fullDescription: {
2902
+ text: finding.description
2903
+ },
2904
+ help: {
2905
+ text: finding.fix?.description || "No fix available",
2906
+ markdown: finding.fix?.description || "No fix available"
2907
+ },
2908
+ properties: {
2909
+ tags: [finding.cweId, finding.owaspCategory].filter(Boolean),
2910
+ precision: "medium",
2911
+ "security-severity": severityToScore(finding.severity).toString()
2912
+ }
2913
+ };
2914
+ }
2915
+ function severityToScore(severity) {
2916
+ return SEVERITY_SCORE[severity] ?? 1;
2917
+ }
2918
+ function buildResult(finding) {
2919
+ return {
2920
+ ruleId: finding.ruleId,
2921
+ level: mapToSarifLevel(finding.severity),
2922
+ message: {
2923
+ text: finding.description
2924
+ },
2925
+ locations: [
2926
+ {
2927
+ physicalLocation: {
2928
+ artifactLocation: {
2929
+ uri: finding.location.file,
2930
+ uriBaseId: "%SRCROOT%"
2931
+ },
2932
+ region: {
2933
+ startLine: finding.location.line,
2934
+ startColumn: finding.location.column,
2935
+ endLine: finding.location.endLine || finding.location.line,
2936
+ snippet: finding.snippet ? {
2937
+ text: finding.snippet.text
2938
+ } : void 0
2939
+ }
2940
+ }
2941
+ }
2942
+ ],
2943
+ fixes: finding.fix ? [
2944
+ {
2945
+ description: {
2946
+ text: finding.fix.description
2947
+ },
2948
+ artifactChanges: finding.fix.replacement ? [
2949
+ {
2950
+ artifactLocation: {
2951
+ uri: finding.location.file
2952
+ },
2953
+ replacements: [
2954
+ {
2955
+ deletedRegion: {
2956
+ startLine: finding.location.line,
2957
+ startColumn: finding.location.column
2958
+ },
2959
+ insertedContent: {
2960
+ text: finding.fix.replacement
2961
+ }
2962
+ }
2963
+ ]
2964
+ }
2965
+ ] : []
2966
+ }
2967
+ ] : void 0,
2968
+ properties: {
2969
+ confidence: finding.confidence,
2970
+ cweId: finding.cweId,
2971
+ owaspCategory: finding.owaspCategory
2972
+ }
2973
+ };
2974
+ }
2975
+ var SarifReporter = class {
2976
+ name = "sarif";
2977
+ format(result) {
2978
+ const rulesMap = /* @__PURE__ */ new Map();
2979
+ for (const finding of result.findings) {
2980
+ if (!rulesMap.has(finding.ruleId)) {
2981
+ rulesMap.set(finding.ruleId, buildRule(finding));
2982
+ }
2983
+ }
2984
+ const sarif = {
2985
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
2986
+ version: "2.1.0",
2987
+ runs: [
2988
+ {
2989
+ tool: {
2990
+ driver: {
2991
+ name: TOOL.NAME,
2992
+ version: TOOL.VERSION,
2993
+ informationUri: TOOL.REPO_URL,
2994
+ rules: Array.from(rulesMap.values())
2995
+ }
2996
+ },
2997
+ results: result.findings.map(buildResult),
2998
+ invocations: [
2999
+ {
3000
+ executionSuccessful: true,
3001
+ endTimeUtc: result.timestamp
3002
+ }
3003
+ ]
3004
+ }
3005
+ ]
3006
+ };
3007
+ return JSON.stringify(sarif, null, 2);
3008
+ }
3009
+ };
3010
+
3011
+ // src/internal/vuln-checker/types.ts
3012
+ init_esm_shims();
3013
+
3014
+ // src/internal/vuln-checker/index.ts
3015
+ var SEVERITY_ORDER2 = {
3016
+ critical: 5,
3017
+ high: 4,
3018
+ medium: 3,
3019
+ low: 2,
3020
+ info: 1
3021
+ };
3022
+ async function buildInlineAnnotationsMap(files) {
3023
+ const inlineAnnotations = /* @__PURE__ */ new Map();
3024
+ for (const file of files) {
3025
+ const annotations = await parseInlineAnnotations(file);
3026
+ if (annotations.length === 0) continue;
3027
+ const lineRules = buildLineRulesSet(annotations);
3028
+ inlineAnnotations.set(file, lineRules);
3029
+ }
3030
+ return inlineAnnotations;
3031
+ }
3032
+ function buildLineRulesSet(annotations) {
3033
+ const lineRules = /* @__PURE__ */ new Set();
3034
+ for (const ann of annotations) {
3035
+ const key = getAnnotationKey(ann);
3036
+ if (key) lineRules.add(key);
3037
+ }
3038
+ return lineRules;
3039
+ }
3040
+ function getAnnotationKey(ann) {
3041
+ if (ann.scope === "line" || ann.scope === "next-line") {
3042
+ const targetLine = ann.scope === "next-line" ? ann.line + 1 : ann.line;
3043
+ return `${targetLine}:${ann.ruleId}`;
3044
+ }
3045
+ if (ann.scope === "file") {
3046
+ return `file:${ann.ruleId}`;
3047
+ }
3048
+ return null;
3049
+ }
3050
+ function isIgnoredByAnnotation(finding, fileAnnotations) {
3051
+ if (!fileAnnotations) return false;
3052
+ return fileAnnotations.has(`${finding.location.line}:${finding.ruleId}`) || fileAnnotations.has(`${finding.location.line}:*`) || fileAnnotations.has(`file:${finding.ruleId}`) || fileAnnotations.has("file:*");
3053
+ }
3054
+ function buildSummary(findings, ignoredCount) {
3055
+ return {
3056
+ total: findings.length,
3057
+ critical: findings.filter((f) => f.severity === "critical").length,
3058
+ high: findings.filter((f) => f.severity === "high").length,
3059
+ medium: findings.filter((f) => f.severity === "medium").length,
3060
+ low: findings.filter((f) => f.severity === "low").length,
3061
+ info: findings.filter((f) => f.severity === "info").length,
3062
+ ignored: ignoredCount
3063
+ };
3064
+ }
3065
+ var VulnChecker = class {
3066
+ options;
3067
+ config;
3068
+ ignores;
3069
+ analyzers;
3070
+ reporters;
3071
+ errors = [];
3072
+ constructor(options) {
3073
+ this.options = {
3074
+ rootDir: options.rootDir,
3075
+ configFile: options.configFile || ".vuln-checker.yml",
3076
+ ignoreFile: options.ignoreFile || ".vuln-ignore.yml",
3077
+ categories: options.categories || [
3078
+ "injection",
3079
+ "auth",
3080
+ "crypto",
3081
+ "secret",
3082
+ "config",
3083
+ "rls",
3084
+ "dependency",
3085
+ "design"
3086
+ ],
3087
+ minSeverity: options.minSeverity || "low",
3088
+ format: options.format || "console",
3089
+ showIgnored: options.showIgnored || false,
3090
+ include: options.include || ["**/*.ts", "**/*.tsx", "**/*.sql"],
3091
+ exclude: options.exclude || [
3092
+ "**/node_modules/**",
3093
+ "**/dist/**",
3094
+ "**/.next/**",
3095
+ "**/coverage/**"
3096
+ ]
3097
+ };
3098
+ this.config = { version: 1 };
3099
+ this.ignores = [];
3100
+ this.analyzers = [
3101
+ new TypeScriptAnalyzer(),
3102
+ new SecretAnalyzer(),
3103
+ new DependencyAnalyzer(),
3104
+ new RLSAnalyzer()
3105
+ ];
3106
+ this.reporters = /* @__PURE__ */ new Map([
3107
+ ["json", new JsonReporter()],
3108
+ ["sarif", new SarifReporter()],
3109
+ ["markdown", new MarkdownReporter()],
3110
+ ["console", new ConsoleReporter()]
3111
+ ]);
3112
+ }
3113
+ async init() {
3114
+ this.config = await loadConfig(this.options.configFile, this.options.rootDir);
3115
+ this.ignores = await loadIgnores(this.options.ignoreFile, this.options.rootDir);
3116
+ if (this.config.ignores) {
3117
+ this.ignores.push(...this.config.ignores);
3118
+ }
3119
+ }
3120
+ async scan() {
3121
+ await this.init();
3122
+ const startTime = Date.now();
3123
+ const files = await this.getFiles();
3124
+ const inlineAnnotations = await buildInlineAnnotationsMap(files);
3125
+ const { findings, ignoredFindings } = await this.runAnalyzers(inlineAnnotations);
3126
+ findings.sort((a, b) => SEVERITY_ORDER2[b.severity] - SEVERITY_ORDER2[a.severity]);
3127
+ const result = {
3128
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3129
+ summary: buildSummary(findings, ignoredFindings.length),
3130
+ findings,
3131
+ metadata: {
3132
+ duration: Date.now() - startTime,
3133
+ filesScanned: files.length,
3134
+ rulesEvaluated: this.analyzers.reduce((acc, a) => acc + a.categories.length * 10, 0),
3135
+ rootDir: this.options.rootDir
3136
+ }
3137
+ };
3138
+ if (this.options.showIgnored) {
3139
+ result.ignoredFindings = ignoredFindings;
3140
+ }
3141
+ return result;
3142
+ }
3143
+ async getFiles() {
3144
+ validateGlobPatterns(this.options.include);
3145
+ if (this.options.exclude) {
3146
+ validateGlobPatterns(this.options.exclude);
3147
+ }
3148
+ const files = await glob(this.options.include, {
3149
+ cwd: this.options.rootDir,
3150
+ ignore: this.options.exclude,
3151
+ absolute: true
3152
+ });
3153
+ return filterPathsWithinBoundary(files, this.options.rootDir);
3154
+ }
3155
+ async runAnalyzers(inlineAnnotations) {
3156
+ const findings = [];
3157
+ const ignoredFindings = [];
3158
+ const analyzerOptions = {
3159
+ rootDir: this.options.rootDir,
3160
+ include: this.options.include,
3161
+ exclude: this.options.exclude,
3162
+ ignores: this.ignores
3163
+ };
3164
+ for (const analyzer of this.analyzers) {
3165
+ if (!this.shouldRunAnalyzer(analyzer)) continue;
3166
+ try {
3167
+ const result = await analyzer.analyze(analyzerOptions);
3168
+ this.classifyFindings(result, inlineAnnotations, findings, ignoredFindings);
3169
+ } catch (error) {
3170
+ this.errors.push({ analyzer: analyzer.name, error });
3171
+ }
3172
+ }
3173
+ return { findings, ignoredFindings };
3174
+ }
3175
+ shouldRunAnalyzer(analyzer) {
3176
+ return analyzer.categories.some((cat) => this.options.categories.includes(cat));
3177
+ }
3178
+ classifyFindings(analyzerFindings, inlineAnnotations, findings, ignoredFindings) {
3179
+ for (const finding of analyzerFindings) {
3180
+ if (!this.meetsMinSeverity(finding)) continue;
3181
+ const isIgnored = this.isFindingIgnored(finding, inlineAnnotations);
3182
+ if (isIgnored) {
3183
+ ignoredFindings.push(finding);
3184
+ } else {
3185
+ findings.push(finding);
3186
+ }
3187
+ }
3188
+ }
3189
+ meetsMinSeverity(finding) {
3190
+ return SEVERITY_ORDER2[finding.severity] >= SEVERITY_ORDER2[this.options.minSeverity];
3191
+ }
3192
+ isFindingIgnored(finding, inlineAnnotations) {
3193
+ const isGloballyIgnored = this.ignores.some((ignore) => matchIgnoreRule(ignore, finding));
3194
+ const fileAnnotations = inlineAnnotations.get(finding.location.file);
3195
+ const isInlineIgnored = isIgnoredByAnnotation(finding, fileAnnotations);
3196
+ return isGloballyIgnored || isInlineIgnored;
3197
+ }
3198
+ format(result) {
3199
+ const reporter = this.reporters.get(this.options.format);
3200
+ if (!reporter) {
3201
+ throw new Error(`Unknown format: ${this.options.format}`);
3202
+ }
3203
+ return reporter.format(result);
3204
+ }
3205
+ async run() {
3206
+ const result = await this.scan();
3207
+ return this.format(result);
3208
+ }
3209
+ getExitCode(result, failOn = "high") {
3210
+ const threshold = SEVERITY_ORDER2[failOn];
3211
+ if (result.summary.critical > 0 && SEVERITY_ORDER2.critical >= threshold) return 1;
3212
+ if (result.summary.high > 0 && SEVERITY_ORDER2.high >= threshold) return 1;
3213
+ if (result.summary.medium > 0 && SEVERITY_ORDER2.medium >= threshold) return 1;
3214
+ if (result.summary.low > 0 && SEVERITY_ORDER2.low >= threshold) return 1;
3215
+ return 0;
3216
+ }
3217
+ /** Get errors that occurred during scanning */
3218
+ getErrors() {
3219
+ return this.errors;
3220
+ }
3221
+ };
3222
+
3223
+ export { VulnChecker };