@oculum/scanner 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/dist/formatters/cli-terminal.d.ts +27 -0
  2. package/dist/formatters/cli-terminal.d.ts.map +1 -0
  3. package/dist/formatters/cli-terminal.js +412 -0
  4. package/dist/formatters/cli-terminal.js.map +1 -0
  5. package/dist/formatters/github-comment.d.ts +41 -0
  6. package/dist/formatters/github-comment.d.ts.map +1 -0
  7. package/dist/formatters/github-comment.js +306 -0
  8. package/dist/formatters/github-comment.js.map +1 -0
  9. package/dist/formatters/grouping.d.ts +52 -0
  10. package/dist/formatters/grouping.d.ts.map +1 -0
  11. package/dist/formatters/grouping.js +152 -0
  12. package/dist/formatters/grouping.js.map +1 -0
  13. package/dist/formatters/index.d.ts +9 -0
  14. package/dist/formatters/index.d.ts.map +1 -0
  15. package/dist/formatters/index.js +35 -0
  16. package/dist/formatters/index.js.map +1 -0
  17. package/dist/formatters/vscode-diagnostic.d.ts +103 -0
  18. package/dist/formatters/vscode-diagnostic.d.ts.map +1 -0
  19. package/dist/formatters/vscode-diagnostic.js +151 -0
  20. package/dist/formatters/vscode-diagnostic.js.map +1 -0
  21. package/dist/index.d.ts +52 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +648 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/layer1/comments.d.ts +8 -0
  26. package/dist/layer1/comments.d.ts.map +1 -0
  27. package/dist/layer1/comments.js +203 -0
  28. package/dist/layer1/comments.js.map +1 -0
  29. package/dist/layer1/config-audit.d.ts +8 -0
  30. package/dist/layer1/config-audit.d.ts.map +1 -0
  31. package/dist/layer1/config-audit.js +252 -0
  32. package/dist/layer1/config-audit.js.map +1 -0
  33. package/dist/layer1/entropy.d.ts +8 -0
  34. package/dist/layer1/entropy.d.ts.map +1 -0
  35. package/dist/layer1/entropy.js +500 -0
  36. package/dist/layer1/entropy.js.map +1 -0
  37. package/dist/layer1/file-flags.d.ts +7 -0
  38. package/dist/layer1/file-flags.d.ts.map +1 -0
  39. package/dist/layer1/file-flags.js +112 -0
  40. package/dist/layer1/file-flags.js.map +1 -0
  41. package/dist/layer1/index.d.ts +36 -0
  42. package/dist/layer1/index.d.ts.map +1 -0
  43. package/dist/layer1/index.js +132 -0
  44. package/dist/layer1/index.js.map +1 -0
  45. package/dist/layer1/patterns.d.ts +8 -0
  46. package/dist/layer1/patterns.d.ts.map +1 -0
  47. package/dist/layer1/patterns.js +482 -0
  48. package/dist/layer1/patterns.js.map +1 -0
  49. package/dist/layer1/urls.d.ts +8 -0
  50. package/dist/layer1/urls.d.ts.map +1 -0
  51. package/dist/layer1/urls.js +296 -0
  52. package/dist/layer1/urls.js.map +1 -0
  53. package/dist/layer1/weak-crypto.d.ts +7 -0
  54. package/dist/layer1/weak-crypto.d.ts.map +1 -0
  55. package/dist/layer1/weak-crypto.js +291 -0
  56. package/dist/layer1/weak-crypto.js.map +1 -0
  57. package/dist/layer2/ai-agent-tools.d.ts +19 -0
  58. package/dist/layer2/ai-agent-tools.d.ts.map +1 -0
  59. package/dist/layer2/ai-agent-tools.js +528 -0
  60. package/dist/layer2/ai-agent-tools.js.map +1 -0
  61. package/dist/layer2/ai-endpoint-protection.d.ts +36 -0
  62. package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -0
  63. package/dist/layer2/ai-endpoint-protection.js +332 -0
  64. package/dist/layer2/ai-endpoint-protection.js.map +1 -0
  65. package/dist/layer2/ai-execution-sinks.d.ts +18 -0
  66. package/dist/layer2/ai-execution-sinks.d.ts.map +1 -0
  67. package/dist/layer2/ai-execution-sinks.js +496 -0
  68. package/dist/layer2/ai-execution-sinks.js.map +1 -0
  69. package/dist/layer2/ai-fingerprinting.d.ts +7 -0
  70. package/dist/layer2/ai-fingerprinting.d.ts.map +1 -0
  71. package/dist/layer2/ai-fingerprinting.js +654 -0
  72. package/dist/layer2/ai-fingerprinting.js.map +1 -0
  73. package/dist/layer2/ai-prompt-hygiene.d.ts +19 -0
  74. package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -0
  75. package/dist/layer2/ai-prompt-hygiene.js +356 -0
  76. package/dist/layer2/ai-prompt-hygiene.js.map +1 -0
  77. package/dist/layer2/ai-rag-safety.d.ts +21 -0
  78. package/dist/layer2/ai-rag-safety.d.ts.map +1 -0
  79. package/dist/layer2/ai-rag-safety.js +459 -0
  80. package/dist/layer2/ai-rag-safety.js.map +1 -0
  81. package/dist/layer2/ai-schema-validation.d.ts +25 -0
  82. package/dist/layer2/ai-schema-validation.d.ts.map +1 -0
  83. package/dist/layer2/ai-schema-validation.js +375 -0
  84. package/dist/layer2/ai-schema-validation.js.map +1 -0
  85. package/dist/layer2/auth-antipatterns.d.ts +20 -0
  86. package/dist/layer2/auth-antipatterns.d.ts.map +1 -0
  87. package/dist/layer2/auth-antipatterns.js +333 -0
  88. package/dist/layer2/auth-antipatterns.js.map +1 -0
  89. package/dist/layer2/byok-patterns.d.ts +12 -0
  90. package/dist/layer2/byok-patterns.d.ts.map +1 -0
  91. package/dist/layer2/byok-patterns.js +299 -0
  92. package/dist/layer2/byok-patterns.js.map +1 -0
  93. package/dist/layer2/dangerous-functions.d.ts +7 -0
  94. package/dist/layer2/dangerous-functions.d.ts.map +1 -0
  95. package/dist/layer2/dangerous-functions.js +1375 -0
  96. package/dist/layer2/dangerous-functions.js.map +1 -0
  97. package/dist/layer2/data-exposure.d.ts +16 -0
  98. package/dist/layer2/data-exposure.d.ts.map +1 -0
  99. package/dist/layer2/data-exposure.js +279 -0
  100. package/dist/layer2/data-exposure.js.map +1 -0
  101. package/dist/layer2/framework-checks.d.ts +7 -0
  102. package/dist/layer2/framework-checks.d.ts.map +1 -0
  103. package/dist/layer2/framework-checks.js +388 -0
  104. package/dist/layer2/framework-checks.js.map +1 -0
  105. package/dist/layer2/index.d.ts +58 -0
  106. package/dist/layer2/index.d.ts.map +1 -0
  107. package/dist/layer2/index.js +380 -0
  108. package/dist/layer2/index.js.map +1 -0
  109. package/dist/layer2/logic-gates.d.ts +7 -0
  110. package/dist/layer2/logic-gates.d.ts.map +1 -0
  111. package/dist/layer2/logic-gates.js +182 -0
  112. package/dist/layer2/logic-gates.js.map +1 -0
  113. package/dist/layer2/risky-imports.d.ts +7 -0
  114. package/dist/layer2/risky-imports.d.ts.map +1 -0
  115. package/dist/layer2/risky-imports.js +161 -0
  116. package/dist/layer2/risky-imports.js.map +1 -0
  117. package/dist/layer2/variables.d.ts +8 -0
  118. package/dist/layer2/variables.d.ts.map +1 -0
  119. package/dist/layer2/variables.js +152 -0
  120. package/dist/layer2/variables.js.map +1 -0
  121. package/dist/layer3/anthropic.d.ts +83 -0
  122. package/dist/layer3/anthropic.d.ts.map +1 -0
  123. package/dist/layer3/anthropic.js +1745 -0
  124. package/dist/layer3/anthropic.js.map +1 -0
  125. package/dist/layer3/index.d.ts +24 -0
  126. package/dist/layer3/index.d.ts.map +1 -0
  127. package/dist/layer3/index.js +119 -0
  128. package/dist/layer3/index.js.map +1 -0
  129. package/dist/layer3/openai.d.ts +25 -0
  130. package/dist/layer3/openai.d.ts.map +1 -0
  131. package/dist/layer3/openai.js +238 -0
  132. package/dist/layer3/openai.js.map +1 -0
  133. package/dist/layer3/package-check.d.ts +63 -0
  134. package/dist/layer3/package-check.d.ts.map +1 -0
  135. package/dist/layer3/package-check.js +508 -0
  136. package/dist/layer3/package-check.js.map +1 -0
  137. package/dist/modes/incremental.d.ts +66 -0
  138. package/dist/modes/incremental.d.ts.map +1 -0
  139. package/dist/modes/incremental.js +200 -0
  140. package/dist/modes/incremental.js.map +1 -0
  141. package/dist/tiers.d.ts +125 -0
  142. package/dist/tiers.d.ts.map +1 -0
  143. package/dist/tiers.js +234 -0
  144. package/dist/tiers.js.map +1 -0
  145. package/dist/types.d.ts +175 -0
  146. package/dist/types.d.ts.map +1 -0
  147. package/dist/types.js +50 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/utils/auth-helper-detector.d.ts +56 -0
  150. package/dist/utils/auth-helper-detector.d.ts.map +1 -0
  151. package/dist/utils/auth-helper-detector.js +360 -0
  152. package/dist/utils/auth-helper-detector.js.map +1 -0
  153. package/dist/utils/context-helpers.d.ts +96 -0
  154. package/dist/utils/context-helpers.d.ts.map +1 -0
  155. package/dist/utils/context-helpers.js +493 -0
  156. package/dist/utils/context-helpers.js.map +1 -0
  157. package/dist/utils/diff-detector.d.ts +53 -0
  158. package/dist/utils/diff-detector.d.ts.map +1 -0
  159. package/dist/utils/diff-detector.js +104 -0
  160. package/dist/utils/diff-detector.js.map +1 -0
  161. package/dist/utils/diff-parser.d.ts +80 -0
  162. package/dist/utils/diff-parser.d.ts.map +1 -0
  163. package/dist/utils/diff-parser.js +202 -0
  164. package/dist/utils/diff-parser.js.map +1 -0
  165. package/dist/utils/imported-auth-detector.d.ts +37 -0
  166. package/dist/utils/imported-auth-detector.d.ts.map +1 -0
  167. package/dist/utils/imported-auth-detector.js +251 -0
  168. package/dist/utils/imported-auth-detector.js.map +1 -0
  169. package/dist/utils/middleware-detector.d.ts +55 -0
  170. package/dist/utils/middleware-detector.d.ts.map +1 -0
  171. package/dist/utils/middleware-detector.js +260 -0
  172. package/dist/utils/middleware-detector.js.map +1 -0
  173. package/dist/utils/oauth-flow-detector.d.ts +41 -0
  174. package/dist/utils/oauth-flow-detector.d.ts.map +1 -0
  175. package/dist/utils/oauth-flow-detector.js +202 -0
  176. package/dist/utils/oauth-flow-detector.js.map +1 -0
  177. package/dist/utils/path-exclusions.d.ts +55 -0
  178. package/dist/utils/path-exclusions.d.ts.map +1 -0
  179. package/dist/utils/path-exclusions.js +222 -0
  180. package/dist/utils/path-exclusions.js.map +1 -0
  181. package/dist/utils/project-context-builder.d.ts +119 -0
  182. package/dist/utils/project-context-builder.d.ts.map +1 -0
  183. package/dist/utils/project-context-builder.js +534 -0
  184. package/dist/utils/project-context-builder.js.map +1 -0
  185. package/dist/utils/registry-clients.d.ts +93 -0
  186. package/dist/utils/registry-clients.d.ts.map +1 -0
  187. package/dist/utils/registry-clients.js +273 -0
  188. package/dist/utils/registry-clients.js.map +1 -0
  189. package/dist/utils/trpc-analyzer.d.ts +78 -0
  190. package/dist/utils/trpc-analyzer.d.ts.map +1 -0
  191. package/dist/utils/trpc-analyzer.js +297 -0
  192. package/dist/utils/trpc-analyzer.js.map +1 -0
  193. package/package.json +45 -0
  194. package/src/__tests__/benchmark/fixtures/false-positives.ts +227 -0
  195. package/src/__tests__/benchmark/fixtures/index.ts +68 -0
  196. package/src/__tests__/benchmark/fixtures/layer1/config-audit.ts +364 -0
  197. package/src/__tests__/benchmark/fixtures/layer1/hardcoded-secrets.ts +173 -0
  198. package/src/__tests__/benchmark/fixtures/layer1/high-entropy.ts +234 -0
  199. package/src/__tests__/benchmark/fixtures/layer1/index.ts +31 -0
  200. package/src/__tests__/benchmark/fixtures/layer1/sensitive-urls.ts +90 -0
  201. package/src/__tests__/benchmark/fixtures/layer1/weak-crypto.ts +197 -0
  202. package/src/__tests__/benchmark/fixtures/layer2/ai-agent-tools.ts +170 -0
  203. package/src/__tests__/benchmark/fixtures/layer2/ai-endpoint-protection.ts +418 -0
  204. package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +189 -0
  205. package/src/__tests__/benchmark/fixtures/layer2/ai-fingerprinting.ts +316 -0
  206. package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +178 -0
  207. package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +184 -0
  208. package/src/__tests__/benchmark/fixtures/layer2/ai-schema-validation.ts +434 -0
  209. package/src/__tests__/benchmark/fixtures/layer2/auth-antipatterns.ts +159 -0
  210. package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +112 -0
  211. package/src/__tests__/benchmark/fixtures/layer2/dangerous-functions.ts +246 -0
  212. package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +168 -0
  213. package/src/__tests__/benchmark/fixtures/layer2/framework-checks.ts +346 -0
  214. package/src/__tests__/benchmark/fixtures/layer2/index.ts +67 -0
  215. package/src/__tests__/benchmark/fixtures/layer2/injection-vulnerabilities.ts +239 -0
  216. package/src/__tests__/benchmark/fixtures/layer2/logic-gates.ts +246 -0
  217. package/src/__tests__/benchmark/fixtures/layer2/risky-imports.ts +231 -0
  218. package/src/__tests__/benchmark/fixtures/layer2/variables.ts +167 -0
  219. package/src/__tests__/benchmark/index.ts +29 -0
  220. package/src/__tests__/benchmark/run-benchmark.ts +144 -0
  221. package/src/__tests__/benchmark/run-depth-validation.ts +206 -0
  222. package/src/__tests__/benchmark/run-real-world-test.ts +243 -0
  223. package/src/__tests__/benchmark/security-benchmark-script.ts +1737 -0
  224. package/src/__tests__/benchmark/tier-integration-script.ts +177 -0
  225. package/src/__tests__/benchmark/types.ts +144 -0
  226. package/src/__tests__/benchmark/utils/test-runner.ts +475 -0
  227. package/src/__tests__/regression/known-false-positives.test.ts +467 -0
  228. package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +178 -0
  229. package/src/__tests__/snapshots/scan-depth.test.ts +258 -0
  230. package/src/__tests__/validation/analyze-results.ts +542 -0
  231. package/src/__tests__/validation/extract-for-triage.ts +146 -0
  232. package/src/__tests__/validation/fp-deep-analysis.ts +327 -0
  233. package/src/__tests__/validation/run-validation.ts +364 -0
  234. package/src/__tests__/validation/triage-template.md +132 -0
  235. package/src/formatters/cli-terminal.ts +446 -0
  236. package/src/formatters/github-comment.ts +382 -0
  237. package/src/formatters/grouping.ts +190 -0
  238. package/src/formatters/index.ts +47 -0
  239. package/src/formatters/vscode-diagnostic.ts +243 -0
  240. package/src/index.ts +823 -0
  241. package/src/layer1/comments.ts +218 -0
  242. package/src/layer1/config-audit.ts +289 -0
  243. package/src/layer1/entropy.ts +583 -0
  244. package/src/layer1/file-flags.ts +127 -0
  245. package/src/layer1/index.ts +181 -0
  246. package/src/layer1/patterns.ts +516 -0
  247. package/src/layer1/urls.ts +334 -0
  248. package/src/layer1/weak-crypto.ts +328 -0
  249. package/src/layer2/ai-agent-tools.ts +601 -0
  250. package/src/layer2/ai-endpoint-protection.ts +387 -0
  251. package/src/layer2/ai-execution-sinks.ts +580 -0
  252. package/src/layer2/ai-fingerprinting.ts +758 -0
  253. package/src/layer2/ai-prompt-hygiene.ts +411 -0
  254. package/src/layer2/ai-rag-safety.ts +511 -0
  255. package/src/layer2/ai-schema-validation.ts +421 -0
  256. package/src/layer2/auth-antipatterns.ts +394 -0
  257. package/src/layer2/byok-patterns.ts +336 -0
  258. package/src/layer2/dangerous-functions.ts +1563 -0
  259. package/src/layer2/data-exposure.ts +315 -0
  260. package/src/layer2/framework-checks.ts +433 -0
  261. package/src/layer2/index.ts +473 -0
  262. package/src/layer2/logic-gates.ts +206 -0
  263. package/src/layer2/risky-imports.ts +186 -0
  264. package/src/layer2/variables.ts +166 -0
  265. package/src/layer3/anthropic.ts +2030 -0
  266. package/src/layer3/index.ts +130 -0
  267. package/src/layer3/package-check.ts +604 -0
  268. package/src/modes/incremental.ts +293 -0
  269. package/src/tiers.ts +318 -0
  270. package/src/types.ts +284 -0
  271. package/src/utils/auth-helper-detector.ts +443 -0
  272. package/src/utils/context-helpers.ts +535 -0
  273. package/src/utils/diff-detector.ts +135 -0
  274. package/src/utils/diff-parser.ts +272 -0
  275. package/src/utils/imported-auth-detector.ts +320 -0
  276. package/src/utils/middleware-detector.ts +333 -0
  277. package/src/utils/oauth-flow-detector.ts +246 -0
  278. package/src/utils/path-exclusions.ts +266 -0
  279. package/src/utils/project-context-builder.ts +707 -0
  280. package/src/utils/registry-clients.ts +351 -0
  281. package/src/utils/trpc-analyzer.ts +382 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Path Exclusion Utility
