@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,127 @@
1
+ /**
2
+ * Layer 1: File Extension Red Flags
3
+ * Detects dangerous files that should not be in a repository
4
+ */
5
+
6
+ import type { Vulnerability } from '../types'
7
+
8
+ // Dangerous file extensions and names that should not be committed
9
+ const DANGEROUS_FILE_PATTERNS = [
10
+ // Private keys
11
+ { pattern: /\.pem$/i, name: 'PEM Private Key', severity: 'critical' as const },
12
+ { pattern: /\.key$/i, name: 'Private Key File', severity: 'critical' as const },
13
+ { pattern: /\.p12$/i, name: 'PKCS#12 Certificate', severity: 'critical' as const },
14
+ { pattern: /\.pfx$/i, name: 'PFX Certificate', severity: 'critical' as const },
15
+ { pattern: /\.p8$/i, name: 'PKCS#8 Private Key', severity: 'critical' as const },
16
+
17
+ // SSH keys
18
+ { pattern: /id_rsa$/i, name: 'RSA SSH Private Key', severity: 'critical' as const },
19
+ { pattern: /id_dsa$/i, name: 'DSA SSH Private Key', severity: 'critical' as const },
20
+ { pattern: /id_ecdsa$/i, name: 'ECDSA SSH Private Key', severity: 'critical' as const },
21
+ { pattern: /id_ed25519$/i, name: 'Ed25519 SSH Private Key', severity: 'critical' as const },
22
+
23
+ // Environment files (should be gitignored)
24
+ { pattern: /^\.env$/i, name: 'Environment File', severity: 'critical' as const },
25
+ { pattern: /^\.env\.local$/i, name: 'Local Environment File', severity: 'critical' as const },
26
+ { pattern: /^\.env\.production$/i, name: 'Production Environment File', severity: 'critical' as const },
27
+ { pattern: /^\.env\.development$/i, name: 'Development Environment File', severity: 'high' as const },
28
+ { pattern: /^\.env\.staging$/i, name: 'Staging Environment File', severity: 'high' as const },
29
+
30
+ // Credential files
31
+ { pattern: /\.pgpass$/i, name: 'PostgreSQL Password File', severity: 'critical' as const },
32
+ { pattern: /\.npmrc$/i, name: 'NPM Configuration (may contain tokens)', severity: 'high' as const },
33
+ { pattern: /\.pypirc$/i, name: 'PyPI Configuration (may contain tokens)', severity: 'high' as const },
34
+ { pattern: /\.netrc$/i, name: 'Netrc File (contains credentials)', severity: 'critical' as const },
35
+ { pattern: /\.htpasswd$/i, name: 'Apache Password File', severity: 'critical' as const },
36
+
37
+ // Database files
38
+ { pattern: /\.sqlite$/i, name: 'SQLite Database', severity: 'medium' as const },
39
+ { pattern: /\.sqlite3$/i, name: 'SQLite Database', severity: 'medium' as const },
40
+ { pattern: /\.db$/i, name: 'Database File', severity: 'medium' as const },
41
+
42
+ // Backup files that may contain secrets
43
+ { pattern: /\.bak$/i, name: 'Backup File', severity: 'low' as const },
44
+ { pattern: /\.backup$/i, name: 'Backup File', severity: 'low' as const },
45
+ { pattern: /\.old$/i, name: 'Old File Backup', severity: 'low' as const },
46
+
47
+ // Keystore files
48
+ { pattern: /\.jks$/i, name: 'Java Keystore', severity: 'high' as const },
49
+ { pattern: /\.keystore$/i, name: 'Keystore File', severity: 'high' as const },
50
+ ]
51
+
52
+ // Files that are okay in certain contexts but should be reviewed
53
+ const CONTEXT_SENSITIVE_PATTERNS = [
54
+ { pattern: /\.env\.example$/i, name: 'Environment Example File', checkContent: true },
55
+ { pattern: /\.env\.sample$/i, name: 'Environment Sample File', checkContent: true },
56
+ { pattern: /\.env\.template$/i, name: 'Environment Template File', checkContent: true },
57
+ ]
58
+
59
+ export function detectDangerousFiles(
60
+ content: string,
61
+ filePath: string
62
+ ): Vulnerability[] {
63
+ const vulnerabilities: Vulnerability[] = []
64
+ const fileName = filePath.split('/').pop() || ''
65
+
66
+ // Check for dangerous file patterns
67
+ for (const { pattern, name, severity } of DANGEROUS_FILE_PATTERNS) {
68
+ if (pattern.test(fileName) || pattern.test(filePath)) {
69
+ vulnerabilities.push({
70
+ id: `file-flag-${filePath}`,
71
+ filePath,
72
+ lineNumber: 1,
73
+ lineContent: `File: ${fileName}`,
74
+ severity,
75
+ category: 'dangerous_file',
76
+ title: `Dangerous file detected: ${name}`,
77
+ description: `This file type (${name}) should not be committed to version control as it may contain sensitive information.`,
78
+ suggestedFix: 'Add this file to .gitignore and remove it from the repository. If it contains secrets, rotate them immediately.',
79
+ confidence: 'high',
80
+ layer: 1,
81
+ })
82
+ }
83
+ }
84
+
85
+ // Check context-sensitive files for actual secrets
86
+ for (const { pattern, name, checkContent } of CONTEXT_SENSITIVE_PATTERNS) {
87
+ if ((pattern.test(fileName) || pattern.test(filePath)) && checkContent) {
88
+ // Check if the example/sample file contains what looks like real values
89
+ const suspiciousPatterns = [
90
+ /[a-zA-Z0-9_]+=sk-[a-zA-Z0-9]{20,}/, // API keys
91
+ /[a-zA-Z0-9_]+=ghp_[a-zA-Z0-9]{36,}/, // GitHub tokens
92
+ /[a-zA-Z0-9_]+=eyJ[a-zA-Z0-9_-]+\./, // JWT tokens
93
+ /password\s*=\s*[^\s$]{8,}/i, // Passwords (not env vars)
94
+ ]
95
+
96
+ const lines = content.split('\n')
97
+ for (let i = 0; i < lines.length; i++) {
98
+ const line = lines[i]
99
+ // Skip lines that reference environment variables
100
+ if (line.includes('${') || line.includes('$env:') || line.includes('process.env')) {
101
+ continue
102
+ }
103
+
104
+ for (const suspicious of suspiciousPatterns) {
105
+ if (suspicious.test(line)) {
106
+ vulnerabilities.push({
107
+ id: `file-flag-content-${filePath}-${i + 1}`,
108
+ filePath,
109
+ lineNumber: i + 1,
110
+ lineContent: line.trim(),
111
+ severity: 'high',
112
+ category: 'dangerous_file',
113
+ title: `${name} may contain real secrets`,
114
+ description: 'This example/sample file appears to contain real secret values instead of placeholders.',
115
+ suggestedFix: 'Replace real values with placeholders like YOUR_API_KEY_HERE or use environment variable references.',
116
+ confidence: 'medium',
117
+ layer: 1,
118
+ })
119
+ break
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ return vulnerabilities
127
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Layer 1: Surface Scan
3
+ * Fast, deterministic scanning using entropy, patterns, config auditing,
4
+ * file flags, comment analysis, URL detection, and weak crypto detection
5
+ */
6
+
7
+ import type { Vulnerability, ScanFile } from '../types'
8
+ import { detectHighEntropyStrings } from './entropy'
9
+ import { detectKnownPatterns } from './patterns'
10
+ import { auditConfiguration } from './config-audit'
11
+ import { detectDangerousFiles } from './file-flags'
12
+ import { detectAICommentPatterns } from './comments'
13
+ import { detectSensitiveURLs } from './urls'
14
+ import { detectWeakCrypto } from './weak-crypto'
15
+ import {
16
+ type TierStats,
17
+ computeTierStats,
18
+ formatTierStats,
19
+ getLayer1DetectorTier,
20
+ type Layer1DetectorName,
21
+ } from '../tiers'
22
+ import {
23
+ filterFindingsByPath,
24
+ type ExclusionConfig,
25
+ } from '../utils/path-exclusions'
26
+
27
+ /**
28
+ * Layer 1 detector stats for raw finding counts before deduplication
29
+ */
30
+ export interface Layer1Stats {
31
+ /** Raw finding counts per detector (before dedupe) */
32
+ raw: Record<string, number>
33
+ /** Deduped finding counts per category */
34
+ deduped: Record<string, number>
35
+ /** Tier breakdown of deduped findings */
36
+ tiers: TierStats
37
+ /** Number of findings suppressed by path exclusions */
38
+ suppressedByPath: number
39
+ }
40
+
41
+ export interface Layer1Result {
42
+ vulnerabilities: Vulnerability[]
43
+ filesScanned: number
44
+ duration: number
45
+ /** Heuristic breakdown stats for noise analysis */
46
+ stats: Layer1Stats
47
+ }
48
+
49
+ export async function runLayer1Scan(files: ScanFile[]): Promise<Layer1Result> {
50
+ const startTime = Date.now()
51
+ const vulnerabilities: Vulnerability[] = []
52
+
53
+ // Track raw counts per detector (before dedupe)
54
+ const rawStats: Record<Layer1DetectorName, number> = {
55
+ known_secrets: 0,
56
+ weak_crypto: 0,
57
+ sensitive_urls: 0,
58
+ entropy: 0,
59
+ config_audit: 0,
60
+ file_flags: 0,
61
+ ai_comments: 0,
62
+ }
63
+
64
+ for (const file of files) {
65
+ // Run all Layer 1 detectors and track raw counts
66
+ const entropyFindings = detectHighEntropyStrings(file.content, file.path)
67
+ const patternFindings = detectKnownPatterns(file.content, file.path)
68
+ const configFindings = auditConfiguration(file.content, file.path)
69
+ const fileFlags = detectDangerousFiles(file.content, file.path)
70
+ const commentFindings = detectAICommentPatterns(file.content, file.path)
71
+ const urlFindings = detectSensitiveURLs(file.content, file.path)
72
+ const cryptoFindings = detectWeakCrypto(file.content, file.path)
73
+
74
+ rawStats.entropy += entropyFindings.length
75
+ rawStats.known_secrets += patternFindings.length
76
+ rawStats.config_audit += configFindings.length
77
+ rawStats.file_flags += fileFlags.length
78
+ rawStats.ai_comments += commentFindings.length
79
+ rawStats.sensitive_urls += urlFindings.length
80
+ rawStats.weak_crypto += cryptoFindings.length
81
+
82
+ vulnerabilities.push(
83
+ ...entropyFindings,
84
+ ...patternFindings,
85
+ ...configFindings,
86
+ ...fileFlags,
87
+ ...commentFindings,
88
+ ...urlFindings,
89
+ ...cryptoFindings
90
+ )
91
+ }
92
+
93
+ // Deduplicate findings (same line might be caught by multiple detectors)
94
+ const dedupedVulnerabilities = deduplicateFindings(vulnerabilities)
95
+
96
+ // Apply path exclusions to filter out findings in test/seed/example files
97
+ const { kept: uniqueVulnerabilities, suppressed } = filterFindingsByPath(dedupedVulnerabilities)
98
+
99
+ // Log suppressed findings
100
+ if (suppressed.length > 0) {
101
+ const byReason: Record<string, number> = {}
102
+ for (const s of suppressed) {
103
+ const reason = s.reason || 'unknown'
104
+ byReason[reason] = (byReason[reason] || 0) + 1
105
+ }
106
+ console.log(`[Layer 1] Suppressed ${suppressed.length} findings in test/seed/example files:`)
107
+ for (const [reason, count] of Object.entries(byReason)) {
108
+ console.log(` - ${reason}: ${count}`)
109
+ }
110
+ }
111
+
112
+ // Compute deduped counts per category
113
+ const dedupedStats: Record<string, number> = {}
114
+ for (const vuln of uniqueVulnerabilities) {
115
+ const cat = vuln.category
116
+ dedupedStats[cat] = (dedupedStats[cat] || 0) + 1
117
+ }
118
+
119
+ // Compute tier breakdown (all Layer 1 findings have layer: 1)
120
+ const tierStats = computeTierStats(
121
+ uniqueVulnerabilities.map(v => ({ category: v.category, layer: 1 as const }))
122
+ )
123
+
124
+ // Log heuristic breakdown with tier info
125
+ console.log('[Layer 1] Heuristic breakdown (raw findings before dedupe):')
126
+ for (const [name, count] of Object.entries(rawStats)) {
127
+ if (count > 0) {
128
+ const tier = getLayer1DetectorTier(name as Layer1DetectorName)
129
+ console.log(` - ${name}: ${count} (${tier})`)
130
+ }
131
+ }
132
+ console.log(`[Layer 1] Tier breakdown (after dedupe): ${formatTierStats(tierStats)}`)
133
+
134
+ return {
135
+ vulnerabilities: uniqueVulnerabilities,
136
+ filesScanned: files.length,
137
+ duration: Date.now() - startTime,
138
+ stats: {
139
+ raw: rawStats,
140
+ deduped: dedupedStats,
141
+ tiers: tierStats,
142
+ suppressedByPath: suppressed.length,
143
+ },
144
+ }
145
+ }
146
+
147
+ // Remove duplicate findings on the same line
148
+ function deduplicateFindings(vulnerabilities: Vulnerability[]): Vulnerability[] {
149
+ const seen = new Map<string, Vulnerability>()
150
+
151
+ for (const vuln of vulnerabilities) {
152
+ const key = `${vuln.filePath}:${vuln.lineNumber}:${vuln.category}`
153
+ const existing = seen.get(key)
154
+
155
+ // Keep the higher severity finding
156
+ if (!existing || severityRank(vuln.severity) > severityRank(existing.severity)) {
157
+ seen.set(key, vuln)
158
+ }
159
+ }
160
+
161
+ return Array.from(seen.values())
162
+ }
163
+
164
+ function severityRank(severity: string): number {
165
+ const ranks: Record<string, number> = {
166
+ critical: 5,
167
+ high: 4,
168
+ medium: 3,
169
+ low: 2,
170
+ info: 1,
171
+ }
172
+ return ranks[severity] || 0
173
+ }
174
+
175
+ export { detectHighEntropyStrings } from './entropy'
176
+ export { detectKnownPatterns } from './patterns'
177
+ export { auditConfiguration } from './config-audit'
178
+ export { detectDangerousFiles } from './file-flags'
179
+ export { detectAICommentPatterns } from './comments'
180
+ export { detectSensitiveURLs } from './urls'
181
+ export { detectWeakCrypto } from './weak-crypto'