@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,434 @@
1
+ /**
2
+ * AI Schema Validation Test Fixtures
3
+ * Tests for detecting missing/weak validation of AI-generated outputs
4
+ */
5
+
6
+ import type { TestGroup } from '../../types'
7
+
8
+ export const aiSchemaValidationTests: TestGroup = {
9
+ name: 'AI Schema Validation',
10
+ tier: 'B',
11
+ layer: 2,
12
+ description: 'Detection of missing or weak schema validation on AI-generated outputs',
13
+
14
+ truePositives: [
15
+ {
16
+ name: 'Schema Validation - True Positives',
17
+ expectFindings: true,
18
+ expectedCategories: ['ai_schema_mismatch'],
19
+ description: 'Unvalidated AI output parsing that MUST be detected',
20
+ file: {
21
+ path: 'src/ai/chat-handler.ts',
22
+ content: `
23
+ import OpenAI from 'openai'
24
+
25
+ const openai = new OpenAI()
26
+
27
+ // Unvalidated JSON.parse of AI response - MEDIUM
28
+ export async function getStructuredResponse(prompt: string) {
29
+ const completion = await openai.chat.completions.create({
30
+ model: 'gpt-4',
31
+ messages: [{ role: 'user', content: prompt }],
32
+ })
33
+
34
+ // UNSAFE: No schema validation
35
+ const data = JSON.parse(completion.choices[0].message.content)
36
+ return data
37
+ }
38
+
39
+ // Tool call arguments parsed without validation - MEDIUM
40
+ export async function handleToolCall(toolCall: any) {
41
+ const args = JSON.parse(toolCall.function.arguments)
42
+ // Using args directly without validation
43
+ return processArgs(args)
44
+ }
45
+
46
+ // Anthropic response parsed directly - MEDIUM
47
+ export async function handleAnthropicResponse(response: any) {
48
+ const parsed = JSON.parse(response.content[0].text)
49
+ return parsed.action
50
+ }
51
+
52
+ // AI response typed as any - LOW
53
+ export async function processAIOutput() {
54
+ const response: any = await getAICompletion()
55
+ // Using untyped response
56
+ return response.result
57
+ }
58
+ `,
59
+ language: 'typescript',
60
+ size: 900,
61
+ },
62
+ },
63
+ {
64
+ name: 'Weak Schema Patterns',
65
+ expectFindings: true,
66
+ expectedCategories: ['ai_schema_mismatch'],
67
+ description: 'Permissive schema patterns that provide inadequate validation',
68
+ file: {
69
+ path: 'src/ai/weak-schemas.ts',
70
+ content: `
71
+ import { z } from 'zod'
72
+ import OpenAI from 'openai'
73
+
74
+ const openai = new OpenAI()
75
+
76
+ // Using z.any() defeats schema validation - LOW
77
+ const responseSchema = z.any()
78
+ const aiResponse = responseSchema.parse(completion.choices[0].message.content)
79
+
80
+ // Passthrough allowing extra properties - INFO
81
+ const dataSchema = z.object({
82
+ action: z.string(),
83
+ }).passthrough()
84
+
85
+ // Record<string, any> for AI data - LOW
86
+ interface AIData {
87
+ aiOutput: Record<string, any>
88
+ }
89
+
90
+ // Generic object type - INFO
91
+ function processResponse(completion: object) {
92
+ return completion
93
+ }
94
+
95
+ // Any typed response at API boundary - MEDIUM (elevated)
96
+ export async function handler(req: Request) {
97
+ const response: any = await openai.chat.completions.create({
98
+ model: 'gpt-4',
99
+ messages: [{ role: 'user', content: 'test' }],
100
+ })
101
+ return Response.json(response)
102
+ }
103
+ `,
104
+ language: 'typescript',
105
+ size: 700,
106
+ },
107
+ },
108
+ {
109
+ name: 'Tool Parameters in Security Sinks',
110
+ expectFindings: true,
111
+ expectedCategories: ['ai_schema_mismatch'],
112
+ description: 'AI-generated tool parameters used in dangerous operations',
113
+ file: {
114
+ path: 'src/ai/tool-execution.ts',
115
+ content: `
116
+ import { exec } from 'child_process'
117
+ import * as fs from 'fs'
118
+
119
+ // Tool parameter in shell command - CRITICAL
120
+ async function executeToolCommand(toolArgs: any) {
121
+ const command = toolArgs.command
122
+ exec(command, (error, stdout) => {
123
+ console.log(stdout)
124
+ })
125
+ }
126
+
127
+ // Tool parameter in file path - HIGH
128
+ async function readToolFile(args: { path: string }) {
129
+ const filePath = args.path
130
+ const content = fs.readFileSync(filePath, 'utf-8')
131
+ return content
132
+ }
133
+
134
+ // Tool parameter in database query - HIGH
135
+ async function queryFromTool(parameters: any, db: any) {
136
+ const tableName = parameters.table
137
+ const results = await db.query(\`SELECT * FROM \${tableName}\`)
138
+ return results
139
+ }
140
+
141
+ // Tool parameter in URL - HIGH
142
+ async function fetchToolUrl(args: any) {
143
+ const endpoint = args.url
144
+ const response = await fetch(endpoint)
145
+ return response.json()
146
+ }
147
+
148
+ // Function name routing without validation - MEDIUM
149
+ async function routeToolCall(tool_call: any) {
150
+ const name = tool_call.function.name
151
+ switch (name) {
152
+ case 'search': return search()
153
+ default: return functions[name]() // Dynamic access
154
+ }
155
+ }
156
+ `,
157
+ language: 'typescript',
158
+ size: 950,
159
+ },
160
+ },
161
+ ],
162
+
163
+ falseNegatives: [
164
+ {
165
+ name: 'Schema Validation - False Negatives',
166
+ expectFindings: false,
167
+ description: 'Properly validated AI outputs that should NOT be flagged',
168
+ allowedInfoFindings: [
169
+ {
170
+ category: 'ai_schema_mismatch',
171
+ maxCount: 2,
172
+ reason: 'Some patterns may generate info-level findings even when properly handled',
173
+ },
174
+ {
175
+ category: 'dangerous_function',
176
+ maxCount: 2,
177
+ reason: 'JSON.parse with schema validation nearby may still trigger info-level findings',
178
+ },
179
+ ],
180
+ file: {
181
+ path: 'src/ai/validated-handler.ts',
182
+ content: `
183
+ import OpenAI from 'openai'
184
+ import { z } from 'zod'
185
+
186
+ const openai = new OpenAI()
187
+
188
+ // Proper schema definition
189
+ const ResponseSchema = z.object({
190
+ action: z.enum(['search', 'create', 'delete']),
191
+ target: z.string().min(1),
192
+ params: z.record(z.string()).optional(),
193
+ }).strict()
194
+
195
+ // Validated AI response parsing - SAFE
196
+ export async function getValidatedResponse(prompt: string) {
197
+ const completion = await openai.chat.completions.create({
198
+ model: 'gpt-4',
199
+ messages: [{ role: 'user', content: prompt }],
200
+ })
201
+
202
+ // Schema validation with zod
203
+ const validated = ResponseSchema.parse(
204
+ JSON.parse(completion.choices[0].message.content!)
205
+ )
206
+
207
+ return validated
208
+ }
209
+
210
+ // safeParse with error handling - SAFE
211
+ export async function getSafeResponse(prompt: string) {
212
+ const raw = await getAICompletion(prompt)
213
+ const result = ResponseSchema.safeParse(JSON.parse(raw))
214
+
215
+ if (!result.success) {
216
+ throw new Error('Invalid AI response: ' + result.error.message)
217
+ }
218
+
219
+ return result.data
220
+ }
221
+
222
+ // Tool arguments with schema validation - SAFE
223
+ const ToolArgsSchema = z.object({
224
+ query: z.string(),
225
+ limit: z.number().int().positive().max(100),
226
+ })
227
+
228
+ export async function handleToolWithValidation(toolCall: any) {
229
+ const args = ToolArgsSchema.parse(
230
+ JSON.parse(toolCall.function.arguments)
231
+ )
232
+ return search(args.query, args.limit)
233
+ }
234
+
235
+ // OpenAI Structured Outputs - SAFE
236
+ export async function getStructuredOutput(prompt: string) {
237
+ const completion = await openai.chat.completions.create({
238
+ model: 'gpt-4',
239
+ messages: [{ role: 'user', content: prompt }],
240
+ response_format: {
241
+ type: 'json_schema',
242
+ json_schema: {
243
+ name: 'response',
244
+ schema: {
245
+ type: 'object',
246
+ properties: {
247
+ action: { type: 'string' },
248
+ target: { type: 'string' },
249
+ },
250
+ required: ['action', 'target'],
251
+ },
252
+ },
253
+ },
254
+ })
255
+
256
+ return JSON.parse(completion.choices[0].message.content!)
257
+ }
258
+ `,
259
+ language: 'typescript',
260
+ size: 1700,
261
+ },
262
+ },
263
+ {
264
+ name: 'Tool Parameters with Allowlist',
265
+ expectFindings: false,
266
+ description: 'Tool parameters validated against allowlists',
267
+ allowedInfoFindings: [
268
+ {
269
+ category: 'ai_schema_mismatch',
270
+ maxCount: 1,
271
+ reason: 'May flag routing pattern at info level',
272
+ },
273
+ {
274
+ category: 'dangerous_function',
275
+ maxCount: 2,
276
+ reason: 'fs operations may trigger info-level findings',
277
+ },
278
+ {
279
+ category: 'ai_unsafe_execution',
280
+ maxCount: 2,
281
+ reason: 'File operations with proper validation may still flag at low severity',
282
+ },
283
+ ],
284
+ file: {
285
+ path: 'src/ai/safe-tool-execution.ts',
286
+ content: `
287
+ import * as path from 'path'
288
+ import * as fs from 'fs'
289
+ import { z } from 'zod'
290
+
291
+ // Allowlisted commands - SAFE
292
+ const ALLOWED_COMMANDS = ['ls', 'cat', 'echo'] as const
293
+ const CommandSchema = z.enum(ALLOWED_COMMANDS)
294
+
295
+ async function executeAllowlistedCommand(args: { cmd: string }) {
296
+ const validated = CommandSchema.parse(args.cmd)
297
+ // Only allowed commands can execute
298
+ return runCommand(validated)
299
+ }
300
+
301
+ // Path validation with allowlist - SAFE
302
+ const ALLOWED_PATHS = ['/app/data', '/app/uploads']
303
+
304
+ async function readAllowlistedFile(args: { filePath: string }) {
305
+ const resolved = path.resolve(args.filePath)
306
+
307
+ // Validate path against allowlist
308
+ if (!ALLOWED_PATHS.some(p => resolved.startsWith(p))) {
309
+ throw new Error('Path not allowed')
310
+ }
311
+
312
+ return fs.readFileSync(resolved, 'utf-8')
313
+ }
314
+
315
+ // URL validation against allowed hosts - SAFE
316
+ const ALLOWED_HOSTS = ['api.example.com', 'data.example.com']
317
+
318
+ async function fetchAllowlistedUrl(args: { url: string }) {
319
+ const parsed = new URL(args.url)
320
+
321
+ if (!ALLOWED_HOSTS.includes(parsed.host)) {
322
+ throw new Error('Host not allowed')
323
+ }
324
+
325
+ return fetch(args.url)
326
+ }
327
+
328
+ // Function routing with explicit allowlist - SAFE
329
+ const ALLOWED_FUNCTIONS = ['search', 'calculate', 'format'] as const
330
+ type AllowedFunction = typeof ALLOWED_FUNCTIONS[number]
331
+
332
+ const handlers: Record<AllowedFunction, () => void> = {
333
+ search: () => {},
334
+ calculate: () => {},
335
+ format: () => {},
336
+ }
337
+
338
+ async function routeWithAllowlist(toolCall: { name: string }) {
339
+ const name = toolCall.name as AllowedFunction
340
+
341
+ if (!ALLOWED_FUNCTIONS.includes(name)) {
342
+ throw new Error('Function not allowed: ' + name)
343
+ }
344
+
345
+ return handlers[name]()
346
+ }
347
+ `,
348
+ language: 'typescript',
349
+ size: 1400,
350
+ },
351
+ },
352
+ {
353
+ name: 'Strongly Typed AI Interfaces',
354
+ expectFindings: false,
355
+ description: 'Properly typed AI responses with strict interfaces',
356
+ allowedInfoFindings: [
357
+ {
358
+ category: 'ai_schema_mismatch',
359
+ maxCount: 1,
360
+ reason: 'Type-only patterns may still flag for runtime validation',
361
+ },
362
+ {
363
+ category: 'dangerous_function',
364
+ maxCount: 2,
365
+ reason: 'JSON.parse with schema validation nearby may still trigger info-level findings',
366
+ },
367
+ ],
368
+ file: {
369
+ path: 'src/ai/typed-responses.ts',
370
+ content: `
371
+ import OpenAI from 'openai'
372
+ import { z } from 'zod'
373
+
374
+ // Strict interface definition
375
+ interface AISearchResult {
376
+ query: string
377
+ results: Array<{
378
+ id: string
379
+ title: string
380
+ score: number
381
+ }>
382
+ metadata: {
383
+ processingTime: number
384
+ model: string
385
+ }
386
+ }
387
+
388
+ // Runtime validation with zod matching interface
389
+ const AISearchResultSchema = z.object({
390
+ query: z.string(),
391
+ results: z.array(z.object({
392
+ id: z.string(),
393
+ title: z.string(),
394
+ score: z.number().min(0).max(1),
395
+ })),
396
+ metadata: z.object({
397
+ processingTime: z.number(),
398
+ model: z.string(),
399
+ }),
400
+ }).strict()
401
+
402
+ // Type-safe handler - SAFE
403
+ export async function searchWithAI(
404
+ query: string,
405
+ openai: OpenAI
406
+ ): Promise<AISearchResult> {
407
+ const completion = await openai.chat.completions.create({
408
+ model: 'gpt-4',
409
+ messages: [{ role: 'user', content: query }],
410
+ })
411
+
412
+ // Runtime validation ensures type safety
413
+ const validated = AISearchResultSchema.parse(
414
+ JSON.parse(completion.choices[0].message.content!)
415
+ )
416
+
417
+ return validated
418
+ }
419
+
420
+ // Generic with constraint - SAFE
421
+ export async function getTypedResponse<T>(
422
+ schema: z.ZodSchema<T>,
423
+ prompt: string
424
+ ): Promise<T> {
425
+ const raw = await getCompletion(prompt)
426
+ return schema.parse(JSON.parse(raw))
427
+ }
428
+ `,
429
+ language: 'typescript',
430
+ size: 1200,
431
+ },
432
+ },
433
+ ],
434
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Auth Anti-Patterns Test Fixtures
3
+ * Tests for detecting missing or weak authentication patterns
4
+ */
5
+
6
+ import type { TestGroup } from '../../types'
7
+
8
+ export const authAntipatternsTests: TestGroup = {
9
+ name: 'Auth Anti-Patterns',
10
+ tier: 'B',
11
+ layer: 2,
12
+ description: 'Detection of missing or weak authentication patterns in API routes',
13
+
14
+ truePositives: [
15
+ {
16
+ name: 'Auth Anti-Patterns - True Positives',
17
+ expectFindings: true,
18
+ expectedCategories: ['missing_auth'],
19
+ description: 'Missing auth patterns that MUST be detected',
20
+ file: {
21
+ path: 'src/api/admin/route.ts',
22
+ content: `
23
+ import { NextRequest, NextResponse } from 'next/server'
24
+ import { db } from '@/lib/db'
25
+
26
+ // No authentication on sensitive endpoint - HIGH
27
+ export async function GET(request: NextRequest) {
28
+ // No auth check!
29
+ const users = await db.user.findMany({
30
+ include: { apiKeys: true } // Exposing sensitive data
31
+ })
32
+ return NextResponse.json(users)
33
+ }
34
+
35
+ // No authentication on data mutation - CRITICAL
36
+ export async function POST(request: NextRequest) {
37
+ // No auth check!
38
+ const body = await request.json()
39
+ await db.user.update({
40
+ where: { id: body.userId },
41
+ data: { role: 'admin' } // Anyone can make themselves admin!
42
+ })
43
+ return NextResponse.json({ success: true })
44
+ }
45
+
46
+ // No authentication on delete - CRITICAL
47
+ export async function DELETE(request: NextRequest) {
48
+ const { searchParams } = new URL(request.url)
49
+ const userId = searchParams.get('userId')
50
+ // No auth check!
51
+ await db.user.delete({ where: { id: userId } })
52
+ return NextResponse.json({ success: true })
53
+ }
54
+ `,
55
+ language: 'typescript',
56
+ size: 900,
57
+ },
58
+ },
59
+ {
60
+ name: 'Auth Anti-Patterns - IDOR Vulnerabilities',
61
+ expectFindings: true,
62
+ expectedCategories: ['missing_auth'],
63
+ description: 'Insecure Direct Object Reference patterns',
64
+ file: {
65
+ path: 'src/api/users/[id]/route.ts',
66
+ content: `
67
+ import { NextRequest, NextResponse } from 'next/server'
68
+ import { db } from '@/lib/db'
69
+
70
+ // IDOR - accessing any user's data without ownership check
71
+ export async function GET(
72
+ request: NextRequest,
73
+ { params }: { params: { id: string } }
74
+ ) {
75
+ // No check that current user owns this resource!
76
+ const user = await db.user.findUnique({
77
+ where: { id: params.id },
78
+ include: {
79
+ apiKeys: true,
80
+ sessions: true,
81
+ billingInfo: true
82
+ }
83
+ })
84
+ return NextResponse.json(user)
85
+ }
86
+
87
+ // IDOR - updating any user's data
88
+ export async function PUT(
89
+ request: NextRequest,
90
+ { params }: { params: { id: string } }
91
+ ) {
92
+ const body = await request.json()
93
+ // No ownership verification!
94
+ await db.user.update({
95
+ where: { id: params.id },
96
+ data: body
97
+ })
98
+ return NextResponse.json({ success: true })
99
+ }
100
+ `,
101
+ language: 'typescript',
102
+ size: 800,
103
+ },
104
+ },
105
+ ],
106
+
107
+ falseNegatives: [
108
+ {
109
+ name: 'Auth Anti-Patterns - False Negatives',
110
+ expectFindings: false,
111
+ description: 'Properly authenticated patterns that should NOT be flagged',
112
+ file: {
113
+ path: 'src/api/protected/route.ts',
114
+ content: `
115
+ import { NextRequest, NextResponse } from 'next/server'
116
+ import { auth } from '@/lib/auth'
117
+ import { getCurrentUserId } from '@/lib/auth-helpers'
118
+ import { db } from '@/lib/db'
119
+
120
+ // Authenticated with session check - SAFE
121
+ export async function GET(request: NextRequest) {
122
+ const session = await auth()
123
+ if (!session?.user) {
124
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
125
+ }
126
+
127
+ const userData = await db.user.findUnique({
128
+ where: { id: session.user.id }
129
+ })
130
+ return NextResponse.json(userData)
131
+ }
132
+
133
+ // Using throwing auth helper - SAFE
134
+ export async function POST(request: NextRequest) {
135
+ // This throws if not authenticated
136
+ const userId = await getCurrentUserId()
137
+
138
+ // userId is guaranteed to exist here
139
+ const body = await request.json()
140
+ await db.post.create({
141
+ data: {
142
+ ...body,
143
+ authorId: userId
144
+ }
145
+ })
146
+ return NextResponse.json({ success: true })
147
+ }
148
+
149
+ // Public health check - SAFE (intentionally public)
150
+ export async function HEAD() {
151
+ return new Response(null, { status: 200 })
152
+ }
153
+ `,
154
+ language: 'typescript',
155
+ size: 1000,
156
+ },
157
+ },
158
+ ],
159
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * BYOK (Bring Your Own Key) Test Fixtures
3
+ * Tests for detecting insecure handling of user-provided API keys
4
+ */
5
+
6
+ import type { TestGroup } from '../../types'
7
+
8
+ export const byokTests: TestGroup = {
9
+ name: 'BYOK Patterns',
10
+ tier: 'A',
11
+ layer: 2,
12
+ description: 'Detection of insecure BYOK (Bring Your Own Key) patterns',
13
+
14
+ truePositives: [
15
+ {
16
+ name: 'BYOK - True Positives',
17
+ expectFindings: true,
18
+ expectedCategories: ['ai_pattern'],
19
+ description: 'Insecure BYOK patterns that MUST be detected',
20
+ file: {
21
+ path: 'src/api/ai/byok-storage.ts',
22
+ content: `
23
+ import { db } from '@/lib/db'
24
+
25
+ // BYOK key stored in database (Medium - should encrypt)
26
+ export async function saveUserApiKey(userId: string, apiKey: string) {
27
+ await db.user.update({
28
+ where: { id: userId },
29
+ data: { openaiApiKey: apiKey } // Stored without encryption!
30
+ })
31
+ }
32
+
33
+ // BYOK key logged (Medium - never log secrets)
34
+ export async function useUserKey(userId: string) {
35
+ const user = await db.user.findUnique({ where: { id: userId } })
36
+ console.log('Using API key:', user.openaiApiKey) // Logged!
37
+ return callOpenAI(user.openaiApiKey)
38
+ }
39
+
40
+ // BYOK key stored in plain text file
41
+ export function saveKeyToFile(apiKey: string) {
42
+ fs.writeFileSync('/data/api-keys.txt', apiKey)
43
+ }
44
+
45
+ // Cross-tenant key access (High - data isolation risk)
46
+ export async function getAnyUserKey(targetUserId: string) {
47
+ // No check that current user owns this key!
48
+ const user = await db.user.findUnique({ where: { id: targetUserId } })
49
+ return user.apiKey
50
+ }
51
+
52
+ // BYOK on unauthenticated endpoint (Medium - abuse risk)
53
+ export async function POST(request: Request) {
54
+ // No auth check!
55
+ const { apiKey, prompt } = await request.json()
56
+ return callOpenAI(apiKey, prompt)
57
+ }
58
+ `,
59
+ language: 'typescript',
60
+ size: 1200,
61
+ },
62
+ },
63
+ ],
64
+
65
+ falseNegatives: [
66
+ {
67
+ name: 'BYOK - False Negatives',
68
+ expectFindings: false,
69
+ description: 'Secure BYOK patterns that should NOT be flagged',
70
+ file: {
71
+ path: 'src/api/ai/byok-transient.ts',
72
+ content: `
73
+ import { auth } from '@/lib/auth'
74
+ import OpenAI from 'openai'
75
+
76
+ // Transient BYOK usage (authenticated) - SAFE (info at most)
77
+ export async function POST(request: Request) {
78
+ // Auth check
79
+ const session = await auth()
80
+ if (!session?.user) {
81
+ return Response.json({ error: 'Unauthorized' }, { status: 401 })
82
+ }
83
+
84
+ // Key from request body, used transiently
85
+ const { apiKey, prompt } = await request.json()
86
+
87
+ // Key only exists in memory for this request
88
+ const openai = new OpenAI({ apiKey })
89
+ const response = await openai.chat.completions.create({
90
+ model: 'gpt-4',
91
+ messages: [{ role: 'user', content: prompt }]
92
+ })
93
+
94
+ // Key is NOT stored or logged - this is the IDEAL BYOK pattern
95
+ return Response.json({ result: response.choices[0].message.content })
96
+ }
97
+
98
+ // User-scoped encrypted key storage - SAFE
99
+ export async function saveEncryptedKey(userId: string, apiKey: string) {
100
+ const encryptedKey = await encrypt(apiKey, process.env.ENCRYPTION_KEY!)
101
+ await db.user.update({
102
+ where: { id: userId },
103
+ data: { encryptedApiKey: encryptedKey }
104
+ })
105
+ }
106
+ `,
107
+ language: 'typescript',
108
+ size: 1100,
109
+ },
110
+ },
111
+ ],
112
+ }