@oculum/scanner 1.0.10 → 1.0.12

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 (520) hide show
  1. package/dist/ai-context/index.d.ts +6 -0
  2. package/dist/ai-context/index.d.ts.map +1 -0
  3. package/dist/ai-context/index.js +13 -0
  4. package/dist/ai-context/index.js.map +1 -0
  5. package/dist/ai-context/manager.d.ts +67 -0
  6. package/dist/ai-context/manager.d.ts.map +1 -0
  7. package/dist/ai-context/manager.js +104 -0
  8. package/dist/ai-context/manager.js.map +1 -0
  9. package/dist/baseline/diff.d.ts +32 -0
  10. package/dist/baseline/diff.d.ts.map +1 -0
  11. package/dist/baseline/diff.js +119 -0
  12. package/dist/baseline/diff.js.map +1 -0
  13. package/dist/baseline/index.d.ts +9 -0
  14. package/dist/baseline/index.d.ts.map +1 -0
  15. package/dist/baseline/index.js +19 -0
  16. package/dist/baseline/index.js.map +1 -0
  17. package/dist/baseline/manager.d.ts +67 -0
  18. package/dist/baseline/manager.d.ts.map +1 -0
  19. package/dist/baseline/manager.js +180 -0
  20. package/dist/baseline/manager.js.map +1 -0
  21. package/dist/baseline/types.d.ts +91 -0
  22. package/dist/baseline/types.d.ts.map +1 -0
  23. package/dist/baseline/types.js +12 -0
  24. package/dist/baseline/types.js.map +1 -0
  25. package/dist/category-filter.d.ts +125 -0
  26. package/dist/category-filter.d.ts.map +1 -0
  27. package/dist/category-filter.js +360 -0
  28. package/dist/category-filter.js.map +1 -0
  29. package/dist/filtering/context-adjustments.d.ts +23 -0
  30. package/dist/filtering/context-adjustments.d.ts.map +1 -0
  31. package/dist/filtering/context-adjustments.js +100 -0
  32. package/dist/filtering/context-adjustments.js.map +1 -0
  33. package/dist/filtering/index.d.ts +3 -0
  34. package/dist/filtering/index.d.ts.map +1 -0
  35. package/dist/filtering/index.js +8 -0
  36. package/dist/filtering/index.js.map +1 -0
  37. package/dist/filtering/pipeline.d.ts +48 -0
  38. package/dist/filtering/pipeline.d.ts.map +1 -0
  39. package/dist/filtering/pipeline.js +76 -0
  40. package/dist/filtering/pipeline.js.map +1 -0
  41. package/dist/formatters/ai-context.d.ts +23 -0
  42. package/dist/formatters/ai-context.d.ts.map +1 -0
  43. package/dist/formatters/ai-context.js +238 -0
  44. package/dist/formatters/ai-context.js.map +1 -0
  45. package/dist/formatters/cli-terminal.d.ts +38 -0
  46. package/dist/formatters/cli-terminal.d.ts.map +1 -1
  47. package/dist/formatters/cli-terminal.js +365 -42
  48. package/dist/formatters/cli-terminal.js.map +1 -1
  49. package/dist/formatters/github-comment.d.ts +2 -2
  50. package/dist/formatters/github-comment.d.ts.map +1 -1
  51. package/dist/formatters/github-comment.js +77 -13
  52. package/dist/formatters/github-comment.js.map +1 -1
  53. package/dist/formatters/ide/claude-code.d.ts +17 -0
  54. package/dist/formatters/ide/claude-code.d.ts.map +1 -0
  55. package/dist/formatters/ide/claude-code.js +94 -0
  56. package/dist/formatters/ide/claude-code.js.map +1 -0
  57. package/dist/formatters/ide/cursor.d.ts +13 -0
  58. package/dist/formatters/ide/cursor.d.ts.map +1 -0
  59. package/dist/formatters/ide/cursor.js +125 -0
  60. package/dist/formatters/ide/cursor.js.map +1 -0
  61. package/dist/formatters/ide/index.d.ts +62 -0
  62. package/dist/formatters/ide/index.d.ts.map +1 -0
  63. package/dist/formatters/ide/index.js +184 -0
  64. package/dist/formatters/ide/index.js.map +1 -0
  65. package/dist/formatters/ide/windsurf.d.ts +13 -0
  66. package/dist/formatters/ide/windsurf.d.ts.map +1 -0
  67. package/dist/formatters/ide/windsurf.js +117 -0
  68. package/dist/formatters/ide/windsurf.js.map +1 -0
  69. package/dist/formatters/index.d.ts +3 -1
  70. package/dist/formatters/index.d.ts.map +1 -1
  71. package/dist/formatters/index.js +20 -1
  72. package/dist/formatters/index.js.map +1 -1
  73. package/dist/index.d.ts +11 -0
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +423 -56
  76. package/dist/index.js.map +1 -1
  77. package/dist/layer1/comments.d.ts +4 -1
  78. package/dist/layer1/comments.d.ts.map +1 -1
  79. package/dist/layer1/comments.js +1 -1
  80. package/dist/layer1/comments.js.map +1 -1
  81. package/dist/layer1/config-audit.d.ts +4 -1
  82. package/dist/layer1/config-audit.d.ts.map +1 -1
  83. package/dist/layer1/config-audit.js +65 -14
  84. package/dist/layer1/config-audit.js.map +1 -1
  85. package/dist/layer1/config-mcp-audit.d.ts +23 -0
  86. package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
  87. package/dist/layer1/config-mcp-audit.js +239 -0
  88. package/dist/layer1/config-mcp-audit.js.map +1 -0
  89. package/dist/layer1/entropy.d.ts +4 -1
  90. package/dist/layer1/entropy.d.ts.map +1 -1
  91. package/dist/layer1/entropy.js +212 -1
  92. package/dist/layer1/entropy.js.map +1 -1
  93. package/dist/layer1/file-flags.d.ts +4 -1
  94. package/dist/layer1/file-flags.d.ts.map +1 -1
  95. package/dist/layer1/file-flags.js +12 -5
  96. package/dist/layer1/file-flags.js.map +1 -1
  97. package/dist/layer1/index.d.ts +1 -0
  98. package/dist/layer1/index.d.ts.map +1 -1
  99. package/dist/layer1/index.js +22 -19
  100. package/dist/layer1/index.js.map +1 -1
  101. package/dist/layer1/patterns.d.ts +4 -1
  102. package/dist/layer1/patterns.d.ts.map +1 -1
  103. package/dist/layer1/patterns.js +34 -4
  104. package/dist/layer1/patterns.js.map +1 -1
  105. package/dist/layer1/urls.d.ts +4 -1
  106. package/dist/layer1/urls.d.ts.map +1 -1
  107. package/dist/layer1/urls.js +162 -14
  108. package/dist/layer1/urls.js.map +1 -1
  109. package/dist/layer1/weak-crypto.d.ts +4 -1
  110. package/dist/layer1/weak-crypto.d.ts.map +1 -1
  111. package/dist/layer1/weak-crypto.js +144 -7
  112. package/dist/layer1/weak-crypto.js.map +1 -1
  113. package/dist/layer2/ai-agent-tools.d.ts +4 -1
  114. package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
  115. package/dist/layer2/ai-agent-tools.js +964 -2
  116. package/dist/layer2/ai-agent-tools.js.map +1 -1
  117. package/dist/layer2/ai-endpoint-protection.d.ts +2 -0
  118. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
  119. package/dist/layer2/ai-endpoint-protection.js +18 -4
  120. package/dist/layer2/ai-endpoint-protection.js.map +1 -1
  121. package/dist/layer2/ai-execution-sinks.d.ts +4 -1
  122. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
  123. package/dist/layer2/ai-execution-sinks.js +688 -29
  124. package/dist/layer2/ai-execution-sinks.js.map +1 -1
  125. package/dist/layer2/ai-fingerprinting.d.ts +4 -1
  126. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
  127. package/dist/layer2/ai-fingerprinting.js +28 -32
  128. package/dist/layer2/ai-fingerprinting.js.map +1 -1
  129. package/dist/layer2/ai-mcp-security.d.ts +20 -0
  130. package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
  131. package/dist/layer2/ai-mcp-security.js +877 -0
  132. package/dist/layer2/ai-mcp-security.js.map +1 -0
  133. package/dist/layer2/ai-package-hallucination.d.ts +22 -0
  134. package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
  135. package/dist/layer2/ai-package-hallucination.js +828 -0
  136. package/dist/layer2/ai-package-hallucination.js.map +1 -0
  137. package/dist/layer2/ai-prompt-hygiene.d.ts +4 -1
  138. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
  139. package/dist/layer2/ai-prompt-hygiene.js +817 -17
  140. package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
  141. package/dist/layer2/ai-rag-safety.d.ts +4 -1
  142. package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
  143. package/dist/layer2/ai-rag-safety.js +454 -3
  144. package/dist/layer2/ai-rag-safety.js.map +1 -1
  145. package/dist/layer2/ai-schema-validation.d.ts +4 -1
  146. package/dist/layer2/ai-schema-validation.d.ts.map +1 -1
  147. package/dist/layer2/ai-schema-validation.js +2 -2
  148. package/dist/layer2/ai-schema-validation.js.map +1 -1
  149. package/dist/layer2/auth-antipatterns.d.ts +2 -0
  150. package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
  151. package/dist/layer2/auth-antipatterns.js +209 -20
  152. package/dist/layer2/auth-antipatterns.js.map +1 -1
  153. package/dist/layer2/byok-patterns.d.ts +4 -1
  154. package/dist/layer2/byok-patterns.d.ts.map +1 -1
  155. package/dist/layer2/byok-patterns.js +5 -2
  156. package/dist/layer2/byok-patterns.js.map +1 -1
  157. package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
  158. package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
  159. package/dist/layer2/dangerous-functions/child-process.js +74 -0
  160. package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
  161. package/dist/layer2/dangerous-functions/dom-xss.d.ts +34 -0
  162. package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
  163. package/dist/layer2/dangerous-functions/dom-xss.js +230 -0
  164. package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
  165. package/dist/layer2/dangerous-functions/index.d.ts +16 -0
  166. package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
  167. package/dist/layer2/dangerous-functions/index.js +1152 -0
  168. package/dist/layer2/dangerous-functions/index.js.map +1 -0
  169. package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
  170. package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
  171. package/dist/layer2/dangerous-functions/json-parse.js +319 -0
  172. package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
  173. package/dist/layer2/dangerous-functions/math-random.d.ts +111 -0
  174. package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
  175. package/dist/layer2/dangerous-functions/math-random.js +684 -0
  176. package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
  177. package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
  178. package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
  179. package/dist/layer2/dangerous-functions/patterns.js +163 -0
  180. package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
  181. package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
  182. package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
  183. package/dist/layer2/dangerous-functions/request-validation.js +119 -0
  184. package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
  185. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +24 -0
  186. package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
  187. package/dist/layer2/dangerous-functions/utils/control-flow.js +70 -0
  188. package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
  189. package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
  190. package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
  191. package/dist/layer2/dangerous-functions/utils/helpers.js +147 -0
  192. package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
  193. package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
  194. package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
  195. package/dist/layer2/dangerous-functions/utils/index.js +23 -0
  196. package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
  197. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
  198. package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
  199. package/dist/layer2/dangerous-functions/utils/schema-validation.js +102 -0
  200. package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
  201. package/dist/layer2/data-exposure.d.ts +4 -1
  202. package/dist/layer2/data-exposure.d.ts.map +1 -1
  203. package/dist/layer2/data-exposure.js +14 -38
  204. package/dist/layer2/data-exposure.js.map +1 -1
  205. package/dist/layer2/framework-checks.d.ts +4 -1
  206. package/dist/layer2/framework-checks.d.ts.map +1 -1
  207. package/dist/layer2/framework-checks.js +5 -2
  208. package/dist/layer2/framework-checks.js.map +1 -1
  209. package/dist/layer2/index.d.ts +12 -1
  210. package/dist/layer2/index.d.ts.map +1 -1
  211. package/dist/layer2/index.js +110 -45
  212. package/dist/layer2/index.js.map +1 -1
  213. package/dist/layer2/logic-gates.d.ts +4 -1
  214. package/dist/layer2/logic-gates.d.ts.map +1 -1
  215. package/dist/layer2/logic-gates.js +58 -20
  216. package/dist/layer2/logic-gates.js.map +1 -1
  217. package/dist/layer2/model-supply-chain.d.ts +23 -0
  218. package/dist/layer2/model-supply-chain.d.ts.map +1 -0
  219. package/dist/layer2/model-supply-chain.js +444 -0
  220. package/dist/layer2/model-supply-chain.js.map +1 -0
  221. package/dist/layer2/risky-imports.d.ts +4 -1
  222. package/dist/layer2/risky-imports.d.ts.map +1 -1
  223. package/dist/layer2/risky-imports.js +6 -2
  224. package/dist/layer2/risky-imports.js.map +1 -1
  225. package/dist/layer2/variables.d.ts +4 -1
  226. package/dist/layer2/variables.d.ts.map +1 -1
  227. package/dist/layer2/variables.js +6 -2
  228. package/dist/layer2/variables.js.map +1 -1
  229. package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
  230. package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
  231. package/dist/layer3/anthropic/auto-dismiss.js +199 -0
  232. package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
  233. package/dist/layer3/anthropic/clients.d.ts +44 -0
  234. package/dist/layer3/anthropic/clients.d.ts.map +1 -0
  235. package/dist/layer3/anthropic/clients.js +81 -0
  236. package/dist/layer3/anthropic/clients.js.map +1 -0
  237. package/dist/layer3/anthropic/index.d.ts +41 -0
  238. package/dist/layer3/anthropic/index.d.ts.map +1 -0
  239. package/dist/layer3/anthropic/index.js +141 -0
  240. package/dist/layer3/anthropic/index.js.map +1 -0
  241. package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
  242. package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
  243. package/dist/layer3/anthropic/prompts/index.js +14 -0
  244. package/dist/layer3/anthropic/prompts/index.js.map +1 -0
  245. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
  246. package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
  247. package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
  248. package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
  249. package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
  250. package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
  251. package/dist/layer3/anthropic/prompts/validation.js +421 -0
  252. package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
  253. package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
  254. package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
  255. package/dist/layer3/anthropic/providers/anthropic.js +266 -0
  256. package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
  257. package/dist/layer3/anthropic/providers/index.d.ts +8 -0
  258. package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
  259. package/dist/layer3/anthropic/providers/index.js +15 -0
  260. package/dist/layer3/anthropic/providers/index.js.map +1 -0
  261. package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
  262. package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
  263. package/dist/layer3/anthropic/providers/openai.js +340 -0
  264. package/dist/layer3/anthropic/providers/openai.js.map +1 -0
  265. package/dist/layer3/anthropic/request-builder.d.ts +20 -0
  266. package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
  267. package/dist/layer3/anthropic/request-builder.js +134 -0
  268. package/dist/layer3/anthropic/request-builder.js.map +1 -0
  269. package/dist/layer3/anthropic/types.d.ts +88 -0
  270. package/dist/layer3/anthropic/types.d.ts.map +1 -0
  271. package/dist/layer3/anthropic/types.js +38 -0
  272. package/dist/layer3/anthropic/types.js.map +1 -0
  273. package/dist/layer3/anthropic/utils/index.d.ts +9 -0
  274. package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
  275. package/dist/layer3/anthropic/utils/index.js +24 -0
  276. package/dist/layer3/anthropic/utils/index.js.map +1 -0
  277. package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
  278. package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
  279. package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
  280. package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
  281. package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
  282. package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
  283. package/dist/layer3/anthropic/utils/response-parser.js +285 -0
  284. package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
  285. package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
  286. package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
  287. package/dist/layer3/anthropic/utils/retry.js +62 -0
  288. package/dist/layer3/anthropic/utils/retry.js.map +1 -0
  289. package/dist/layer3/index.d.ts +1 -0
  290. package/dist/layer3/index.d.ts.map +1 -1
  291. package/dist/layer3/index.js +16 -6
  292. package/dist/layer3/index.js.map +1 -1
  293. package/dist/layer3/osv-check.d.ts +75 -0
  294. package/dist/layer3/osv-check.d.ts.map +1 -0
  295. package/dist/layer3/osv-check.js +308 -0
  296. package/dist/layer3/osv-check.js.map +1 -0
  297. package/dist/modes/incremental.js +1 -1
  298. package/dist/rules/framework-fixes.d.ts +48 -0
  299. package/dist/rules/framework-fixes.d.ts.map +1 -0
  300. package/dist/rules/framework-fixes.js +439 -0
  301. package/dist/rules/framework-fixes.js.map +1 -0
  302. package/dist/rules/index.d.ts +8 -0
  303. package/dist/rules/index.d.ts.map +1 -0
  304. package/dist/rules/index.js +18 -0
  305. package/dist/rules/index.js.map +1 -0
  306. package/dist/rules/metadata.d.ts +43 -0
  307. package/dist/rules/metadata.d.ts.map +1 -0
  308. package/dist/rules/metadata.js +734 -0
  309. package/dist/rules/metadata.js.map +1 -0
  310. package/dist/suppression/config-loader.d.ts +74 -0
  311. package/dist/suppression/config-loader.d.ts.map +1 -0
  312. package/dist/suppression/config-loader.js +424 -0
  313. package/dist/suppression/config-loader.js.map +1 -0
  314. package/dist/suppression/hash.d.ts +48 -0
  315. package/dist/suppression/hash.d.ts.map +1 -0
  316. package/dist/suppression/hash.js +88 -0
  317. package/dist/suppression/hash.js.map +1 -0
  318. package/dist/suppression/index.d.ts +11 -0
  319. package/dist/suppression/index.d.ts.map +1 -0
  320. package/dist/suppression/index.js +39 -0
  321. package/dist/suppression/index.js.map +1 -0
  322. package/dist/suppression/inline-parser.d.ts +39 -0
  323. package/dist/suppression/inline-parser.d.ts.map +1 -0
  324. package/dist/suppression/inline-parser.js +218 -0
  325. package/dist/suppression/inline-parser.js.map +1 -0
  326. package/dist/suppression/manager.d.ts +94 -0
  327. package/dist/suppression/manager.d.ts.map +1 -0
  328. package/dist/suppression/manager.js +292 -0
  329. package/dist/suppression/manager.js.map +1 -0
  330. package/dist/suppression/types.d.ts +151 -0
  331. package/dist/suppression/types.d.ts.map +1 -0
  332. package/dist/suppression/types.js +28 -0
  333. package/dist/suppression/types.js.map +1 -0
  334. package/dist/tiers.d.ts +3 -3
  335. package/dist/tiers.d.ts.map +1 -1
  336. package/dist/tiers.js +34 -7
  337. package/dist/tiers.js.map +1 -1
  338. package/dist/types.d.ts +140 -9
  339. package/dist/types.d.ts.map +1 -1
  340. package/dist/types.js +34 -0
  341. package/dist/types.js.map +1 -1
  342. package/dist/utils/code-analysis.d.ts +39 -0
  343. package/dist/utils/code-analysis.d.ts.map +1 -0
  344. package/dist/utils/code-analysis.js +159 -0
  345. package/dist/utils/code-analysis.js.map +1 -0
  346. package/dist/utils/comment-analyzer.d.ts +38 -0
  347. package/dist/utils/comment-analyzer.d.ts.map +1 -0
  348. package/dist/utils/comment-analyzer.js +218 -0
  349. package/dist/utils/comment-analyzer.js.map +1 -0
  350. package/dist/utils/context-helpers.d.ts +112 -1
  351. package/dist/utils/context-helpers.d.ts.map +1 -1
  352. package/dist/utils/context-helpers.js +364 -11
  353. package/dist/utils/context-helpers.js.map +1 -1
  354. package/dist/utils/environment-context.d.ts +76 -0
  355. package/dist/utils/environment-context.d.ts.map +1 -0
  356. package/dist/utils/environment-context.js +271 -0
  357. package/dist/utils/environment-context.js.map +1 -0
  358. package/dist/utils/intent-detector.d.ts +66 -0
  359. package/dist/utils/intent-detector.d.ts.map +1 -0
  360. package/dist/utils/intent-detector.js +282 -0
  361. package/dist/utils/intent-detector.js.map +1 -0
  362. package/dist/utils/parsed-file.d.ts +51 -0
  363. package/dist/utils/parsed-file.d.ts.map +1 -0
  364. package/dist/utils/parsed-file.js +95 -0
  365. package/dist/utils/parsed-file.js.map +1 -0
  366. package/dist/utils/route-hierarchy.d.ts +50 -0
  367. package/dist/utils/route-hierarchy.d.ts.map +1 -0
  368. package/dist/utils/route-hierarchy.js +226 -0
  369. package/dist/utils/route-hierarchy.js.map +1 -0
  370. package/dist/utils/schema-semantics.d.ts +45 -0
  371. package/dist/utils/schema-semantics.d.ts.map +1 -0
  372. package/dist/utils/schema-semantics.js +193 -0
  373. package/dist/utils/schema-semantics.js.map +1 -0
  374. package/package.json +4 -2
  375. package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
  376. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
  377. package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
  378. package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
  379. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
  380. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
  381. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
  382. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
  383. package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
  384. package/src/__tests__/benchmark/fixtures/layer2/index.ts +30 -0
  385. package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
  386. package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
  387. package/src/__tests__/benchmark/fixtures/layer2/phase5-excessive-agency.ts +580 -0
  388. package/src/__tests__/benchmark/fixtures/layer2/sprint6-ai-enhancements.ts +515 -0
  389. package/src/__tests__/benchmark/run-depth-validation.ts +9 -9
  390. package/src/__tests__/category-filter.test.ts +478 -0
  391. package/src/__tests__/regression/known-false-positives.test.ts +490 -0
  392. package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +762 -0
  393. package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
  394. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +0 -9
  395. package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
  396. package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
  397. package/src/__tests__/validation/run-validation.ts +7 -7
  398. package/src/ai-context/__tests__/manager.test.ts +193 -0
  399. package/src/ai-context/index.ts +15 -0
  400. package/src/ai-context/manager.ts +145 -0
  401. package/src/baseline/__tests__/diff.test.ts +261 -0
  402. package/src/baseline/__tests__/manager.test.ts +225 -0
  403. package/src/baseline/diff.ts +135 -0
  404. package/src/baseline/index.ts +29 -0
  405. package/src/baseline/manager.ts +230 -0
  406. package/src/baseline/types.ts +97 -0
  407. package/src/category-filter.ts +400 -0
  408. package/src/filtering/__tests__/pipeline.test.ts +134 -0
  409. package/src/filtering/context-adjustments.ts +111 -0
  410. package/src/filtering/index.ts +10 -0
  411. package/src/filtering/pipeline.ts +130 -0
  412. package/src/formatters/__tests__/ai-context.test.ts +254 -0
  413. package/src/formatters/ai-context.ts +302 -0
  414. package/src/formatters/cli-terminal.ts +444 -41
  415. package/src/formatters/github-comment.ts +82 -14
  416. package/src/formatters/ide/__tests__/ide.test.ts +319 -0
  417. package/src/formatters/ide/claude-code.ts +110 -0
  418. package/src/formatters/ide/cursor.ts +147 -0
  419. package/src/formatters/ide/index.ts +216 -0
  420. package/src/formatters/ide/windsurf.ts +135 -0
  421. package/src/formatters/index.ts +28 -0
  422. package/src/index.ts +506 -45
  423. package/src/layer1/comments.ts +3 -1
  424. package/src/layer1/config-audit.ts +74 -14
  425. package/src/layer1/config-mcp-audit.ts +278 -0
  426. package/src/layer1/entropy.ts +234 -1
  427. package/src/layer1/file-flags.ts +17 -6
  428. package/src/layer1/index.ts +29 -23
  429. package/src/layer1/patterns.ts +42 -4
  430. package/src/layer1/urls.ts +188 -14
  431. package/src/layer1/weak-crypto.ts +168 -16
  432. package/src/layer2/ai-agent-tools.ts +1043 -2
  433. package/src/layer2/ai-endpoint-protection.ts +19 -4
  434. package/src/layer2/ai-execution-sinks.ts +755 -29
  435. package/src/layer2/ai-fingerprinting.ts +33 -33
  436. package/src/layer2/ai-mcp-security.ts +933 -0
  437. package/src/layer2/ai-package-hallucination.ts +940 -0
  438. package/src/layer2/ai-prompt-hygiene.ts +898 -17
  439. package/src/layer2/ai-rag-safety.ts +467 -5
  440. package/src/layer2/ai-schema-validation.ts +4 -2
  441. package/src/layer2/auth-antipatterns.ts +235 -20
  442. package/src/layer2/byok-patterns.ts +9 -3
  443. package/src/layer2/dangerous-functions/child-process.ts +98 -0
  444. package/src/layer2/dangerous-functions/dom-xss.ts +292 -0
  445. package/src/layer2/dangerous-functions/index.ts +1533 -0
  446. package/src/layer2/dangerous-functions/json-parse.ts +385 -0
  447. package/src/layer2/dangerous-functions/math-random.ts +789 -0
  448. package/src/layer2/dangerous-functions/patterns.ts +176 -0
  449. package/src/layer2/dangerous-functions/request-validation.ts +145 -0
  450. package/src/layer2/dangerous-functions/utils/control-flow.ts +35 -0
  451. package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
  452. package/src/layer2/dangerous-functions/utils/index.ts +25 -0
  453. package/src/layer2/dangerous-functions/utils/schema-validation.ts +106 -0
  454. package/src/layer2/data-exposure.ts +18 -39
  455. package/src/layer2/framework-checks.ts +9 -2
  456. package/src/layer2/index.ts +124 -43
  457. package/src/layer2/logic-gates.ts +64 -22
  458. package/src/layer2/model-supply-chain.ts +531 -0
  459. package/src/layer2/risky-imports.ts +9 -2
  460. package/src/layer2/variables.ts +9 -2
  461. package/src/layer3/__tests__/osv-check.test.ts +384 -0
  462. package/src/layer3/anthropic/auto-dismiss.ts +223 -0
  463. package/src/layer3/anthropic/clients.ts +84 -0
  464. package/src/layer3/anthropic/index.ts +170 -0
  465. package/src/layer3/anthropic/prompts/index.ts +14 -0
  466. package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
  467. package/src/layer3/anthropic/prompts/validation.ts +419 -0
  468. package/src/layer3/anthropic/providers/anthropic.ts +310 -0
  469. package/src/layer3/anthropic/providers/index.ts +8 -0
  470. package/src/layer3/anthropic/providers/openai.ts +384 -0
  471. package/src/layer3/anthropic/request-builder.ts +150 -0
  472. package/src/layer3/anthropic/types.ts +148 -0
  473. package/src/layer3/anthropic/utils/index.ts +26 -0
  474. package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
  475. package/src/layer3/anthropic/utils/response-parser.ts +322 -0
  476. package/src/layer3/anthropic/utils/retry.ts +75 -0
  477. package/src/layer3/index.ts +18 -5
  478. package/src/layer3/osv-check.ts +420 -0
  479. package/src/modes/incremental.ts +1 -1
  480. package/src/rules/__tests__/framework-fixes.test.ts +689 -0
  481. package/src/rules/__tests__/metadata.test.ts +218 -0
  482. package/src/rules/framework-fixes.ts +470 -0
  483. package/src/rules/index.ts +21 -0
  484. package/src/rules/metadata.ts +831 -0
  485. package/src/suppression/__tests__/config-loader.test.ts +382 -0
  486. package/src/suppression/__tests__/hash.test.ts +166 -0
  487. package/src/suppression/__tests__/inline-parser.test.ts +212 -0
  488. package/src/suppression/__tests__/manager.test.ts +415 -0
  489. package/src/suppression/config-loader.ts +462 -0
  490. package/src/suppression/hash.ts +95 -0
  491. package/src/suppression/index.ts +51 -0
  492. package/src/suppression/inline-parser.ts +273 -0
  493. package/src/suppression/manager.ts +379 -0
  494. package/src/suppression/types.ts +174 -0
  495. package/src/tiers.ts +45 -9
  496. package/src/types.ts +212 -8
  497. package/src/utils/__tests__/code-analysis.test.ts +165 -0
  498. package/src/utils/__tests__/parsed-file.test.ts +124 -0
  499. package/src/utils/code-analysis.ts +179 -0
  500. package/src/utils/comment-analyzer.ts +249 -0
  501. package/src/utils/context-helpers.ts +421 -11
  502. package/src/utils/environment-context.ts +304 -0
  503. package/src/utils/intent-detector.ts +318 -0
  504. package/src/utils/parsed-file.ts +103 -0
  505. package/src/utils/route-hierarchy.ts +250 -0
  506. package/src/utils/schema-semantics.ts +233 -0
  507. package/dist/layer2/dangerous-functions.d.ts +0 -7
  508. package/dist/layer2/dangerous-functions.d.ts.map +0 -1
  509. package/dist/layer2/dangerous-functions.js +0 -1701
  510. package/dist/layer2/dangerous-functions.js.map +0 -1
  511. package/dist/layer3/anthropic.d.ts +0 -87
  512. package/dist/layer3/anthropic.d.ts.map +0 -1
  513. package/dist/layer3/anthropic.js +0 -1948
  514. package/dist/layer3/anthropic.js.map +0 -1
  515. package/dist/layer3/openai.d.ts +0 -25
  516. package/dist/layer3/openai.d.ts.map +0 -1
  517. package/dist/layer3/openai.js +0 -238
  518. package/dist/layer3/openai.js.map +0 -1
  519. package/src/layer2/dangerous-functions.ts +0 -1940
  520. package/src/layer3/anthropic.ts +0 -2257
