@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
@@ -9,7 +9,7 @@
9
9
  * - Context logging risks
10
10
  */
11
11
 
12
- import type { Vulnerability, VulnerabilitySeverity } from '../types'
12
+ import type { Vulnerability, VulnerabilitySeverity, VulnerabilityCategory } from '../types'
13
13
  import {
14
14
  isComment,
15
15
  isTestOrMockFile,
@@ -220,7 +220,7 @@ function getSurroundingContext(content: string, lineIndex: number, windowSize: n
220
220
  interface RAGSafetyPattern {
221
221
  name: string
222
222
  pattern: RegExp
223
- riskType: 'unscoped_retrieval' | 'context_exposure' | 'context_logging'
223
+ riskType: 'unscoped_retrieval' | 'context_exposure' | 'context_logging' | 'corpus_poisoning' | 'pii_leakage' | 'query_injection' | 'embedding_poisoning' | 'chunk_injection'
224
224
  baseSeverity: VulnerabilitySeverity
225
225
  description: string
226
226
  suggestedFix: string
@@ -382,10 +382,298 @@ const CONTEXT_LOGGING_PATTERNS: RAGSafetyPattern[] = [
382
382
  },
383
383
  ]
384
384
 
385
+ // ============================================================================
386
+ // AI Detection Roadmap Phase 1: Enhanced RAG Detection
387
+ // ============================================================================
388
+
389
+ /**
390
+ * Corpus Poisoning Patterns
391
+ * Detects user uploads directly embedded without sanitization
392
+ */
393
+ const CORPUS_POISONING_PATTERNS: RAGSafetyPattern[] = [
394
+ // User content embedded directly
395
+ {
396
+ name: 'User content embedded directly',
397
+ pattern: /(?:embeddings?\.create|createEmbedding|embed)\s*\([^)]*(?:document\.content|user\.content|req\.body|req\.json|upload|file\.content)/gi,
398
+ riskType: 'corpus_poisoning',
399
+ baseSeverity: 'high',
400
+ description: 'User-provided content embedded directly without sanitization. Malicious instructions in uploads could poison the RAG corpus.',
401
+ suggestedFix: 'Sanitize user content before embedding: const sanitized = sanitizeForRAG(content); await embed(sanitized)',
402
+ },
403
+ // External content fetched and embedded
404
+ {
405
+ name: 'External content embedded without validation',
406
+ pattern: /(?:fetch|axios\.get|httpx\.get)\s*\([^)]+\)[^;]*(?:embed|addDocument|upsert|index)/gi,
407
+ riskType: 'corpus_poisoning',
408
+ baseSeverity: 'high',
409
+ description: 'External content fetched and embedded without validation. External sources could contain prompt injection payloads.',
410
+ suggestedFix: 'Validate and sanitize external content before embedding. Check source trustworthiness.',
411
+ },
412
+ // PDF/file content indexed without scanning
413
+ {
414
+ name: 'File content indexed without sanitization',
415
+ pattern: /(?:pdfParser|parse|readFile)[^;]*(?:addToCorpus|embedDocument|vectorStore\.add|index\.upsert)/gi,
416
+ riskType: 'corpus_poisoning',
417
+ baseSeverity: 'medium',
418
+ description: 'File content indexed without sanitization. PDFs and documents may contain hidden injection instructions.',
419
+ suggestedFix: 'Scan file content for injection patterns before indexing. Consider content classification.',
420
+ },
421
+ // User messages embedded
422
+ {
423
+ name: 'User messages embedded to corpus',
424
+ pattern: /(?:messages?|msg|chat)[^;]*(?:embedDocument|addToCorpus|vectorStore\.add)/gi,
425
+ riskType: 'corpus_poisoning',
426
+ baseSeverity: 'medium',
427
+ description: 'User messages being embedded into corpus. Messages could contain crafted injection payloads.',
428
+ suggestedFix: 'Filter user messages for instruction-like patterns. Use separate namespace for user content.',
429
+ },
430
+ // Direct upsert without sanitization
431
+ {
432
+ name: 'Direct vector upsert with user data',
433
+ pattern: /\.upsert\s*\(\s*\[\s*\{[^}]*content\s*:\s*(?:document|user|upload|req)/gi,
434
+ riskType: 'corpus_poisoning',
435
+ baseSeverity: 'high',
436
+ description: 'User data upserted directly to vector store. Content should be sanitized first.',
437
+ suggestedFix: 'Sanitize content before upserting: { content: sanitize(document.content), ... }',
438
+ },
439
+ ]
440
+
441
+ /**
442
+ * PII Leakage Patterns
443
+ * Detects PII fields in embedded documents or retrieval responses
444
+ */
445
+ const PII_LEAKAGE_PATTERNS: RAGSafetyPattern[] = [
446
+ // PII fields in metadata
447
+ {
448
+ name: 'PII in document metadata',
449
+ pattern: /metadata\s*:\s*\{[^}]*(?:email|ssn|phone(?:Number)?|fullName|dateOfBirth|dob|address|socialSecurity)/gi,
450
+ riskType: 'pii_leakage',
451
+ baseSeverity: 'high',
452
+ description: 'PII fields stored in document metadata. This data will be exposed when documents are retrieved.',
453
+ suggestedFix: 'Remove PII from metadata. Store only non-sensitive identifiers: { userId: user.id, category: doc.type }',
454
+ },
455
+ // SSN/financial data in embedded docs
456
+ {
457
+ name: 'Sensitive financial/identity data embedded',
458
+ pattern: /(?:metadata|doc|document)\s*[:{][^}]*(?:ssn|socialSecurity|cardNumber|cvv|accountNum|insuranceId)/gi,
459
+ riskType: 'pii_leakage',
460
+ baseSeverity: 'critical',
461
+ description: 'Highly sensitive data (SSN, financial) in embedded documents. This is a compliance violation.',
462
+ suggestedFix: 'Never embed SSN, card numbers, or financial account data. Use tokenized references instead.',
463
+ },
464
+ // Patient/medical data in embeddings
465
+ {
466
+ name: 'PHI in embedded documents',
467
+ pattern: /(?:embed|metadata|doc)[^;]*(?:patientName|patientDob|patientSsn|medicalRecord|diagnosis)/gi,
468
+ riskType: 'pii_leakage',
469
+ baseSeverity: 'critical',
470
+ description: 'Protected Health Information (PHI) in embedded documents. HIPAA compliance violation.',
471
+ suggestedFix: 'Remove PHI before embedding. Use de-identification and tokenization for medical data.',
472
+ },
473
+ // Returning PII in search results
474
+ {
475
+ name: 'PII in retrieval response',
476
+ pattern: /return\s*(?:results\.map|docs\.map)[^}]*(?:email|phone|ssn|fullName|address)/gi,
477
+ riskType: 'pii_leakage',
478
+ baseSeverity: 'high',
479
+ description: 'PII fields returned in retrieval response. User PII may be exposed to unauthorized queries.',
480
+ suggestedFix: 'Filter PII from responses: return docs.map(d => ({ id: d.id, content: d.content })) // no PII',
481
+ },
482
+ // Direct metadata exposure with PII
483
+ {
484
+ name: 'Metadata with PII exposed in response',
485
+ pattern: /return\s*\{[^}]*metadata\.[^}]*(?:email|phone|ssn|name|address)/gi,
486
+ riskType: 'pii_leakage',
487
+ baseSeverity: 'high',
488
+ description: 'Document metadata containing PII exposed in response.',
489
+ suggestedFix: 'Filter metadata before returning. Only include non-sensitive fields.',
490
+ },
491
+ ]
492
+
493
+ // ============================================================================
494
+ // Phase 1 Enhancement Backlog: Advanced RAG Attack Detection
495
+ // ============================================================================
496
+
497
+ /**
498
+ * Query Injection Patterns
499
+ * Detects user queries used in retrieval without sanitization
500
+ */
501
+ const QUERY_INJECTION_PATTERNS: RAGSafetyPattern[] = [
502
+ // User input directly in vector store query
503
+ {
504
+ name: 'User input directly in retrieval query',
505
+ pattern: /(?:vectorStore|retriever|index|collection)\.(?:query|invoke|search|similaritySearch)\s*\(\s*(?:req\.|user\.|input\.|body\.|params\.)/gi,
506
+ riskType: 'query_injection',
507
+ baseSeverity: 'high',
508
+ description: 'User input flows directly to vector store query without sanitization. Could manipulate retrieval results.',
509
+ suggestedFix: 'Validate and sanitize user queries: const sanitizedQuery = sanitizeQuery(userInput)',
510
+ },
511
+ // Query from request body without validation
512
+ {
513
+ name: 'Query from request body without validation',
514
+ pattern: /(?:const|let|var)\s*\{\s*query\s*\}.*(?:req\.body|req\.json|request\.body)[\s\S]{0,100}(?:search|query|retrieve|similaritySearch)/gi,
515
+ riskType: 'query_injection',
516
+ baseSeverity: 'medium',
517
+ description: 'Query destructured from request body and used in retrieval. Validate before use.',
518
+ suggestedFix: 'Add input validation: const { query } = validateSchema(req.body, querySchema)',
519
+ },
520
+ // Query template with user input interpolation
521
+ {
522
+ name: 'Query template with user input',
523
+ pattern: /(?:prompt|query|searchQuery)\s*=\s*[`'"].*\$\{.*(?:user|input|query|req).*\}.*[`'"]/gi,
524
+ riskType: 'query_injection',
525
+ baseSeverity: 'medium',
526
+ description: 'Query template interpolates user input. Could inject adversarial retrieval instructions.',
527
+ suggestedFix: 'Use parameterized queries or sanitize user input before interpolation.',
528
+ },
529
+ // Direct query passthrough in API
530
+ {
531
+ name: 'Query passthrough to vector store',
532
+ pattern: /app\.(?:post|get)\s*\([^)]+(?:search|query|retrieve)[^)]*\)[^{]*\{[^}]*(?:vectorStore|retriever)\.(?:query|search)\s*\(\s*(?:req|ctx)\.(?:body|query)/gi,
533
+ riskType: 'query_injection',
534
+ baseSeverity: 'high',
535
+ description: 'API endpoint passes request directly to vector store. No validation layer.',
536
+ suggestedFix: 'Add validation middleware. Sanitize and validate queries before retrieval.',
537
+ },
538
+ // No query length validation
539
+ {
540
+ name: 'Query without length validation',
541
+ pattern: /(?:query|search|retrieve)\s*\(\s*(?:userQuery|searchQuery|q)\s*\)(?![\s\S]{0,50}(?:\.length|\.trim\(\)|maxLength|minLength))/gi,
542
+ riskType: 'query_injection',
543
+ baseSeverity: 'low',
544
+ description: 'Query used without visible length validation. Consider adding bounds.',
545
+ suggestedFix: 'Add query length validation: if (query.length > MAX_QUERY_LENGTH) throw new Error("Query too long")',
546
+ },
547
+ ]
548
+
549
+ /**
550
+ * Embedding Poisoning Patterns
551
+ * Detects adversarial document embedding vulnerabilities
552
+ */
553
+ const EMBEDDING_POISONING_PATTERNS: RAGSafetyPattern[] = [
554
+ // User document embedded without validation
555
+ {
556
+ name: 'User document embedded without validation',
557
+ pattern: /(?:embed|embeddings?\.(?:create|embed|generate)|createEmbedding)[\s\S]{0,50}(?:user|req\.|upload|file)[\s\S]{0,80}(?:vectorStore|index)\.(?:add|upsert|insert)/gis,
558
+ riskType: 'embedding_poisoning',
559
+ baseSeverity: 'high',
560
+ description: 'User-provided documents embedded directly. Adversarial content could poison retrieval.',
561
+ suggestedFix: 'Validate and sanitize user documents before embedding. Implement content classification.',
562
+ },
563
+ // Retrieval without similarity threshold
564
+ {
565
+ name: 'Retrieval without similarity threshold',
566
+ pattern: /similaritySearch\s*\(\s*[^,)]+\s*,\s*\d+\s*\)(?![\s\S]{0,50}(?:filter|threshold|score\s*>|minScore|scoreThreshold))/gi,
567
+ riskType: 'embedding_poisoning',
568
+ baseSeverity: 'medium',
569
+ description: 'Vector search without similarity threshold. Low-relevance adversarial content may be retrieved.',
570
+ suggestedFix: 'Add similarity threshold: similaritySearch(query, k, { scoreThreshold: 0.7 })',
571
+ },
572
+ // Batch embedding without deduplication
573
+ {
574
+ name: 'Batch embedding without duplicate detection',
575
+ pattern: /(?:for|forEach|map)\s*\([^)]+\)[\s\S]{0,100}(?:vectorStore|index)\.(?:add|upsert)(?![\s\S]{0,80}(?:exists|duplicate|similar|dedup))/gis,
576
+ riskType: 'embedding_poisoning',
577
+ baseSeverity: 'low',
578
+ description: 'Batch document embedding without duplicate detection. Attackers could flood corpus.',
579
+ suggestedFix: 'Check for duplicate or near-duplicate documents before embedding.',
580
+ },
581
+ // Dynamic embedding model selection
582
+ {
583
+ name: 'Dynamic embedding model from config',
584
+ pattern: /(?:embeddingModel|embeddings?)\s*=\s*(?:new\s+)?(?:config|options|params)\[?\s*['".]?(?:model|embedding)/gi,
585
+ riskType: 'embedding_poisoning',
586
+ baseSeverity: 'medium',
587
+ description: 'Embedding model selected from configuration. Malicious config could use compromised model.',
588
+ suggestedFix: 'Use hardcoded embedding model or validate against allowlist.',
589
+ },
590
+ // External URL content embedded
591
+ {
592
+ name: 'External URL content embedded directly',
593
+ pattern: /(?:fetch|axios\.get|httpx\.get)\s*\([^)]+\)[\s\S]{0,150}(?:embed|vectorStore\.add|index\.upsert)/gis,
594
+ riskType: 'embedding_poisoning',
595
+ baseSeverity: 'high',
596
+ description: 'Content from external URLs embedded without validation. Source could be compromised.',
597
+ suggestedFix: 'Validate URL source against allowlist. Sanitize fetched content before embedding.',
598
+ },
599
+ ]
600
+
601
+ /**
602
+ * Chunk Boundary Exploitation Patterns
603
+ * Detects cross-chunk injection vulnerabilities
604
+ */
605
+ const CHUNK_INJECTION_PATTERNS: RAGSafetyPattern[] = [
606
+ // User content chunked without per-chunk validation
607
+ {
608
+ name: 'User content chunked without validation',
609
+ pattern: /(?:splitter|textSplitter|chunker)\.(?:split|createDocuments|chunk)[\s\S]{0,50}(?:user|upload|req)[\s\S]{0,100}(?:vectorStore|index)\.(?:add|upsert)(?![\s\S]{0,50}(?:sanitize|validate|filter))/gis,
610
+ riskType: 'chunk_injection',
611
+ baseSeverity: 'medium',
612
+ description: 'User content split and embedded without per-chunk validation. Injection could span chunks.',
613
+ suggestedFix: 'Validate each chunk before embedding: chunks.map(c => sanitizeChunk(c))',
614
+ },
615
+ // Context joined without separators
616
+ {
617
+ name: 'Context chunks joined without separators',
618
+ pattern: /\.map\s*\([^)]*(?:pageContent|content|text)[^)]*\)\.join\s*\(\s*['"]['"]\s*\)/gi,
619
+ riskType: 'chunk_injection',
620
+ baseSeverity: 'low',
621
+ description: 'Retrieved chunks joined without separators. Adjacent chunk content could be misinterpreted.',
622
+ suggestedFix: 'Use clear separators: chunks.map(c => c.content).join("\\n---\\n")',
623
+ },
624
+ // Chunk metadata from user input
625
+ {
626
+ name: 'Chunk metadata from user input',
627
+ pattern: /(?:vectorStore|index)\.(?:add|upsert)[\s\S]{0,100}metadata\s*:\s*(?:user|req\.|input\.|body\.)/gi,
628
+ riskType: 'chunk_injection',
629
+ baseSeverity: 'medium',
630
+ description: 'Chunk metadata derived from user input. Could inject malicious metadata for filtering.',
631
+ suggestedFix: 'Generate metadata server-side. Validate any user-provided metadata fields.',
632
+ },
633
+ // No chunk size limits
634
+ {
635
+ name: 'Chunking without size validation',
636
+ pattern: /(?:splitter|textSplitter)\.(?:split|createDocuments)\s*\(\s*(?:content|text|document)(?![\s\S]{0,50}(?:maxChunkSize|chunkSize|maxLength))/gi,
637
+ riskType: 'chunk_injection',
638
+ baseSeverity: 'low',
639
+ description: 'Text splitting without explicit size limits. Very long inputs could cause issues.',
640
+ suggestedFix: 'Configure chunk size limits: new TextSplitter({ chunkSize: 1000, chunkOverlap: 200 })',
641
+ },
642
+ // Overlapping chunks with user content
643
+ {
644
+ name: 'Large chunk overlap with user content',
645
+ pattern: /(?:chunkOverlap|overlap)\s*[:=]\s*(?:\d{3,}|[a-zA-Z])[\s\S]{0,100}(?:user|upload)/gi,
646
+ riskType: 'chunk_injection',
647
+ baseSeverity: 'low',
648
+ description: 'Large chunk overlap configured. User-injected content could appear in multiple chunks.',
649
+ suggestedFix: 'Use reasonable overlap (10-20% of chunk size). Validate user content before chunking.',
650
+ },
651
+ ]
652
+
385
653
  // ============================================================================
386
654
  // Main Detection Function
387
655
  // ============================================================================
388
656
 
657
+ /**
658
+ * Map risk type to vulnerability category
659
+ */
660
+ function mapRiskTypeToCategory(riskType: RAGSafetyPattern['riskType']): VulnerabilityCategory {
661
+ switch (riskType) {
662
+ case 'corpus_poisoning':
663
+ return 'ai_rag_corpus_poisoning'
664
+ case 'pii_leakage':
665
+ return 'ai_rag_pii_leakage'
666
+ case 'query_injection':
667
+ return 'ai_rag_query_injection'
668
+ case 'embedding_poisoning':
669
+ return 'ai_rag_embedding_poisoning'
670
+ case 'chunk_injection':
671
+ return 'ai_rag_chunk_injection'
672
+ default:
673
+ return 'ai_rag_exfiltration'
674
+ }
675
+ }
676
+
389
677
  /**
390
678
  * Main detection function for RAG data safety issues
391
679
  */
@@ -415,6 +703,13 @@ export function detectRAGSafetyIssues(
415
703
  ...UNSCOPED_RETRIEVAL_PATTERNS,
416
704
  ...CONTEXT_EXPOSURE_PATTERNS,
417
705
  ...CONTEXT_LOGGING_PATTERNS,
706
+ // AI Detection Roadmap Phase 1
707
+ ...CORPUS_POISONING_PATTERNS,
708
+ ...PII_LEAKAGE_PATTERNS,
709
+ // Phase 1 Enhancement Backlog
710
+ ...QUERY_INJECTION_PATTERNS,
711
+ ...EMBEDDING_POISONING_PATTERNS,
712
+ ...CHUNK_INJECTION_PATTERNS,
418
713
  ]
419
714
 
420
715
  for (const pattern of allPatterns) {
@@ -464,6 +759,90 @@ export function detectRAGSafetyIssues(
464
759
  }
465
760
  }
466
761
 
762
+ // Corpus poisoning - check for sanitization
763
+ if (pattern.riskType === 'corpus_poisoning') {
764
+ // Check for content sanitization in context
765
+ if (/sanitize|validate|filter|clean|strip/i.test(context)) {
766
+ severity = 'info'
767
+ notes.push('Content sanitization detected nearby')
768
+ }
769
+ // Check for content classification/scanning
770
+ if (/classify|scan|detect|check.*injection/i.test(context)) {
771
+ severity = 'info'
772
+ notes.push('Content scanning detected')
773
+ }
774
+ }
775
+
776
+ // PII leakage - critical data types remain high severity
777
+ if (pattern.riskType === 'pii_leakage') {
778
+ // Check for PII redaction/masking
779
+ if (/redact|mask|anonymize|deidentify|tokenize/i.test(context)) {
780
+ severity = 'info'
781
+ notes.push('PII redaction detected')
782
+ }
783
+ // SSN, CVV, and PHI patterns remain critical regardless of context
784
+ if (/ssn|cvv|patient/i.test(pattern.name.toLowerCase())) {
785
+ // Keep severity high/critical for these
786
+ }
787
+ }
788
+
789
+ // Query injection - check for input validation
790
+ if (pattern.riskType === 'query_injection') {
791
+ // Check for input validation/sanitization
792
+ if (/sanitize|validate|clean|escape|zod|schema\.parse|safeParse/i.test(context)) {
793
+ severity = 'low'
794
+ notes.push('Input validation detected nearby')
795
+ }
796
+ // Check for query length/bounds validation
797
+ if (/maxLength|minLength|\.length\s*[<>]|slice\s*\(\s*0/i.test(context)) {
798
+ if (severity === 'high') severity = 'medium'
799
+ notes.push('Length validation detected')
800
+ }
801
+ // Check for rate limiting
802
+ if (/rateLimit|throttle|limiter/i.test(context)) {
803
+ if (severity === 'high') severity = 'medium'
804
+ notes.push('Rate limiting detected')
805
+ }
806
+ }
807
+
808
+ // Embedding poisoning - check for content validation
809
+ if (pattern.riskType === 'embedding_poisoning') {
810
+ // Check for content sanitization
811
+ if (/sanitize|validate|filter|clean|strip|scan/i.test(context)) {
812
+ severity = 'low'
813
+ notes.push('Content validation detected nearby')
814
+ }
815
+ // Check for content classification
816
+ if (/classify|moderation|detect.*injection|contentFilter/i.test(context)) {
817
+ severity = 'info'
818
+ notes.push('Content classification detected')
819
+ }
820
+ // Check for similarity threshold
821
+ if (/threshold|scoreThreshold|minScore|score\s*>/i.test(context)) {
822
+ if (severity === 'medium') severity = 'low'
823
+ notes.push('Similarity threshold configured')
824
+ }
825
+ }
826
+
827
+ // Chunk injection - check for chunk validation
828
+ if (pattern.riskType === 'chunk_injection') {
829
+ // Check for per-chunk validation
830
+ if (/chunks?\.map\s*\([^)]*sanitize|validate.*chunk|chunk.*validate/i.test(context)) {
831
+ severity = 'info'
832
+ notes.push('Chunk validation detected')
833
+ }
834
+ // Check for separator usage
835
+ if (/separator|delimiter|join\s*\(\s*['"][^'"]{2,}['"]\s*\)/i.test(context)) {
836
+ if (severity === 'low') severity = 'info'
837
+ notes.push('Chunk separators detected')
838
+ }
839
+ // Check for metadata sanitization
840
+ if (/metadata\s*[:=]\s*\{[^}]*(?:id|type|source)[^}]*\}/i.test(context)) {
841
+ if (severity === 'medium') severity = 'low'
842
+ notes.push('Server-generated metadata pattern')
843
+ }
844
+ }
845
+
467
846
  // Downgrade test files
468
847
  if (isTestFile) {
469
848
  severity = 'info'
@@ -493,7 +872,7 @@ export function detectRAGSafetyIssues(
493
872
  lineNumber,
494
873
  lineContent,
495
874
  severity,
496
- category: 'ai_rag_exfiltration',
875
+ category: mapRiskTypeToCategory(pattern.riskType),
497
876
  title: pattern.name,
498
877
  description,
499
878
  suggestedFix: pattern.suggestedFix,
@@ -14,6 +14,7 @@ import { isRouteProtectedByMiddleware, getRoutePathFromFile } from '../utils/mid
14
14
  import type { AuthHelper, AuthHelperContext } from '../utils/auth-helper-detector'
15
15
  import { hasAuthHelperCallBefore, isUserIdAlreadyValidated } from '../utils/auth-helper-detector'
16
16
  import type { FileAuthImports } from '../utils/imported-auth-detector'
17
+ import { isScannerOrFixtureFile } from '../utils/context-helpers'
17
18
 
18
19
  interface AuthAntiPattern {
19
20
  name: string
@@ -263,6 +264,10 @@ export function detectAuthAntipatterns(
263
264
  ): Vulnerability[] {
264
265
  const { middlewareConfig, authHelpers, fileAuthImports } = options
265
266
  const vulnerabilities: Vulnerability[] = []
267
+
268
+ // Skip scanner/fixture files to avoid self-detection
269
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
270
+
266
271
  const lines = content.split('\n')
267
272
  const isAuthFile = isAuthRelatedFile(filePath)
268
273
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  import type { Vulnerability, VulnerabilitySeverity } from '../types'
8
8
  import type { MiddlewareAuthConfig } from '../utils/middleware-detector'
9
- import { isComment, isTestOrMockFile, isExampleFile, isPlaceholderValue } from '../utils/context-helpers'
9
+ import { isComment, isTestOrMockFile, isExampleFile, isPlaceholderValue, isScannerOrFixtureFile } from '../utils/context-helpers'
10
10
  import { isRouteProtectedByMiddleware, getRoutePathFromFile, detectUserScopingPatterns } from '../utils/middleware-detector'
11
11
 
12
12
  /**
@@ -224,6 +224,10 @@ export function detectBYOKPatterns(
224
224
  middlewareConfig?: MiddlewareAuthConfig
225
225
  ): Vulnerability[] {
226
226
  const vulnerabilities: Vulnerability[] = []
227
+
228
+ // Skip scanner/fixture files to avoid self-detection
229
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
230
+
227
231
  const lines = content.split('\n')
228
232
  const isTestFile = isTestOrMockFile(filePath)
229
233
 
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Child Process Detection
3
+ *
4
+ * Detection logic for child_process functions (exec, spawn, execFile, etc.)
5
+ * that can lead to command injection vulnerabilities.
6
+ */
7
+
8
+ /**
9
+ * Check if exec() call is from child_process (dangerous) vs RegExp.exec (safe)
10
+ * Returns true if this is a child_process exec call that should be flagged
11
+ */
12
+ export function isChildProcessExec(content: string, lineContent: string): boolean {
13
+ // Check for child_process import
14
+ const hasChildProcessImport =
15
+ /require\s*\(\s*['"]child_process['"]\s*\)/.test(content) ||
16
+ /from\s+['"]child_process['"]/.test(content) ||
17
+ /import\s+.*child_process/.test(content) ||
18
+ /require\s*\(\s*['"]node:child_process['"]\s*\)/.test(content) ||
19
+ /from\s+['"]node:child_process['"]/.test(content)
20
+
21
+ // If no child_process import, this is likely RegExp.exec or similar
22
+ if (!hasChildProcessImport) {
23
+ return false
24
+ }
25
+
26
+ // Check if this specific line is RegExp.exec pattern
27
+ // RegExp.exec is called as: regex.exec(string) or /pattern/.exec(string)
28
+ const isRegExpExec =
29
+ /\.\s*exec\s*\(/.test(lineContent) && // Method call on an object
30
+ !/\bexec\s*\(/.test(lineContent.replace(/\.\s*exec\s*\(/, '')) // Not a standalone exec()
31
+
32
+ // Also check for common RegExp patterns
33
+ const isRegExpPattern =
34
+ /\/[^/]+\/[gimsuy]*\.exec\s*\(/.test(lineContent) || // /pattern/.exec()
35
+ /new\s+RegExp\s*\([^)]+\)\.exec\s*\(/.test(lineContent) || // new RegExp().exec()
36
+ /regex\.exec\s*\(/i.test(lineContent) || // regex.exec()
37
+ /pattern\.exec\s*\(/i.test(lineContent) || // pattern.exec()
38
+ /match\.exec\s*\(/i.test(lineContent) || // match.exec()
39
+ /re\.exec\s*\(/i.test(lineContent) // re.exec()
40
+
41
+ if (isRegExpExec || isRegExpPattern) {
42
+ return false
43
+ }
44
+
45
+ // Check if exec is imported/destructured from child_process
46
+ const execImported =
47
+ /\{\s*[^}]*\bexec\b[^}]*\}\s*=\s*require\s*\(\s*['"]child_process['"]/.test(
48
+ content
49
+ ) ||
50
+ /\{\s*[^}]*\bexec\b[^}]*\}\s*=\s*require\s*\(\s*['"]node:child_process['"]/.test(
51
+ content
52
+ ) ||
53
+ /import\s+\{\s*[^}]*\bexec\b[^}]*\}\s+from\s+['"]child_process['"]/.test(
54
+ content
55
+ ) ||
56
+ /import\s+\{\s*[^}]*\bexec\b[^}]*\}\s+from\s+['"]node:child_process['"]/.test(
57
+ content
58
+ )
59
+
60
+ // If exec is directly imported from child_process, standalone exec() is dangerous
61
+ if (execImported && /\bexec\s*\(/.test(lineContent)) {
62
+ return true
63
+ }
64
+
65
+ // Check for child_process.exec() pattern
66
+ if (
67
+ /child_process\.exec\s*\(/.test(lineContent) ||
68
+ /cp\.exec\s*\(/.test(lineContent) ||
69
+ /childProcess\.exec\s*\(/.test(lineContent)
70
+ ) {
71
+ return true
72
+ }
73
+
74
+ // If we have child_process import but can't determine usage, be conservative
75
+ // Only flag if it looks like a standalone exec() call
76
+ return /\bexec\s*\(/.test(lineContent) && !/\.\s*exec\s*\(/.test(lineContent)
77
+ }
78
+
79
+ /**
80
+ * Check if spawn/execFile/execSync is from child_process
81
+ */
82
+ export function isChildProcessSpawn(content: string, lineContent: string): boolean {
83
+ // Check for child_process import
84
+ const hasChildProcessImport =
85
+ /require\s*\(\s*['"]child_process['"]\s*\)/.test(content) ||
86
+ /from\s+['"]child_process['"]/.test(content) ||
87
+ /require\s*\(\s*['"]node:child_process['"]\s*\)/.test(content) ||
88
+ /from\s+['"]node:child_process['"]/.test(content)
89
+
90
+ if (!hasChildProcessImport) {
91
+ return false
92
+ }
93
+
94
+ // These functions are always from child_process when that module is imported
95
+ return /\b(spawn|spawnSync|execSync|execFile|execFileSync)\s*\(/.test(
96
+ lineContent
97
+ )
98
+ }