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