@@ -35,7 +35,29 @@ const CATEGORY_DOCS: Partial<Record<VulnerabilityCategory, string>> = {
35
35
  }
36
36
 
37
37
  /**
38
- * Format a single finding as a markdown list item
38
+ * Helper to determine language from file path
39
+ */
40
+ function getLanguageFromPath(filePath: string): string {
41
+ const ext = filePath.split('.').pop()?.toLowerCase() || ''
42
+ const langMap: Record<string, string> = {
43
+ ts: 'typescript',
44
+ tsx: 'typescript',
45
+ js: 'javascript',
46
+ jsx: 'javascript',
47
+ py: 'python',
48
+ go: 'go',
49
+ java: 'java',
50
+ rb: 'ruby',
51
+ php: 'php',
52
+ yaml: 'yaml',
53
+ yml: 'yaml',
54
+ json: 'json',
55
+ }
56
+ return langMap[ext] || ''
57
+ }
58
+
59
+ /**
60
+ * Format a single finding as a markdown section with actionable info (PRO-82)
39
61
  */
40
62
  function formatFinding(finding: Vulnerability, options: { showFile?: boolean; showDocs?: boolean } = {}): string {
41
63
  const { showFile = true, showDocs = true } = options
@@ -44,18 +66,43 @@ function formatFinding(finding: Vulnerability, options: { showFile?: boolean; sh
44
66
  ? `\`${finding.filePath}:${finding.lineNumber}\``
45
67
  : `Line ${finding.lineNumber}`
46
68
 
47
- let md = `- ${badge} **${finding.title}**\n`
48
- md += ` - 📍 ${location}\n`
49
- md += ` - ${finding.description}\n`
69
+ let md = `#### ${badge} ${finding.title}\n\n`
70
+ md += `📍 ${location}\n\n`
71
+
72
+ // Impact (why this matters) - shown if available
73
+ if (finding.impact) {
74
+ md += `**Impact:** ${finding.impact}\n\n`
75
+ }
76
+
77
+ // Code snippet in collapsible
78
+ if (finding.lineContent && finding.lineContent.trim()) {
79
+ const language = getLanguageFromPath(finding.filePath)
80
+ md += `<details>\n<summary>View code</summary>\n\n`
81
+ md += `\`\`\`${language}\n${finding.lineContent.trim()}\n\`\`\`\n\n`
82
+ md += `</details>\n\n`
83
+ }
50
84
 
51
- if (finding.suggestedFix) {
52
- md += ` - 💡 **Fix:** ${finding.suggestedFix}\n`
85
+ // Fix steps - shown as numbered list (PRO-82)
86
+ if (finding.fixSteps && finding.fixSteps.length > 0) {
87
+ md += `**Fix:**\n`
88
+ finding.fixSteps.forEach((step, i) => {
89
+ md += `${i + 1}. ${step}\n`
90
+ })
91
+ md += '\n'
92
+ } else if (finding.suggestedFix) {
93
+ // Fallback to legacy field
94
+ md += `💡 **Fix:** ${finding.suggestedFix}\n\n`
53
95
  }
54
96
 
55
- // Add documentation link if available
97
+ // Documentation links
56
98
  const docsUrl = CATEGORY_DOCS[finding.category]
57
- if (showDocs && docsUrl) {
58
- md += ` - 📚 [Learn more](${docsUrl})\n`
99
+ const referenceUrl = finding.references && finding.references.length > 0 ? finding.references[0] : null
100
+
101
+ if (showDocs && (docsUrl || referenceUrl)) {
102
+ const links: string[] = []
103
+ if (docsUrl) links.push(`[Learn more](${docsUrl})`)
104
+ if (referenceUrl && referenceUrl !== docsUrl) links.push(`[OWASP/CWE](${referenceUrl})`)
105
+ md += links.join(' · ') + '\n\n'
59
106
  }
60
107
 
61
108
  return md
@@ -103,7 +150,7 @@ export interface GitHubCommentOptions {
103
150
  maxFindingsPerGroup?: number
104
151
  showAllFindings?: boolean
105
152
  includeFooter?: boolean
106
- scanDepth?: 'cheap' | 'validated' | 'deep'
153
+ scanDepth?: 'local' | 'verified' | 'deep'
107
154
  previousScanCounts?: {
108
155
  critical: number
109
156
  high: number
@@ -293,8 +340,8 @@ function formatScanMetadata(result: ScanResult, scanDepth?: string): string {
293
340
  md += `| Scan duration | ${(result.scanDuration / 1000).toFixed(1)}s |\n`
294
341
  if (scanDepth) {
295
342
  const depthLabels: Record<string, string> = {
296
- cheap: 'Fast (pattern matching)',
297
- validated: 'Validated (AI-assisted)',
343
+ local: 'Fast (pattern matching)',
344
+ verified: 'Verified (AI-assisted)',
298
345
  deep: 'Deep (full semantic)',
299
346
  }
300
347
  md += `| Scan depth | ${depthLabels[scanDepth] || scanDepth} |\n`
@@ -357,7 +404,7 @@ export function formatShortStatus(result: ScanResult): string {
357
404
  }
358
405
 
359
406
  /**
360
- * Format as inline annotation for GitHub check run
407
+ * Format as inline annotation for GitHub check run (PRO-82: actionable output)
361
408
  */
362
409
  export function formatAnnotation(finding: Vulnerability): {
363
410
  path: string
@@ -371,12 +418,33 @@ export function formatAnnotation(finding: Vulnerability): {
371
418
  finding.severity === 'critical' || finding.severity === 'high' ? 'failure' :
372
419
  finding.severity === 'medium' ? 'warning' : 'notice'
373
420
 
421
+ // Build actionable message
422
+ let message = ''
423
+
424
+ // Impact first (why this matters)
425
+ if (finding.impact) {
426
+ message += `Impact: ${finding.impact}\n\n`
427
+ }
428
+
429
+ // Description
430
+ message += finding.description
431
+
432
+ // Fix steps or legacy suggestedFix
433
+ if (finding.fixSteps && finding.fixSteps.length > 0) {
434
+ message += '\n\n💡 Fix:\n'
435
+ finding.fixSteps.forEach((step, i) => {
436
+ message += `${i + 1}. ${step}\n`
437
+ })
438
+ } else if (finding.suggestedFix) {
439
+ message += `\n\n💡 Fix: ${finding.suggestedFix}`
440
+ }
441
+
374
442
  return {
375
443
  path: finding.filePath,
376
444
  start_line: finding.lineNumber,
377
445
  end_line: finding.lineNumber,
378
446
  annotation_level: level,
379
447
  title: `${SEVERITY_BADGE[finding.severity]} ${finding.title}`,
380
- message: finding.description + (finding.suggestedFix ? `\n\n💡 Fix: ${finding.suggestedFix}` : ''),
448
+ message,
381
449
  }
382
450
  }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * IDE Integration Tests
3
+ */
4
+
5
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs'
6
+ import { join } from 'path'
7
+ import { tmpdir } from 'os'
8
+ import {
9
+ formatCursorRules,
10
+ formatWindsurfRules,
11
+ formatClaudeCodeSection,
12
+ detectIDEConfigs,
13
+ writeIDEFile,
14
+ updateClaudeMdSection,
15
+ clearIDEFiles,
16
+ } from '../index'
17
+ import type { ScanResult, Vulnerability } from '../../../types'
18
+
19
+ const createMockVulnerability = (overrides: Partial<Vulnerability> = {}): Vulnerability => ({
20
+ id: 'test-vuln-1',
21
+ filePath: 'src/api/users.ts',
22
+ lineNumber: 42,
23
+ lineContent: 'const query = `SELECT * FROM users WHERE id = ${userId}`',
24
+ severity: 'high',
25
+ category: 'sql_injection',
26
+ title: 'SQL Injection Vulnerability',
27
+ description: 'User input is directly concatenated into SQL query without parameterization.',
28
+ suggestedFix: 'Use parameterized queries instead of string concatenation.',
29
+ confidence: 'high',
30
+ layer: 2,
31
+ fixSteps: [
32
+ 'Replace string concatenation with parameterized query',
33
+ 'Use prepared statements or an ORM',
34
+ ],
35
+ ...overrides,
36
+ })
37
+
38
+ const createMockScanResult = (vulnerabilities: Vulnerability[] = []): ScanResult => ({
39
+ repoName: 'test-repo',
40
+ repoUrl: 'https://github.com/test/test-repo',
41
+ branch: 'main',
42
+ filesScanned: 50,
43
+ filesSkipped: 5,
44
+ vulnerabilities,
45
+ severityCounts: {
46
+ critical: vulnerabilities.filter(v => v.severity === 'critical').length,
47
+ high: vulnerabilities.filter(v => v.severity === 'high').length,
48
+ medium: vulnerabilities.filter(v => v.severity === 'medium').length,
49
+ low: vulnerabilities.filter(v => v.severity === 'low').length,
50
+ info: vulnerabilities.filter(v => v.severity === 'info').length,
51
+ },
52
+ categoryCounts: {},
53
+ hasBlockingIssues: vulnerabilities.some(v => v.severity === 'critical' || v.severity === 'high'),
54
+ scanDuration: 1500,
55
+ timestamp: '2024-01-15T10:30:00.000Z',
56
+ })
57
+
58
+ describe('formatCursorRules', () => {
59
+ it('should generate MDC format with frontmatter', () => {
60
+ const vuln = createMockVulnerability()
61
+ const result = createMockScanResult([vuln])
62
+
63
+ const output = formatCursorRules(result)
64
+
65
+ // Check frontmatter
66
+ expect(output).toContain('---')
67
+ expect(output).toContain('description: Oculum Security Findings')
68
+ expect(output).toContain('alwaysApply: false')
69
+ expect(output).toContain('---')
70
+ })
71
+
72
+ it('should include glob patterns for affected files', () => {
73
+ const vulns = [
74
+ createMockVulnerability({ filePath: 'src/api/users.ts' }),
75
+ createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
76
+ createMockVulnerability({ id: 'v3', filePath: 'src/config/db.ts' }),
77
+ ]
78
+ const result = createMockScanResult(vulns)
79
+
80
+ const output = formatCursorRules(result)
81
+
82
+ // Should include globs for affected directories
83
+ expect(output).toContain('globs:')
84
+ expect(output).toContain('src/api/**')
85
+ expect(output).toContain('src/config/**')
86
+ })
87
+
88
+ it('should include security findings', () => {
89
+ const vuln = createMockVulnerability()
90
+ const result = createMockScanResult([vuln])
91
+
92
+ const output = formatCursorRules(result)
93
+
94
+ expect(output).toContain('SQL Injection Vulnerability')
95
+ expect(output).toContain('src/api/users.ts:42')
96
+ })
97
+
98
+ it('should include fix instructions', () => {
99
+ const vuln = createMockVulnerability()
100
+ const result = createMockScanResult([vuln])
101
+
102
+ const output = formatCursorRules(result)
103
+
104
+ expect(output).toContain('DO NOT')
105
+ // The fix shows example code with '?' instead of the word 'parameterized'
106
+ expect(output).toContain('GOOD')
107
+ expect(output).toContain("'SELECT * FROM users WHERE id = ?'")
108
+ })
109
+
110
+ it('should return minimal output for zero findings', () => {
111
+ const result = createMockScanResult([])
112
+
113
+ const output = formatCursorRules(result)
114
+
115
+ expect(output).toContain('description: Oculum Security Findings')
116
+ expect(output).toContain('No security issues found')
117
+ })
118
+ })
119
+
120
+ describe('formatWindsurfRules', () => {
121
+ it('should generate markdown security rules', () => {
122
+ const vuln = createMockVulnerability()
123
+ const result = createMockScanResult([vuln])
124
+
125
+ const output = formatWindsurfRules(result)
126
+
127
+ expect(output).toContain('# Oculum Security Rules')
128
+ expect(output).toContain('## Security Issues')
129
+ })
130
+
131
+ it('should list findings by file', () => {
132
+ const vulns = [
133
+ createMockVulnerability({ filePath: 'src/api/users.ts' }),
134
+ createMockVulnerability({ id: 'v2', filePath: 'src/api/orders.ts' }),
135
+ ]
136
+ const result = createMockScanResult(vulns)
137
+
138
+ const output = formatWindsurfRules(result)
139
+
140
+ expect(output).toContain('src/api/users.ts')
141
+ expect(output).toContain('src/api/orders.ts')
142
+ expect(output).toContain('Line 42')
143
+ })
144
+
145
+ it('should include code patterns section', () => {
146
+ const vuln = createMockVulnerability()
147
+ const result = createMockScanResult([vuln])
148
+
149
+ const output = formatWindsurfRules(result)
150
+
151
+ expect(output).toContain('## Code Patterns')
152
+ expect(output).toContain('ALWAYS')
153
+ })
154
+ })
155
+
156
+ describe('formatClaudeCodeSection', () => {
157
+ it('should return section with markers', () => {
158
+ const vuln = createMockVulnerability()
159
+ const result = createMockScanResult([vuln])
160
+
161
+ const output = formatClaudeCodeSection(result)
162
+
163
+ expect(output).toContain('<!-- OCULUM_SECURITY_START -->')
164
+ expect(output).toContain('<!-- OCULUM_SECURITY_END -->')
165
+ })
166
+
167
+ it('should include security issues', () => {
168
+ const vuln = createMockVulnerability()
169
+ const result = createMockScanResult([vuln])
170
+
171
+ const output = formatClaudeCodeSection(result)
172
+
173
+ expect(output).toContain('## Security Issues')
174
+ expect(output).toContain('SQL Injection')
175
+ expect(output).toContain('src/api/users.ts:42')
176
+ })
177
+
178
+ it('should include verification instructions', () => {
179
+ const vuln = createMockVulnerability()
180
+ const result = createMockScanResult([vuln])
181
+
182
+ const output = formatClaudeCodeSection(result)
183
+
184
+ expect(output).toContain('oculum scan')
185
+ })
186
+ })
187
+
188
+ describe('detectIDEConfigs', () => {
189
+ const testDir = join(tmpdir(), 'oculum-ide-detect-test-' + Date.now())
190
+
191
+ beforeAll(() => {
192
+ mkdirSync(testDir, { recursive: true })
193
+ })
194
+
195
+ afterAll(() => {
196
+ try {
197
+ rmSync(testDir, { recursive: true, force: true })
198
+ } catch {
199
+ // Ignore cleanup errors
200
+ }
201
+ })
202
+
203
+ it('should detect .cursor directory', () => {
204
+ // Create .cursor directory
205
+ mkdirSync(join(testDir, '.cursor'), { recursive: true })
206
+
207
+ const detected = detectIDEConfigs(testDir)
208
+
209
+ expect(detected).toContain('cursor')
210
+ })
211
+
212
+ it('should detect CLAUDE.md file', () => {
213
+ // Create CLAUDE.md file
214
+ writeFileSync(join(testDir, 'CLAUDE.md'), '# Claude Code\n')
215
+
216
+ const detected = detectIDEConfigs(testDir)
217
+
218
+ expect(detected).toContain('claude-code')
219
+ })
220
+
221
+ it('should return array of detected IDEs', () => {
222
+ const detected = detectIDEConfigs(testDir)
223
+
224
+ expect(Array.isArray(detected)).toBe(true)
225
+ })
226
+ })
227
+
228
+ describe('IDE file operations', () => {
229
+ const testDir = join(tmpdir(), 'oculum-ide-ops-test-' + Date.now())
230
+
231
+ beforeAll(() => {
232
+ mkdirSync(testDir, { recursive: true })
233
+ })
234
+
235
+ afterAll(() => {
236
+ try {
237
+ rmSync(testDir, { recursive: true, force: true })
238
+ } catch {
239
+ // Ignore cleanup errors
240
+ }
241
+ })
242
+
243
+ it('should write IDE file and create directories', () => {
244
+ const result = writeIDEFile(
245
+ testDir,
246
+ '.cursor/rules/security.mdc',
247
+ '# Security Rules'
248
+ )
249
+
250
+ expect(result.success).toBe(true)
251
+ expect(existsSync(join(testDir, '.cursor/rules/security.mdc'))).toBe(true)
252
+ })
253
+
254
+ it('should update CLAUDE.md section between markers', () => {
255
+ // Create initial CLAUDE.md
256
+ const initialContent = `# Project
257
+
258
+ Some existing content.
259
+
260
+ <!-- OCULUM_SECURITY_START -->
261
+ Old security content
262
+ <!-- OCULUM_SECURITY_END -->
263
+
264
+ More content.
265
+ `
266
+ writeFileSync(join(testDir, 'CLAUDE.md'), initialContent)
267
+
268
+ // Update section
269
+ const result = updateClaudeMdSection(
270
+ testDir,
271
+ '<!-- OCULUM_SECURITY_START -->\nNew security content\n<!-- OCULUM_SECURITY_END -->'
272
+ )
273
+
274
+ expect(result.success).toBe(true)
275
+
276
+ // Read and verify
277
+ const { readFileSync } = require('fs')
278
+ const updated = readFileSync(join(testDir, 'CLAUDE.md'), 'utf-8')
279
+
280
+ expect(updated).toContain('New security content')
281
+ expect(updated).not.toContain('Old security content')
282
+ expect(updated).toContain('Some existing content')
283
+ expect(updated).toContain('More content')
284
+ })
285
+
286
+ it('should append section if no markers exist', () => {
287
+ // Create CLAUDE.md without markers
288
+ const noMarkersDir = join(testDir, 'no-markers')
289
+ mkdirSync(noMarkersDir, { recursive: true })
290
+ writeFileSync(join(noMarkersDir, 'CLAUDE.md'), '# Project\n\nSome content.\n')
291
+
292
+ const result = updateClaudeMdSection(
293
+ noMarkersDir,
294
+ '<!-- OCULUM_SECURITY_START -->\nSecurity content\n<!-- OCULUM_SECURITY_END -->'
295
+ )
296
+
297
+ expect(result.success).toBe(true)
298
+
299
+ const { readFileSync } = require('fs')
300
+ const updated = readFileSync(join(noMarkersDir, 'CLAUDE.md'), 'utf-8')
301
+
302
+ expect(updated).toContain('Security content')
303
+ expect(updated).toContain('Some content')
304
+ })
305
+
306
+ it('should clear IDE files', () => {
307
+ // Create some IDE files
308
+ const clearDir = join(testDir, 'clear-test')
309
+ mkdirSync(join(clearDir, '.cursor/rules'), { recursive: true })
310
+ writeFileSync(join(clearDir, '.cursor/rules/security.mdc'), 'content')
311
+ writeFileSync(join(clearDir, '.windsurfrules'), 'content')
312
+
313
+ const result = clearIDEFiles(clearDir)
314
+
315
+ expect(result.success).toBe(true)
316
+ expect(existsSync(join(clearDir, '.cursor/rules/security.mdc'))).toBe(false)
317
+ expect(existsSync(join(clearDir, '.windsurfrules'))).toBe(false)
318
+ })
319
+ })
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Claude Code IDE Integration
3
+ * Generates CLAUDE.md section format
4
+ */
5
+
6
+ import type { ScanResult, Vulnerability, VulnerabilityCategory } from '../../types'
7
+ import { sortBySeverity } from '../grouping'
8
+
9
+ /** Start marker for Oculum section in CLAUDE.md */
10
+ export const OCULUM_SECTION_START = '<!-- OCULUM_SECURITY_START -->'
11
+
12
+ /** End marker for Oculum section in CLAUDE.md */
13
+ export const OCULUM_SECTION_END = '<!-- OCULUM_SECURITY_END -->'
14
+
15
+ /**
16
+ * Get "ALWAYS" rule for a category
17
+ */
18
+ function getAlwaysRule(category: VulnerabilityCategory): string | null {
19
+ const rules: Partial<Record<VulnerabilityCategory, string>> = {
20
+ sql_injection: 'Use parameterized queries for all database operations',
21
+ xss: 'Escape or sanitize user input before rendering in HTML',
22
+ hardcoded_secret: 'Use environment variables for secrets and API keys',
23
+ high_entropy_string: 'Store credentials in secure vaults, not in code',
24
+ missing_auth: 'Add authentication middleware to API endpoints',
25
+ dangerous_function: 'Avoid eval() and exec() - use safe alternatives',
26
+ command_injection: 'Sanitize all input passed to shell commands',
27
+ data_exposure: 'Never log sensitive data or expose it in responses',
28
+ weak_crypto: 'Use modern cryptographic algorithms (SHA-256+, AES-256)',
29
+ ai_prompt_injection: 'Sanitize user input before including in prompts',
30
+ ai_unsafe_execution: 'Validate AI-generated code before execution',
31
+ }
32
+
33
+ return rules[category] || null
34
+ }
35
+
36
+ /**
37
+ * Format scan result as CLAUDE.md section
38
+ *
39
+ * @param result - The scan result to format
40
+ * @returns Markdown string with markers for CLAUDE.md
41
+ */
42
+ export function formatClaudeCodeSection(result: ScanResult): string {
43
+ const { vulnerabilities, timestamp } = result
44
+
45
+ // Sort by severity
46
+ const sorted = sortBySeverity(vulnerabilities)
47
+
48
+ let md = ''
49
+
50
+ // Section markers
51
+ md += `${OCULUM_SECTION_START}\n`
52
+ md += `## Security Issues (Auto-Generated)\n\n`
53
+ md += `> Last scan: ${timestamp}\n\n`
54
+
55
+ // No findings case
56
+ if (vulnerabilities.length === 0) {
57
+ md += `No security issues detected.\n\n`
58
+ md += `Run \`oculum scan\` to scan for vulnerabilities.\n`
59
+ md += `${OCULUM_SECTION_END}\n`
60
+ return md
61
+ }
62
+
63
+ // Summary
64
+ const { severityCounts, hasBlockingIssues } = result
65
+ if (hasBlockingIssues) {
66
+ md += `**BLOCKING ISSUES:** ${severityCounts.critical + severityCounts.high} issues must be fixed.\n\n`
67
+ }
68
+
69
+ // DO NOT section - list findings
70
+ md += `**DO NOT** ignore these findings:\n\n`
71
+
72
+ let count = 0
73
+ for (const vuln of sorted.slice(0, 10)) {
74
+ count++
75
+ const severityLabel =
76
+ vuln.severity === 'critical' ? 'CRITICAL' :
77
+ vuln.severity === 'high' ? 'HIGH' :
78
+ vuln.severity.toUpperCase()
79
+
80
+ md += `${count}. **${vuln.title}** [${severityLabel}] in \`${vuln.filePath}:${vuln.lineNumber}\`\n`
81
+ }
82
+
83
+ if (sorted.length > 10) {
84
+ md += `\n... and ${sorted.length - 10} more issues.\n`
85
+ }
86
+ md += '\n'
87
+
88
+ // ALWAYS section - best practices
89
+ const alwaysRules = new Set<string>()
90
+ for (const vuln of vulnerabilities) {
91
+ const rule = getAlwaysRule(vuln.category)
92
+ if (rule) {
93
+ alwaysRules.add(rule)
94
+ }
95
+ }
96
+
97
+ if (alwaysRules.size > 0) {
98
+ md += `**ALWAYS:**\n`
99
+ for (const rule of alwaysRules) {
100
+ md += `- ${rule}\n`
101
+ }
102
+ md += '\n'
103
+ }
104
+
105
+ // Verification instructions
106
+ md += `Run \`oculum scan\` to verify fixes.\n`
107
+ md += `${OCULUM_SECTION_END}\n`
108
+
109
+ return md
110
+ }