@oculum/scanner 1.0.9 → 1.0.11

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 (365) hide show
  1. package/dist/baseline/diff.d.ts +32 -0
  2. package/dist/baseline/diff.d.ts.map +1 -0
  3. package/dist/baseline/diff.js +119 -0
  4. package/dist/baseline/diff.js.map +1 -0
  5. package/dist/baseline/index.d.ts +9 -0
  6. package/dist/baseline/index.d.ts.map +1 -0
  7. package/dist/baseline/index.js +19 -0
  8. package/dist/baseline/index.js.map +1 -0
  9. package/dist/baseline/manager.d.ts +67 -0
  10. package/dist/baseline/manager.d.ts.map +1 -0
  11. package/dist/baseline/manager.js +180 -0
  12. package/dist/baseline/manager.js.map +1 -0
  13. package/dist/baseline/types.d.ts +91 -0
  14. package/dist/baseline/types.d.ts.map +1 -0
  15. package/dist/baseline/types.js +12 -0
  16. package/dist/baseline/types.js.map +1 -0
  17. package/dist/formatters/cli-terminal.d.ts +38 -0
  18. package/dist/formatters/cli-terminal.d.ts.map +1 -1
  19. package/dist/formatters/cli-terminal.js +365 -42
  20. package/dist/formatters/cli-terminal.js.map +1 -1
  21. package/dist/formatters/github-comment.d.ts +1 -1
  22. package/dist/formatters/github-comment.d.ts.map +1 -1
  23. package/dist/formatters/github-comment.js +75 -11
  24. package/dist/formatters/github-comment.js.map +1 -1
  25. package/dist/formatters/index.d.ts +1 -1
  26. package/dist/formatters/index.d.ts.map +1 -1
  27. package/dist/formatters/index.js +4 -1
  28. package/dist/formatters/index.js.map +1 -1
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +155 -16
  32. package/dist/index.js.map +1 -1
  33. package/dist/layer1/config-audit.d.ts.map +1 -1
  34. package/dist/layer1/config-audit.js +20 -3
  35. package/dist/layer1/config-audit.js.map +1 -1
  36. package/dist/layer1/config-mcp-audit.d.ts +20 -0
  37. package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
  38. package/dist/layer1/config-mcp-audit.js +239 -0
  39. package/dist/layer1/config-mcp-audit.js.map +1 -0
  40. package/dist/layer1/index.d.ts +1 -0
  41. package/dist/layer1/index.d.ts.map +1 -1
  42. package/dist/layer1/index.js +9 -1
  43. package/dist/layer1/index.js.map +1 -1
  44. package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
  45. package/dist/layer2/ai-agent-tools.js +303 -0
  46. package/dist/layer2/ai-agent-tools.js.map +1 -1
  47. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
  48. package/dist/layer2/ai-endpoint-protection.js +17 -3
  49. package/dist/layer2/ai-endpoint-protection.js.map +1 -1
  50. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
  51. package/dist/layer2/ai-execution-sinks.js +462 -12
  52. package/dist/layer2/ai-execution-sinks.js.map +1 -1
  53. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
  54. package/dist/layer2/ai-fingerprinting.js +3 -0
  55. package/dist/layer2/ai-fingerprinting.js.map +1 -1
  56. package/dist/layer2/ai-mcp-security.d.ts +17 -0
  57. package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
  58. package/dist/layer2/ai-mcp-security.js +679 -0
  59. package/dist/layer2/ai-mcp-security.js.map +1 -0
  60. package/dist/layer2/ai-package-hallucination.d.ts +19 -0
  61. package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
  62. package/dist/layer2/ai-package-hallucination.js +696 -0
  63. package/dist/layer2/ai-package-hallucination.js.map +1 -0
  64. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
  65. package/dist/layer2/ai-prompt-hygiene.js +495 -9
  66. package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
  67. package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
  68. package/dist/layer2/ai-rag-safety.js +372 -1
  69. package/dist/layer2/ai-rag-safety.js.map +1 -1
  70. package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
  71. package/dist/layer2/auth-antipatterns.js +4 -0
  72. package/dist/layer2/auth-antipatterns.js.map +1 -1
  73. package/dist/layer2/byok-patterns.d.ts.map +1 -1
  74. package/dist/layer2/byok-patterns.js +3 -0
  75. package/dist/layer2/byok-patterns.js.map +1 -1
  76. package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
  77. package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
  78. package/dist/layer2/dangerous-functions/child-process.js +74 -0
  79. package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
  80. package/dist/layer2/dangerous-functions/dom-xss.d.ts +29 -0
  81. package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
  82. package/dist/layer2/dangerous-functions/dom-xss.js +179 -0
  83. package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
  84. package/dist/layer2/dangerous-functions/index.d.ts +13 -0
  85. package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
  86. package/dist/layer2/dangerous-functions/index.js +621 -0
  87. package/dist/layer2/dangerous-functions/index.js.map +1 -0
  88. package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
  89. package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
  90. package/dist/layer2/dangerous-functions/json-parse.js +319 -0
  91. package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
  92. package/dist/layer2/dangerous-functions/math-random.d.ts +61 -0
  93. package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
  94. package/dist/layer2/dangerous-functions/math-random.js +459 -0
  95. package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
  96. package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
  97. package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
  98. package/dist/layer2/dangerous-functions/patterns.js +161 -0
  99. package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
  100. package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
  101. package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
  102. package/dist/layer2/dangerous-functions/request-validation.js +119 -0
  103. package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
  104. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +23 -0
  105. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
  106. package/dist/layer2/dangerous-functions/utils/control-flow.js +149 -0
  107. package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
  108. package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
  109. package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
  110. package/dist/layer2/dangerous-functions/utils/helpers.js +124 -0
  111. package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
  112. package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
  113. package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
  114. package/dist/layer2/dangerous-functions/utils/index.js +23 -0
  115. package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
  116. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
  117. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
  118. package/dist/layer2/dangerous-functions/utils/schema-validation.js +89 -0
  119. package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
  120. package/dist/layer2/data-exposure.d.ts.map +1 -1
  121. package/dist/layer2/data-exposure.js +3 -0
  122. package/dist/layer2/data-exposure.js.map +1 -1
  123. package/dist/layer2/framework-checks.d.ts.map +1 -1
  124. package/dist/layer2/framework-checks.js +3 -0
  125. package/dist/layer2/framework-checks.js.map +1 -1
  126. package/dist/layer2/index.d.ts +3 -0
  127. package/dist/layer2/index.d.ts.map +1 -1
  128. package/dist/layer2/index.js +61 -2
  129. package/dist/layer2/index.js.map +1 -1
  130. package/dist/layer2/logic-gates.d.ts.map +1 -1
  131. package/dist/layer2/logic-gates.js +4 -0
  132. package/dist/layer2/logic-gates.js.map +1 -1
  133. package/dist/layer2/model-supply-chain.d.ts +20 -0
  134. package/dist/layer2/model-supply-chain.d.ts.map +1 -0
  135. package/dist/layer2/model-supply-chain.js +376 -0
  136. package/dist/layer2/model-supply-chain.js.map +1 -0
  137. package/dist/layer2/risky-imports.d.ts.map +1 -1
  138. package/dist/layer2/risky-imports.js +4 -0
  139. package/dist/layer2/risky-imports.js.map +1 -1
  140. package/dist/layer2/variables.d.ts.map +1 -1
  141. package/dist/layer2/variables.js +4 -0
  142. package/dist/layer2/variables.js.map +1 -1
  143. package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
  144. package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
  145. package/dist/layer3/anthropic/auto-dismiss.js +188 -0
  146. package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
  147. package/dist/layer3/anthropic/clients.d.ts +44 -0
  148. package/dist/layer3/anthropic/clients.d.ts.map +1 -0
  149. package/dist/layer3/anthropic/clients.js +81 -0
  150. package/dist/layer3/anthropic/clients.js.map +1 -0
  151. package/dist/layer3/anthropic/index.d.ts +41 -0
  152. package/dist/layer3/anthropic/index.d.ts.map +1 -0
  153. package/dist/layer3/anthropic/index.js +141 -0
  154. package/dist/layer3/anthropic/index.js.map +1 -0
  155. package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
  156. package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
  157. package/dist/layer3/anthropic/prompts/index.js +14 -0
  158. package/dist/layer3/anthropic/prompts/index.js.map +1 -0
  159. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
  160. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
  161. package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
  162. package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
  163. package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
  164. package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
  165. package/dist/layer3/anthropic/prompts/validation.js +421 -0
  166. package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
  167. package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
  168. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
  169. package/dist/layer3/anthropic/providers/anthropic.js +266 -0
  170. package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
  171. package/dist/layer3/anthropic/providers/index.d.ts +8 -0
  172. package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
  173. package/dist/layer3/anthropic/providers/index.js +15 -0
  174. package/dist/layer3/anthropic/providers/index.js.map +1 -0
  175. package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
  176. package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
  177. package/dist/layer3/anthropic/providers/openai.js +340 -0
  178. package/dist/layer3/anthropic/providers/openai.js.map +1 -0
  179. package/dist/layer3/anthropic/request-builder.d.ts +20 -0
  180. package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
  181. package/dist/layer3/anthropic/request-builder.js +134 -0
  182. package/dist/layer3/anthropic/request-builder.js.map +1 -0
  183. package/dist/layer3/anthropic/types.d.ts +88 -0
  184. package/dist/layer3/anthropic/types.d.ts.map +1 -0
  185. package/dist/layer3/anthropic/types.js +38 -0
  186. package/dist/layer3/anthropic/types.js.map +1 -0
  187. package/dist/layer3/anthropic/utils/index.d.ts +9 -0
  188. package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
  189. package/dist/layer3/anthropic/utils/index.js +24 -0
  190. package/dist/layer3/anthropic/utils/index.js.map +1 -0
  191. package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
  192. package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
  193. package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
  194. package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
  195. package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
  196. package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
  197. package/dist/layer3/anthropic/utils/response-parser.js +285 -0
  198. package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
  199. package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
  200. package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
  201. package/dist/layer3/anthropic/utils/retry.js +62 -0
  202. package/dist/layer3/anthropic/utils/retry.js.map +1 -0
  203. package/dist/layer3/index.d.ts +1 -0
  204. package/dist/layer3/index.d.ts.map +1 -1
  205. package/dist/layer3/index.js +16 -6
  206. package/dist/layer3/index.js.map +1 -1
  207. package/dist/layer3/osv-check.d.ts +75 -0
  208. package/dist/layer3/osv-check.d.ts.map +1 -0
  209. package/dist/layer3/osv-check.js +308 -0
  210. package/dist/layer3/osv-check.js.map +1 -0
  211. package/dist/rules/framework-fixes.d.ts +48 -0
  212. package/dist/rules/framework-fixes.d.ts.map +1 -0
  213. package/dist/rules/framework-fixes.js +439 -0
  214. package/dist/rules/framework-fixes.js.map +1 -0
  215. package/dist/rules/index.d.ts +8 -0
  216. package/dist/rules/index.d.ts.map +1 -0
  217. package/dist/rules/index.js +18 -0
  218. package/dist/rules/index.js.map +1 -0
  219. package/dist/rules/metadata.d.ts +43 -0
  220. package/dist/rules/metadata.d.ts.map +1 -0
  221. package/dist/rules/metadata.js +734 -0
  222. package/dist/rules/metadata.js.map +1 -0
  223. package/dist/suppression/config-loader.d.ts +74 -0
  224. package/dist/suppression/config-loader.d.ts.map +1 -0
  225. package/dist/suppression/config-loader.js +424 -0
  226. package/dist/suppression/config-loader.js.map +1 -0
  227. package/dist/suppression/hash.d.ts +48 -0
  228. package/dist/suppression/hash.d.ts.map +1 -0
  229. package/dist/suppression/hash.js +88 -0
  230. package/dist/suppression/hash.js.map +1 -0
  231. package/dist/suppression/index.d.ts +11 -0
  232. package/dist/suppression/index.d.ts.map +1 -0
  233. package/dist/suppression/index.js +39 -0
  234. package/dist/suppression/index.js.map +1 -0
  235. package/dist/suppression/inline-parser.d.ts +39 -0
  236. package/dist/suppression/inline-parser.d.ts.map +1 -0
  237. package/dist/suppression/inline-parser.js +218 -0
  238. package/dist/suppression/inline-parser.js.map +1 -0
  239. package/dist/suppression/manager.d.ts +94 -0
  240. package/dist/suppression/manager.d.ts.map +1 -0
  241. package/dist/suppression/manager.js +292 -0
  242. package/dist/suppression/manager.js.map +1 -0
  243. package/dist/suppression/types.d.ts +151 -0
  244. package/dist/suppression/types.d.ts.map +1 -0
  245. package/dist/suppression/types.js +28 -0
  246. package/dist/suppression/types.js.map +1 -0
  247. package/dist/tiers.d.ts +1 -1
  248. package/dist/tiers.d.ts.map +1 -1
  249. package/dist/tiers.js +27 -0
  250. package/dist/tiers.js.map +1 -1
  251. package/dist/types.d.ts +62 -1
  252. package/dist/types.d.ts.map +1 -1
  253. package/dist/types.js.map +1 -1
  254. package/dist/utils/context-helpers.d.ts +4 -0
  255. package/dist/utils/context-helpers.d.ts.map +1 -1
  256. package/dist/utils/context-helpers.js +13 -9
  257. package/dist/utils/context-helpers.js.map +1 -1
  258. package/package.json +4 -2
  259. package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
  260. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
  261. package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
  262. package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
  263. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
  264. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
  265. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
  266. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
  267. package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
  268. package/src/__tests__/benchmark/fixtures/layer2/index.ts +18 -0
  269. package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
  270. package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
  271. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +758 -0
  272. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
  273. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
  274. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
  275. package/src/baseline/__tests__/diff.test.ts +261 -0
  276. package/src/baseline/__tests__/manager.test.ts +225 -0
  277. package/src/baseline/diff.ts +135 -0
  278. package/src/baseline/index.ts +29 -0
  279. package/src/baseline/manager.ts +230 -0
  280. package/src/baseline/types.ts +97 -0
  281. package/src/formatters/cli-terminal.ts +444 -41
  282. package/src/formatters/github-comment.ts +79 -11
  283. package/src/formatters/index.ts +4 -0
  284. package/src/index.ts +197 -14
  285. package/src/layer1/config-audit.ts +24 -3
  286. package/src/layer1/config-mcp-audit.ts +276 -0
  287. package/src/layer1/index.ts +16 -6
  288. package/src/layer2/ai-agent-tools.ts +336 -0
  289. package/src/layer2/ai-endpoint-protection.ts +16 -3
  290. package/src/layer2/ai-execution-sinks.ts +516 -12
  291. package/src/layer2/ai-fingerprinting.ts +5 -1
  292. package/src/layer2/ai-mcp-security.ts +730 -0
  293. package/src/layer2/ai-package-hallucination.ts +791 -0
  294. package/src/layer2/ai-prompt-hygiene.ts +547 -9
  295. package/src/layer2/ai-rag-safety.ts +382 -3
  296. package/src/layer2/auth-antipatterns.ts +5 -0
  297. package/src/layer2/byok-patterns.ts +5 -1
  298. package/src/layer2/dangerous-functions/child-process.ts +98 -0
  299. package/src/layer2/dangerous-functions/dom-xss.ts +220 -0
  300. package/src/layer2/dangerous-functions/index.ts +949 -0
  301. package/src/layer2/dangerous-functions/json-parse.ts +385 -0
  302. package/src/layer2/dangerous-functions/math-random.ts +537 -0
  303. package/src/layer2/dangerous-functions/patterns.ts +174 -0
  304. package/src/layer2/dangerous-functions/request-validation.ts +145 -0
  305. package/src/layer2/dangerous-functions/utils/control-flow.ts +162 -0
  306. package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
  307. package/src/layer2/dangerous-functions/utils/index.ts +25 -0
  308. package/src/layer2/dangerous-functions/utils/schema-validation.ts +91 -0
  309. package/src/layer2/data-exposure.ts +5 -1
  310. package/src/layer2/framework-checks.ts +5 -0
  311. package/src/layer2/index.ts +63 -1
  312. package/src/layer2/logic-gates.ts +5 -0
  313. package/src/layer2/model-supply-chain.ts +456 -0
  314. package/src/layer2/risky-imports.ts +5 -0
  315. package/src/layer2/variables.ts +5 -0
  316. package/src/layer3/__tests__/osv-check.test.ts +384 -0
  317. package/src/layer3/anthropic/auto-dismiss.ts +212 -0
  318. package/src/layer3/anthropic/clients.ts +84 -0
  319. package/src/layer3/anthropic/index.ts +170 -0
  320. package/src/layer3/anthropic/prompts/index.ts +14 -0
  321. package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
  322. package/src/layer3/anthropic/prompts/validation.ts +419 -0
  323. package/src/layer3/anthropic/providers/anthropic.ts +310 -0
  324. package/src/layer3/anthropic/providers/index.ts +8 -0
  325. package/src/layer3/anthropic/providers/openai.ts +384 -0
  326. package/src/layer3/anthropic/request-builder.ts +150 -0
  327. package/src/layer3/anthropic/types.ts +148 -0
  328. package/src/layer3/anthropic/utils/index.ts +26 -0
  329. package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
  330. package/src/layer3/anthropic/utils/response-parser.ts +322 -0
  331. package/src/layer3/anthropic/utils/retry.ts +75 -0
  332. package/src/layer3/index.ts +18 -5
  333. package/src/layer3/osv-check.ts +420 -0
  334. package/src/rules/__tests__/framework-fixes.test.ts +689 -0
  335. package/src/rules/__tests__/metadata.test.ts +218 -0
  336. package/src/rules/framework-fixes.ts +470 -0
  337. package/src/rules/index.ts +21 -0
  338. package/src/rules/metadata.ts +831 -0
  339. package/src/suppression/__tests__/config-loader.test.ts +382 -0
  340. package/src/suppression/__tests__/hash.test.ts +166 -0
  341. package/src/suppression/__tests__/inline-parser.test.ts +212 -0
  342. package/src/suppression/__tests__/manager.test.ts +415 -0
  343. package/src/suppression/config-loader.ts +462 -0
  344. package/src/suppression/hash.ts +95 -0
  345. package/src/suppression/index.ts +51 -0
  346. package/src/suppression/inline-parser.ts +273 -0
  347. package/src/suppression/manager.ts +379 -0
  348. package/src/suppression/types.ts +174 -0
  349. package/src/tiers.ts +36 -0
  350. package/src/types.ts +90 -0
  351. package/src/utils/context-helpers.ts +13 -9
  352. package/dist/layer2/dangerous-functions.d.ts +0 -7
  353. package/dist/layer2/dangerous-functions.d.ts.map +0 -1
  354. package/dist/layer2/dangerous-functions.js +0 -1701
  355. package/dist/layer2/dangerous-functions.js.map +0 -1
  356. package/dist/layer3/anthropic.d.ts +0 -87
  357. package/dist/layer3/anthropic.d.ts.map +0 -1
  358. package/dist/layer3/anthropic.js +0 -1948
  359. package/dist/layer3/anthropic.js.map +0 -1
  360. package/dist/layer3/openai.d.ts +0 -25
  361. package/dist/layer3/openai.d.ts.map +0 -1
  362. package/dist/layer3/openai.js +0 -238
  363. package/dist/layer3/openai.js.map +0 -1
  364. package/src/layer2/dangerous-functions.ts +0 -1940
  365. package/src/layer3/anthropic.ts +0 -2257
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Dangerous Function Pattern Definitions
3
+ *
4
+ * This module defines the patterns for detecting dangerous function calls.
5
+ * These patterns are used by the main detection engine.
6
+ */
7
+
8
+ import type { VulnerabilitySeverity } from '../../types'
9
+
10
+ export interface DangerousFunctionPattern {
11
+ name: string
12
+ pattern: RegExp
13
+ severity: VulnerabilitySeverity
14
+ description: string
15
+ suggestedFix: string
16
+ languages?: string[] // Optional: restrict to specific languages
17
+ }
18
+
19
+ export const DANGEROUS_FUNCTIONS: DangerousFunctionPattern[] = [
20
+ // Code execution
21
+ {
22
+ name: 'eval() usage',
23
+ pattern: /\beval\s*\(/gi,
24
+ severity: 'critical',
25
+ description: 'eval() executes arbitrary code and is a major security risk',
26
+ suggestedFix: 'Use JSON.parse() for JSON data, or refactor to avoid dynamic code execution',
27
+ },
28
+ {
29
+ name: 'Function constructor',
30
+ pattern: /new\s+Function\s*\(/gi,
31
+ severity: 'critical',
32
+ description: 'Function constructor can execute arbitrary code like eval()',
33
+ suggestedFix: 'Refactor to use static functions or safe alternatives',
34
+ },
35
+ {
36
+ name: 'setTimeout/setInterval with string',
37
+ pattern: /set(Timeout|Interval)\s*\(\s*['"`]/gi,
38
+ severity: 'high',
39
+ description: 'setTimeout/setInterval with string argument acts like eval()',
40
+ suggestedFix: 'Pass a function reference instead of a string',
41
+ },
42
+
43
+ // Command injection
44
+ {
45
+ name: 'child_process exec',
46
+ pattern: /\b(exec|execSync|spawn|spawnSync|execFile)\s*\(/gi,
47
+ severity: 'high',
48
+ description: 'Shell command execution can lead to command injection',
49
+ suggestedFix: 'Validate and sanitize all inputs, prefer execFile over exec',
50
+ },
51
+ {
52
+ name: 'os.system/subprocess (Python)',
53
+ pattern: /\b(os\.system|subprocess\.(call|run|Popen|check_output))\s*\(/gi,
54
+ severity: 'high',
55
+ description: 'Shell command execution can lead to command injection',
56
+ suggestedFix: 'Use subprocess with shell=False and pass arguments as a list',
57
+ languages: ['py'],
58
+ },
59
+
60
+ // SQL injection risks
61
+ {
62
+ name: 'Raw SQL query construction',
63
+ pattern: /\.(query|execute|raw)\s*\(\s*[`'"].*\$\{|\.query\s*\(\s*['"].*\+/gi,
64
+ severity: 'critical',
65
+ description: 'String concatenation in SQL queries can lead to SQL injection',
66
+ suggestedFix: 'Use parameterized queries or prepared statements',
67
+ },
68
+ {
69
+ name: 'SQL template literal',
70
+ pattern: /`SELECT.*FROM.*WHERE.*\$\{|`INSERT.*INTO.*VALUES.*\$\{|`UPDATE.*SET.*\$\{|`DELETE.*FROM.*WHERE.*\$\{/gi,
71
+ severity: 'critical',
72
+ description: 'Template literals in SQL queries can lead to SQL injection',
73
+ suggestedFix: 'Use parameterized queries with placeholders (?, $1, etc.)',
74
+ },
75
+
76
+ // XSS risks
77
+ {
78
+ name: 'innerHTML assignment',
79
+ pattern: /\.innerHTML\s*=|\.outerHTML\s*=/gi,
80
+ severity: 'high',
81
+ description: 'Direct innerHTML assignment can lead to XSS vulnerabilities',
82
+ suggestedFix: 'Use textContent for text, or sanitize HTML with DOMPurify',
83
+ },
84
+ {
85
+ name: 'document.write',
86
+ pattern: /document\.write\s*\(/gi,
87
+ severity: 'high',
88
+ description: 'document.write can introduce XSS vulnerabilities',
89
+ suggestedFix: 'Use DOM manipulation methods instead',
90
+ },
91
+ {
92
+ name: 'dangerouslySetInnerHTML',
93
+ pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:/gi,
94
+ severity: 'high',
95
+ description: 'dangerouslySetInnerHTML can lead to XSS if content is not sanitized',
96
+ suggestedFix: 'Sanitize HTML content with DOMPurify before rendering',
97
+ },
98
+
99
+ // Deserialization
100
+ {
101
+ name: 'Unsafe deserialization',
102
+ pattern: /\b(pickle\.loads?|yaml\.load\s*\((?!.*Loader)|unserialize|Marshal\.load)\s*\(/gi,
103
+ severity: 'critical',
104
+ description: 'Unsafe deserialization can lead to remote code execution',
105
+ suggestedFix: 'Use safe loaders (yaml.safe_load) or validate input before deserializing',
106
+ },
107
+ // Note: JSON.parse is handled specially with source-aware severity - see json-parse.ts
108
+ // Note: request.json() is NOT a dangerous function - see request-validation.ts
109
+
110
+ // File system risks
111
+ {
112
+ name: 'Dynamic file path',
113
+ pattern: /\b(readFile|writeFile|readFileSync|writeFileSync|createReadStream|createWriteStream)\s*\(\s*[^'"]/gi,
114
+ severity: 'medium',
115
+ description: 'Dynamic file paths can lead to path traversal attacks',
116
+ suggestedFix: 'Validate and sanitize file paths, use path.resolve with a base directory',
117
+ },
118
+ {
119
+ name: 'Path traversal risk',
120
+ pattern: /path\.(join|resolve)\s*\([^)]*req\.(params|query|body)/gi,
121
+ severity: 'high',
122
+ description: 'User input in file paths can lead to path traversal attacks',
123
+ suggestedFix: 'Validate paths and ensure they stay within allowed directories',
124
+ },
125
+
126
+ // Crypto weaknesses
127
+ {
128
+ name: 'Math.random for security',
129
+ pattern: /Math\.random\s*\(\s*\)/gi,
130
+ severity: 'medium',
131
+ description: 'Math.random() is not cryptographically secure',
132
+ suggestedFix: 'Use crypto.randomBytes() or crypto.getRandomValues() for security-sensitive operations',
133
+ },
134
+
135
+ // Regex DoS
136
+ {
137
+ name: 'Potentially unsafe regex',
138
+ pattern: /new\s+RegExp\s*\(\s*[^'"]/gi,
139
+ severity: 'medium',
140
+ description: 'Dynamic regex construction can lead to ReDoS attacks',
141
+ suggestedFix: 'Validate regex patterns and consider using safe-regex library',
142
+ },
143
+
144
+ // Prototype pollution
145
+ {
146
+ name: 'Object.assign with user input',
147
+ pattern: /Object\.assign\s*\(\s*\{\s*\}\s*,\s*(req\.|request\.|body|params|query)/gi,
148
+ severity: 'high',
149
+ description: 'Object.assign with user input can lead to prototype pollution',
150
+ suggestedFix: 'Validate and sanitize input, or use a safe merge function',
151
+ },
152
+ {
153
+ name: 'Spread operator with user input',
154
+ pattern: /\{\s*\.\.\.req\.(body|params|query)|\.\.\.request\.(body|params|query)/gi,
155
+ severity: 'medium',
156
+ description: 'Spreading user input can lead to mass assignment vulnerabilities',
157
+ suggestedFix: 'Explicitly pick allowed properties instead of spreading all input',
158
+ },
159
+ ]
160
+
161
+ /**
162
+ * Check if file matches language filter
163
+ */
164
+ export function matchesLanguage(filePath: string, languages?: string[]): boolean {
165
+ if (!languages || languages.length === 0) return true
166
+
167
+ const ext = filePath.split('.').pop()?.toLowerCase() || ''
168
+ return languages.some(lang => {
169
+ if (lang === 'py') return ext === 'py'
170
+ if (lang === 'js') return ['js', 'jsx', 'mjs', 'cjs'].includes(ext)
171
+ if (lang === 'ts') return ['ts', 'tsx'].includes(ext)
172
+ return ext === lang
173
+ })
174
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Request Body Validation Detection
3
+ *
4
+ * Detection logic for request.json() / req.json() usage without
5
+ * proper schema validation.
6
+ */
7
+
8
+ import type { Vulnerability } from '../../types'
9
+ import { isComment } from '../../utils/context-helpers'
10
+ import { hasManualValidation } from './utils/schema-validation'
11
+ import { hasThrowingAuthHelper } from './utils/helpers'
12
+
13
+ /**
14
+ * Detect request.json() / req.json() and suggest schema validation
15
+ * This is NOT a dangerous function - it's a prompt for best practices
16
+ */
17
+ export function detectRequestJsonValidation(
18
+ content: string,
19
+ filePath: string,
20
+ isTestFile: boolean,
21
+ vulnerabilities: Vulnerability[]
22
+ ): void {
23
+ // Only check API route files
24
+ if (
25
+ !/\/(api|routes?|handlers?|controllers?)\//i.test(filePath) &&
26
+ !/route\.(ts|js)$/i.test(filePath)
27
+ ) {
28
+ return
29
+ }
30
+
31
+ // Skip if route has throwing auth helper - these are already protected routes
32
+ // and the schema validation suggestion is lower priority
33
+ if (hasThrowingAuthHelper(content)) {
34
+ return
35
+ }
36
+
37
+ const lines = content.split('\n')
38
+ // Matches: request.json(), req.json(), await request.json(), etc.
39
+ const requestJsonPattern = /\b(request|req)\.json\s*\(\s*\)/gi
40
+
41
+ // Check if file has schema validation (library-based)
42
+ const hasSchemaLibrary =
43
+ /\b(zod|yup|joi|ajv|superstruct|valibot|typebox)\b/i.test(content) ||
44
+ /\.parse\s*\(|\.validate\s*\(|\.safeParse\s*\(/i.test(content)
45
+
46
+ // If file has schema library validation, don't report
47
+ if (hasSchemaLibrary) return
48
+
49
+ // Check for manual validation patterns (less robust but still indicates intent)
50
+ const hasManualCheck = hasManualValidation(content)
51
+
52
+ // Track instances for potential aggregation
53
+ const instances: { lineNumber: number; lineContent: string }[] = []
54
+
55
+ lines.forEach((line, index) => {
56
+ if (isComment(line)) return
57
+
58
+ requestJsonPattern.lastIndex = 0
59
+ if (!requestJsonPattern.test(line)) return
60
+
61
+ // Check if there's validation nearby (within 10 lines after)
62
+ const startCheck = index
63
+ const endCheck = Math.min(lines.length, index + 10)
64
+ const nearbyContent = lines.slice(startCheck, endCheck).join('\n')
65
+
66
+ // If there's validation in the nearby lines, skip
67
+ if (
68
+ /\.parse\s*\(|\.validate\s*\(|\.safeParse\s*\(|schema\./i.test(
69
+ nearbyContent
70
+ )
71
+ ) {
72
+ return
73
+ }
74
+
75
+ // If manual validation is present, skip individual reporting but track for aggregate
76
+ if (hasManualCheck) {
77
+ instances.push({ lineNumber: index + 1, lineContent: line.trim() })
78
+ return
79
+ }
80
+
81
+ if (isTestFile) {
82
+ return // Don't report in test files
83
+ }
84
+
85
+ instances.push({ lineNumber: index + 1, lineContent: line.trim() })
86
+ })
87
+
88
+ // Don't report if no instances found
89
+ if (instances.length === 0) return
90
+
91
+ // If manual validation exists, create a single info-level note
92
+ if (hasManualCheck && instances.length > 0) {
93
+ vulnerabilities.push({
94
+ id: `request-json-manual-${filePath}`,
95
+ filePath,
96
+ lineNumber: instances[0].lineNumber,
97
+ lineContent: instances[0].lineContent,
98
+ severity: 'info',
99
+ category: 'dangerous_function',
100
+ title: 'Request body with manual validation',
101
+ description: `API endpoint parses request body with manual validation patterns detected. Consider using a schema library (zod, yup) for more robust type-safe validation.`,
102
+ suggestedFix:
103
+ 'While manual validation works, schema libraries provide better TypeScript integration and error messages.',
104
+ confidence: 'low',
105
+ layer: 2,
106
+ })
107
+ return
108
+ }
109
+
110
+ // Aggregate if multiple instances without validation
111
+ if (instances.length >= 2) {
112
+ const lineNumbers = instances.map(i => i.lineNumber).slice(0, 5)
113
+ vulnerabilities.push({
114
+ id: `request-json-aggregated-${filePath}`,
115
+ filePath,
116
+ lineNumber: instances[0].lineNumber,
117
+ lineContent: `${instances.length} instances`,
118
+ severity: 'info',
119
+ category: 'dangerous_function',
120
+ title: `Request body without schema validation (${instances.length} instances)`,
121
+ description: `API endpoint parses request body without visible schema validation at lines: ${lineNumbers.join(', ')}. Consider validating the shape of incoming data.`,
122
+ suggestedFix:
123
+ 'Add schema validation (e.g., zod): const body = await request.json(); const data = schema.parse(body);',
124
+ confidence: 'low',
125
+ layer: 2,
126
+ })
127
+ } else {
128
+ // Single instance
129
+ vulnerabilities.push({
130
+ id: `request-json-${filePath}-${instances[0].lineNumber}`,
131
+ filePath,
132
+ lineNumber: instances[0].lineNumber,
133
+ lineContent: instances[0].lineContent,
134
+ severity: 'info',
135
+ category: 'dangerous_function',
136
+ title: 'Request body without schema validation',
137
+ description:
138
+ 'API endpoint parses request body without visible schema validation. Consider validating the shape of incoming data.',
139
+ suggestedFix:
140
+ 'Add schema validation (e.g., zod): const body = await request.json(); const data = schema.parse(body);',
141
+ confidence: 'low',
142
+ layer: 2,
143
+ })
144
+ }
145
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Control Flow Analysis Utilities
3
+ *
4
+ * Functions for analyzing code control flow, including try-catch detection
5
+ * and function context extraction.
6
+ */
7
+
8
+ import { isComment } from '../../../utils/context-helpers'
9
+
10
+ /**
11
+ * Check if a line is inside a try-catch block
12
+ * Looks for enclosing try { ... } catch pattern
13
+ */
14
+ export function isInsideTryCatch(content: string, lineNumber: number): boolean {
15
+ const lines = content.split('\n')
16
+
17
+ // Track brace depth and whether we're in a try block
18
+ let tryDepth = 0
19
+ let inTryBlock = false
20
+ const braceStack: Array<'try' | 'other'> = []
21
+
22
+ // Scan from start to the target line
23
+ for (let i = 0; i < lineNumber && i < lines.length; i++) {
24
+ const line = lines[i]
25
+
26
+ // Check for try keyword (not in a comment)
27
+ if (/\btry\s*\{/.test(line) && !isComment(line)) {
28
+ inTryBlock = true
29
+ tryDepth++
30
+ // Count opening braces on this line
31
+ const openBraces = (line.match(/\{/g) || []).length
32
+ const closeBraces = (line.match(/\}/g) || []).length
33
+ for (let j = 0; j < openBraces - closeBraces; j++) {
34
+ braceStack.push('try')
35
+ }
36
+ } else if (/\bcatch\s*\(/.test(line) && !isComment(line)) {
37
+ // Entering catch block - still protected
38
+ // Don't decrement tryDepth yet
39
+ } else if (/\bfinally\s*\{/.test(line) && !isComment(line)) {
40
+ // Entering finally block - still protected
41
+ } else {
42
+ // Track regular braces
43
+ const openBraces = (line.match(/\{/g) || []).length
44
+ const closeBraces = (line.match(/\}/g) || []).length
45
+
46
+ for (let j = 0; j < openBraces; j++) {
47
+ braceStack.push(inTryBlock && tryDepth > 0 ? 'try' : 'other')
48
+ }
49
+
50
+ for (let j = 0; j < closeBraces; j++) {
51
+ const popped = braceStack.pop()
52
+ if (popped === 'try') {
53
+ tryDepth--
54
+ if (tryDepth === 0) {
55
+ inTryBlock = false
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ return tryDepth > 0
63
+ }
64
+
65
+ /**
66
+ * Simpler heuristic: check if there's a try-catch in the same function scope
67
+ * Looks for try { before the line and } catch after, within reasonable bounds
68
+ */
69
+ export function hasTryCatchNearby(content: string, lineNumber: number, windowSize: number = 20): boolean {
70
+ const lines = content.split('\n')
71
+ const startLine = Math.max(0, lineNumber - windowSize)
72
+ const endLine = Math.min(lines.length, lineNumber + windowSize)
73
+
74
+ // Look backward for 'try {'
75
+ let foundTry = false
76
+ for (let i = lineNumber - 1; i >= startLine; i--) {
77
+ const line = lines[i]
78
+ if (/\btry\s*\{/.test(line) && !isComment(line)) {
79
+ foundTry = true
80
+ break
81
+ }
82
+ // Stop if we hit a function boundary
83
+ if (/\b(function|async function|=>|class)\b/.test(line) && /\{/.test(line)) {
84
+ break
85
+ }
86
+ }
87
+
88
+ if (!foundTry) return false
89
+
90
+ // Look forward for '} catch'
91
+ for (let i = lineNumber; i < endLine; i++) {
92
+ const line = lines[i]
93
+ if (/\}\s*catch\s*\(/.test(line) && !isComment(line)) {
94
+ return true
95
+ }
96
+ // Stop if we hit another function boundary
97
+ if (i > lineNumber && /\b(function|async function|class)\b/.test(line) && /\{/.test(line)) {
98
+ break
99
+ }
100
+ }
101
+
102
+ return false
103
+ }
104
+
105
+ /**
106
+ * Extract function context where a call is being made
107
+ * Looks backwards from the current line to find enclosing function name
108
+ * Returns lowercase function name or null if not found
109
+ */
110
+ export function extractFunctionContext(content: string, lineNumber: number): string | null {
111
+ const lines = content.split('\n')
112
+ const start = Math.max(0, lineNumber - 20) // Increased from 10 to 20 for nested callbacks
113
+
114
+ // Look backwards for function declaration
115
+ for (let i = lineNumber; i >= start; i--) {
116
+ const line = lines[i]
117
+
118
+ // Skip anonymous arrow functions in callbacks (e.g., .map((x) => ...), .replace(/x/g, (c) => ...))
119
+ // These are not the function context we're looking for
120
+ // Look for pattern: .methodName(..., (param) => or .methodName(...(param) =>
121
+ const hasMethodCallWithArrowCallback = /\.\w+\(.*\([^)]*\)\s*=>/.test(line)
122
+
123
+ // Skip lines that only have arrow callbacks in method calls
124
+ if (hasMethodCallWithArrowCallback && !/^(const|let|var|function|async|export)/.test(line.trim())) {
125
+ continue
126
+ }
127
+
128
+ // Named function declaration: function funcName(
129
+ const funcMatch = line.match(/function\s+(\w+)\s*\(/)
130
+ if (funcMatch) {
131
+ return funcMatch[1].toLowerCase()
132
+ }
133
+
134
+ // Arrow function with const/let/var: const funcName = () => | const funcName = async () =>
135
+ // Must have => after the parameters to distinguish from const x = (expression)
136
+ // Also handles TypeScript return type annotations: const funcName = (): string =>
137
+ const arrowMatch = line.match(/(const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)(?:\s*:\s*\w+)?\s*=>/)
138
+ if (arrowMatch) {
139
+ return arrowMatch[2].toLowerCase()
140
+ }
141
+
142
+ // Method declaration: methodName() { or async methodName() {
143
+ const methodMatch = line.match(/^\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/)
144
+ if (methodMatch) {
145
+ return methodMatch[1].toLowerCase()
146
+ }
147
+
148
+ // Export function: export function funcName( or export const funcName = () =>
149
+ const exportFuncMatch = line.match(/export\s+(?:async\s+)?function\s+(\w+)\s*\(/)
150
+ if (exportFuncMatch) {
151
+ return exportFuncMatch[1].toLowerCase()
152
+ }
153
+
154
+ // Also handles TypeScript return type annotations: export const funcName = (): string =>
155
+ const exportConstMatch = line.match(/export\s+const\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)(?:\s*:\s*\w+)?\s*=>/)
156
+ if (exportConstMatch) {
157
+ return exportConstMatch[1].toLowerCase()
158
+ }
159
+ }
160
+
161
+ return null
162
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * General Helper Utilities
3
+ *
4
+ * Small utility functions used across the dangerous functions detection module.
5
+ */
6
+
7
+ /**
8
+ * Get a specific line from content by line number (0-indexed)
9
+ */
10
+ export function getLineContent(content: string, lineNumber: number): string {
11
+ const lines = content.split('\n')
12
+ return lines[lineNumber] || ''
13
+ }
14
+
15
+ /**
16
+ * Get a range of lines from content
17
+ */
18
+ export function getLineRange(
19
+ content: string,
20
+ startLine: number,
21
+ endLine: number
22
+ ): string {
23
+ const lines = content.split('\n')
24
+ const start = Math.max(0, startLine)
25
+ const end = Math.min(lines.length, endLine)
26
+ return lines.slice(start, end).join('\n')
27
+ }
28
+
29
+ /**
30
+ * Check if eval/exec/Function has only static literal inputs (no user data)
31
+ * Static inputs like eval('({ mode: "production" })') are low risk
32
+ *
33
+ * Returns true ONLY if the argument is a string literal (not a variable)
34
+ */
35
+ export function hasOnlyStaticInputs(
36
+ lineContent: string,
37
+ content: string,
38
+ lineNumber: number
39
+ ): boolean {
40
+ // Check if the argument to eval/exec/Function is a string literal ONLY
41
+ // If it's a variable, it's NOT static (could come from anywhere)
42
+ //
43
+ // String literal patterns:
44
+ // - Single quotes: 'content with "double quotes" inside' (no $ interpolation)
45
+ // - Double quotes: "content" (no $ interpolation)
46
+ // - Backticks without ${}: `content` (template literal but no interpolation)
47
+ //
48
+ // Note: We allow quotes INSIDE the string (e.g., 'text "with" quotes')
49
+ // but NOT $ (which would indicate interpolation)
50
+ const staticPatterns = [
51
+ // Single-quoted string: eval('...') - can contain anything except single quotes and $
52
+ /eval\s*\(\s*'[^'$]*'\s*\)/,
53
+ // Double-quoted string: eval("...") - can contain anything except double quotes and $
54
+ /eval\s*\(\s*"[^"$]*"\s*\)/,
55
+ // Backtick without interpolation: eval(`...`) - must not have ${ inside
56
+ /eval\s*\(\s*`[^`$]*`\s*\)/,
57
+ // Function constructor with string literal
58
+ /new\s+Function\s*\(\s*'[^'$]*'\s*\)/,
59
+ /new\s+Function\s*\(\s*"[^"$]*"\s*\)/,
60
+ // execSync with string literal
61
+ /execSync\s*\(\s*'[^'$]*'\s*\)/,
62
+ /execSync\s*\(\s*"[^"$]*"\s*\)/,
63
+ // exec with string literal
64
+ /exec\s*\(\s*'[^'$]*'/,
65
+ /exec\s*\(\s*"[^"$]*"/,
66
+ ]
67
+
68
+ // Only return true if it matches a static pattern (string literal)
69
+ // If it's a variable like eval(code), we can't assume it's static
70
+ return staticPatterns.some(p => p.test(lineContent))
71
+ }
72
+
73
+ /**
74
+ * Check if path traversal protection is in place
75
+ * Looks for common sanitization patterns that prevent directory traversal attacks
76
+ */
77
+ export function hasPathTraversalProtection(
78
+ context: string,
79
+ lineContent: string
80
+ ): boolean {
81
+ const protectionPatterns = [
82
+ // Path normalization with base directory check (same line)
83
+ /path\.resolve\s*\([^)]+\).*\.startsWith\s*\(/i,
84
+
85
+ // startsWith check with common safe directory variable names
86
+ /\.startsWith\s*\([^)]*(?:baseDir|basePath|rootDir|uploadDir|allowedDir|safeDir|SAFE_)/i,
87
+
88
+ // MULTI-LINE PATTERN: path.resolve followed by startsWith check in context
89
+ // This handles the common case where resolve and startsWith are on separate lines:
90
+ // const resolved = path.resolve(baseDir, userPath)
91
+ // if (!resolved.startsWith(baseDir)) throw new Error()
92
+ // We check for BOTH patterns being present in the context
93
+ // (handled below as combined check)
94
+
95
+ // Explicit ".." rejection
96
+ /\.includes\s*\(\s*['"`]\.\.['"`]\s*\)/i,
97
+ /\.indexOf\s*\(\s*['"`]\.\.['"`]\s*\)/i,
98
+ /['"`]\.\.['"`].*(?:throw|reject|return|error)/i,
99
+ // Replace ".." pattern (sanitization)
100
+ /\.replace\s*\([^)]*\\?\.\\?\.\s*[^)]*,\s*['"`]['"`]\s*\)/i,
101
+
102
+ // Path sanitization libraries
103
+ /sanitizePath|sanitizeFilename|sanitize-filename/i,
104
+ /path-sanitizer|secure-path/i,
105
+
106
+ // Explicit path validation
107
+ /validatePath|isValidPath|checkPath|verifyPath/i,
108
+ /isPathAllowed|isAllowedPath|pathIsAllowed/i,
109
+
110
+ // Normalize and check pattern
111
+ /path\.normalize\s*\([^)]+\).*(?:startsWith|includes|indexOf)/i,
112
+
113
+ // Regex validation for safe characters only
114
+ /\/\^?\[a-zA-Z0-9_\-\.\\\/\]\+\$?\//, // Only alphanumeric, dash, underscore, dot
115
+
116
+ // Allowlist/whitelist patterns
117
+ /allowedExtensions|allowedTypes|whitelist/i,
118
+ /\.endsWith\s*\(\s*['"`]\.\w+['"`]\s*\)/i, // Extension check
119
+
120
+ // Path.basename to strip directory
121
+ /path\.basename\s*\(/i,
122
+
123
+ // Zod/validation for filename patterns
124
+ /z\.string\s*\(\s*\)\.regex\s*\(/i,
125
+ ]
126
+
127
+ // Check single-line patterns
128
+ if (protectionPatterns.some(p => p.test(context) || p.test(lineContent))) {
129
+ return true
130
+ }
131
+
132
+ // Multi-line pattern: path.resolve + startsWith check on separate lines
133
+ // This is a very common secure pattern:
134
+ // const resolved = path.resolve(safeBaseDir, userInput)
135
+ // if (!resolved.startsWith(safeBaseDir)) { throw ... }
136
+ const hasPathResolve = /path\.resolve\s*\(/i.test(context)
137
+ const hasStartsWithCheck = /\.startsWith\s*\(/i.test(context)
138
+ const hasThrowOnFailure = /(throw|return|reject)\s+.*(error|invalid|denied)/i.test(context)
139
+
140
+ if (hasPathResolve && hasStartsWithCheck && hasThrowOnFailure) {
141
+ return true
142
+ }
143
+
144
+ return false
145
+ }
146
+
147
+ /**
148
+ * Check if route has throwing auth helper (getCurrentUserId, requireAuth, etc.)
149
+ * Routes with throwing auth helpers are already protected
150
+ */
151
+ export function hasThrowingAuthHelper(content: string): boolean {
152
+ const throwingAuthPatterns = [
153
+ /\bgetCurrentUserId\s*\(/i,
154
+ /\brequireAuth\s*\(/i,
155
+ /\bensureAuth\s*\(/i,
156
+ /\bauth\s*\(\s*\)\s*\.protect\s*\(/i, // Clerk: auth().protect()
157
+ /\bcurrentUser\s*\(\s*\)/i, // Clerk: currentUser()
158
+ /\bgetServerSession\s*\([^)]*\)/i, // NextAuth
159
+ /\bauth\s*\(\s*\)/i, // Generic auth() call
160
+ /\bcheckAuth\s*\(/i,
161
+ /\bverifyAuth\s*\(/i,
162
+ /\bvalidateAuth\s*\(/i,
163
+ /\bassertAuth\s*\(/i,
164
+ /\bgetAuth\s*\(/i,
165
+ /\brequireUser\s*\(/i,
166
+ /\bgetUser\s*\(\s*\)/i, // supabase.auth.getUser()
167
+ /const\s+\{\s*user\s*\}\s*=\s*await/i, // Destructuring pattern
168
+ ]
169
+ return throwingAuthPatterns.some(p => p.test(content))
170
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Utility Functions Index
3
+ *
4
+ * Re-exports all utility functions from the dangerous-functions module.
5
+ */
6
+
7
+ export {
8
+ isInsideTryCatch,
9
+ hasTryCatchNearby,
10
+ extractFunctionContext,
11
+ } from './control-flow'
12
+
13
+ export {
14
+ hasSchemaValidationNearby,
15
+ hasManualValidation,
16
+ hasSQLWhitelistValidation,
17
+ } from './schema-validation'
18
+
19
+ export {
20
+ getLineContent,
21
+ getLineRange,
22
+ hasOnlyStaticInputs,
23
+ hasPathTraversalProtection,
24
+ hasThrowingAuthHelper,
25
+ } from './helpers'