3
+ * Provides configurable exclusion of test files, seed files, examples, and fixtures
4
+ * to reduce false positives in security scans.
5
+ */
6
+
7
+ import type { Vulnerability, VulnerabilityCategory } from '../types'
8
+
9
+ export interface ExclusionConfig {
10
+ testPatterns: string[]
11
+ seedPatterns: string[]
12
+ examplePatterns: string[]
13
+ fixturePatterns: string[]
14
+ }
15
+
16
+ const DEFAULT_EXCLUSIONS: ExclusionConfig = {
17
+ testPatterns: [
18
+ '**/*.spec.ts',
19
+ '**/*.spec.js',
20
+ '**/*.test.ts',
21
+ '**/*.test.js',
22
+ '**/test/**',
23
+ '**/tests/**',
24
+ '**/__tests__/**',
25
+ '**/e2e/**',
26
+ '**/app-tests/**',
27
+ '**/*.spec.tsx',
28
+ '**/*.spec.jsx',
29
+ '**/*.test.tsx',
30
+ '**/*.test.jsx',
31
+ '**/cypress/**',
32
+ '**/playwright/**',
33
+ ],
34
+ seedPatterns: [
35
+ '**/seed/**',
36
+ '**/seeds/**',
37
+ '**/prisma/seed/**',
38
+ '**/prisma/seed.ts',
39
+ '**/prisma/seed.js',
40
+ '**/db/seed/**',
41
+ '**/database/seed/**',
42
+ ],
43
+ examplePatterns: [
44
+ '**/examples/**',
45
+ '**/demo/**',
46
+ '**/sample/**',
47
+ '**/samples/**',
48
+ '**/playground/**',
49
+ ],
50
+ fixturePatterns: [
51
+ '**/fixtures/**',
52
+ '**/mocks/**',
53
+ '**/__mocks__/**',
54
+ '**/stubs/**',
55
+ '**/__fixtures__/**',
56
+ ],
57
+ }
58
+
59
+ /**
60
+ * Categories that should be SUPPRESSED in test/seed/example files
61
+ * These are typically false positives in non-production code
62
+ */
63
+ const SUPPRESSIBLE_CATEGORIES: VulnerabilityCategory[] = [
64
+ 'weak_crypto', // Math.random in tests is fine
65
+ 'dangerous_function', // regex, eval in tests often intentional
66
+ 'hardcoded_secret', // test fixtures have fake secrets
67
+ 'high_entropy_string', // test data often looks like secrets
68
+ 'sensitive_url', // localhost in test configs
69
+ 'sensitive_variable', // test variables named 'password' etc.
70
+ 'insecure_config', // test configs are intentionally loose
71
+ 'ai_pattern', // AI patterns in test code
72
+ ]
73
+
74
+ /**
75
+ * Categories that should ALWAYS be scanned, even in test files
76
+ * These represent real vulnerabilities that matter in test code too
77
+ */
78
+ const ALWAYS_SCAN_CATEGORIES: VulnerabilityCategory[] = [
79
+ 'sql_injection',
80
+ 'command_injection',
81
+ 'xss',
82
+ 'path_traversal' as VulnerabilityCategory, // Cast since not in original types
83
+ 'missing_auth', // Keep but may downgrade
84
+ ]
85
+
86
+ export type ExclusionReason = 'test_file' | 'seed_file' | 'example_file' | 'fixture_file' | null
87
+
88
+ /**
89
+ * Convert glob pattern to regex for matching
90
+ */
91
+ function globToRegex(pattern: string): RegExp {
92
+ // Escape special regex characters except * and **
93
+ let regexStr = pattern
94
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
95
+ .replace(/\*\*/g, '{{GLOBSTAR}}')
96
+ .replace(/\*/g, '[^/]*')
97
+ .replace(/\{\{GLOBSTAR\}\}/g, '.*')
98
+
99
+ return new RegExp(regexStr, 'i')
100
+ }
101
+
102
+ /**
103
+ * Check if a file path matches any pattern in a list
104
+ */
105
+ function matchesPatterns(filePath: string, patterns: string[]): boolean {
106
+ // Normalize path separators
107
+ const normalizedPath = filePath.replace(/\\/g, '/')
108
+
109
+ return patterns.some(pattern => {
110
+ const regex = globToRegex(pattern)
111
+ return regex.test(normalizedPath)
112
+ })
113
+ }
114
+
115
+ /**
116
+ * Check if a file path should be excluded from scanning
117
+ * Returns the exclusion reason or null if not excluded
118
+ */
119
+ export function getExclusionReason(
120
+ filePath: string,
121
+ config: ExclusionConfig = DEFAULT_EXCLUSIONS
122
+ ): ExclusionReason {
123
+ const normalizedPath = filePath.replace(/\\/g, '/')
124
+
125
+ // Check test patterns
126
+ if (matchesPatterns(normalizedPath, config.testPatterns)) {
127
+ return 'test_file'
128
+ }
129
+
130
+ // Check seed patterns
131
+ if (matchesPatterns(normalizedPath, config.seedPatterns)) {
132
+ return 'seed_file'
133
+ }
134
+
135
+ // Check example patterns
136
+ if (matchesPatterns(normalizedPath, config.examplePatterns)) {
137
+ return 'example_file'
138
+ }
139
+
140
+ // Check fixture patterns
141
+ if (matchesPatterns(normalizedPath, config.fixturePatterns)) {
142
+ return 'fixture_file'
143
+ }
144
+
145
+ return null
146
+ }
147
+
148
+ /**
149
+ * Check if a file path should be excluded
150
+ */
151
+ export function isExcludedPath(
152
+ filePath: string,
153
+ config?: Partial<ExclusionConfig>
154
+ ): boolean {
155
+ const mergedConfig: ExclusionConfig = {
156
+ ...DEFAULT_EXCLUSIONS,
157
+ ...config,
158
+ }
159
+
160
+ return getExclusionReason(filePath, mergedConfig) !== null
161
+ }
162
+
163
+ /**
164
+ * Determine if a finding should be suppressed based on file path and category
165
+ *
166
+ * @param finding - The vulnerability finding
167
+ * @param exclusionReason - The reason the file is excluded (if any)
168
+ * @returns true if the finding should be suppressed
169
+ */
170
+ export function shouldSuppressFinding(
171
+ finding: Vulnerability,
172
+ exclusionReason: ExclusionReason
173
+ ): boolean {
174
+ // If file is not excluded, don't suppress
175
+ if (!exclusionReason) {
176
+ return false
177
+ }
178
+
179
+ // Always scan certain critical categories even in test files
180
+ if (ALWAYS_SCAN_CATEGORIES.includes(finding.category)) {
181
+ return false
182
+ }
183
+
184
+ // Suppress suppressible categories in excluded files
185
+ if (SUPPRESSIBLE_CATEGORIES.includes(finding.category)) {
186
+ return true
187
+ }
188
+
189
+ // Default: don't suppress
190
+ return false
191
+ }
192
+
193
+ /**
194
+ * Get a human-readable description of the exclusion reason
195
+ */
196
+ export function getExclusionDescription(reason: ExclusionReason): string {
197
+ switch (reason) {
198
+ case 'test_file':
199
+ return 'Finding in test file'
200
+ case 'seed_file':
201
+ return 'Finding in seed/fixture data file'
202
+ case 'example_file':
203
+ return 'Finding in example/demo file'
204
+ case 'fixture_file':
205
+ return 'Finding in mock/fixture file'
206
+ default:
207
+ return ''
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Filter findings based on path exclusions
213
+ * Returns findings that should be kept and a list of suppressed findings with reasons
214
+ */
215
+ export function filterFindingsByPath(
216
+ findings: Vulnerability[],
217
+ config?: Partial<ExclusionConfig>
218
+ ): {
219
+ kept: Vulnerability[]
220
+ suppressed: Array<{ finding: Vulnerability; reason: ExclusionReason; description: string }>
221
+ } {
222
+ const mergedConfig: ExclusionConfig = {
223
+ ...DEFAULT_EXCLUSIONS,
224
+ ...config,
225
+ }
226
+
227
+ const kept: Vulnerability[] = []
228
+ const suppressed: Array<{ finding: Vulnerability; reason: ExclusionReason; description: string }> = []
229
+
230
+ for (const finding of findings) {
231
+ const exclusionReason = getExclusionReason(finding.filePath, mergedConfig)
232
+
233
+ if (shouldSuppressFinding(finding, exclusionReason)) {
234
+ suppressed.push({
235
+ finding,
236
+ reason: exclusionReason,
237
+ description: getExclusionDescription(exclusionReason),
238
+ })
239
+ } else {
240
+ kept.push(finding)
241
+ }
242
+ }
243
+
244
+ return { kept, suppressed }
245
+ }
246
+
247
+ /**
248
+ * Get the default exclusion configuration
249
+ */
250
+ export function getDefaultExclusionConfig(): ExclusionConfig {
251
+ return { ...DEFAULT_EXCLUSIONS }
252
+ }
253
+
254
+ /**
255
+ * Merge custom exclusion patterns with defaults
256
+ */
257
+ export function mergeExclusionConfig(
258
+ custom: Partial<ExclusionConfig>
259
+ ): ExclusionConfig {
260
+ return {
261
+ testPatterns: [...DEFAULT_EXCLUSIONS.testPatterns, ...(custom.testPatterns || [])],
262
+ seedPatterns: [...DEFAULT_EXCLUSIONS.seedPatterns, ...(custom.seedPatterns || [])],
263
+ examplePatterns: [...DEFAULT_EXCLUSIONS.examplePatterns, ...(custom.examplePatterns || [])],
264
+ fixturePatterns: [...DEFAULT_EXCLUSIONS.fixturePatterns, ...(custom.fixturePatterns || [])],
265
+ }
266
+ }