@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,601 @@
1
+ /**
2
+ * Layer 2: AI Agent Tool Permission Detection
3
+ * Detects overly permissive agent tools and missing authorization checks
4
+ *
5
+ * Covers B4: Agent/tool orchestration logic
6
+ *
7
+ * Issues detected:
8
+ * - Tools with unrestricted file system access
9
+ * - Tools with unrestricted network access
10
+ * - Tools with shell/code execution capability
11
+ * - Tools without user/tenant context verification
12
+ * - Database tools without proper scoping
13
+ */
14
+
15
+ import type { Vulnerability, VulnerabilitySeverity } from '../types'
16
+ import {
17
+ isComment,
18
+ isTestOrMockFile,
19
+ isScannerOrFixtureFile,
20
+ isExampleDirectory,
21
+ isLibraryCode,
22
+ } from '../utils/context-helpers'
23
+
24
+ // ============================================================================
25
+ // Agent/Tool Context Detection
26
+ // ============================================================================
27
+
28
+ /**
29
+ * Check if file contains agent or tool definitions
30
+ */
31
+ function isAgentOrToolFile(filePath: string, content: string): boolean {
32
+ // File path indicators
33
+ const agentPathPatterns = [
34
+ /\/(agents?|tools?|functions?|actions?)\//i,
35
+ /\/(mcp|langchain|llamaindex|autogen)\//i,
36
+ /(agent|tool|function|action).*\.(ts|js|py)$/i,
37
+ ]
38
+
39
+ if (agentPathPatterns.some(p => p.test(filePath))) {
40
+ return true
41
+ }
42
+
43
+ // Content patterns indicating tool/agent definitions
44
+ const toolDefinitionPatterns = [
45
+ /@tool/i, // Python decorator
46
+ /def\s+\w+_tool\s*\(/i, // Python tool function
47
+ /defineTool\s*\(/i, // JS/TS tool definition
48
+ /createTool\s*\(/i, // Tool creation
49
+ /\.registerTool\s*\(/i, // Tool registration
50
+ /\.addTool\s*\(/i, // Adding tool to agent
51
+ /tools\s*:\s*\[/i, // Tools array
52
+ /FunctionTool|StructuredTool/i, // LangChain tools
53
+ /tool_choice|function_call/i, // OpenAI function calling
54
+ /Tool\s*\(\s*\{/i, // Tool configuration object
55
+ /type:\s*['"`]function['"`]/i, // OpenAI function type
56
+ /mcpServer|McpServer/i, // MCP server
57
+ ]
58
+
59
+ return toolDefinitionPatterns.some(p => p.test(content))
60
+ }
61
+
62
+ /**
63
+ * Find tool definition boundaries (start and end lines)
64
+ */
65
+ function findToolDefinitionContext(
66
+ content: string,
67
+ lineNumber: number,
68
+ windowSize: number = 30
69
+ ): { context: string; startLine: number; endLine: number } {
70
+ const lines = content.split('\n')
71
+ const startLine = Math.max(0, lineNumber - windowSize)
72
+ const endLine = Math.min(lines.length, lineNumber + windowSize)
73
+
74
+ return {
75
+ context: lines.slice(startLine, endLine).join('\n'),
76
+ startLine,
77
+ endLine,
78
+ }
79
+ }
80
+
81
+ // ============================================================================
82
+ // Authorization Detection
83
+ // ============================================================================
84
+
85
+ /**
86
+ * Check if user context is verified in tool
87
+ */
88
+ function hasUserContextVerification(context: string): boolean {
89
+ const userContextPatterns = [
90
+ /user[_.]?id/i,
91
+ /userId/i,
92
+ /currentUser/i,
93
+ /req\.user/i,
94
+ /request\.user/i,
95
+ /session\.user/i,
96
+ /getUser\s*\(/i,
97
+ /getCurrentUser\s*\(/i,
98
+ /authenticatedUser/i,
99
+ /ctx\.user/i,
100
+ /context\.user/i,
101
+ ]
102
+
103
+ return userContextPatterns.some(p => p.test(context))
104
+ }
105
+
106
+ /**
107
+ * Check if tenant/organization context is verified
108
+ */
109
+ function hasTenantContextVerification(context: string): boolean {
110
+ const tenantContextPatterns = [
111
+ /tenant[_.]?id/i,
112
+ /tenantId/i,
113
+ /org[_.]?id/i,
114
+ /orgId/i,
115
+ /organization[_.]?id/i,
116
+ /workspace[_.]?id/i,
117
+ /workspaceId/i,
118
+ /team[_.]?id/i,
119
+ /teamId/i,
120
+ /account[_.]?id/i,
121
+ /accountId/i,
122
+ ]
123
+
124
+ return tenantContextPatterns.some(p => p.test(context))
125
+ }
126
+
127
+ /**
128
+ * Patterns indicating strong/verified restrictions (actual implementation)
129
+ */
130
+ const STRONG_RESTRICTION_PATTERNS = [
131
+ // Sandboxing libraries and environments
132
+ /\bvm2\b/i,
133
+ /\bisolated-vm\b/i,
134
+ /\bquickjs\b/i,
135
+ /\bsandboxed\b/i,
136
+ /\bRestrictedPython\b/i,
137
+ /\bnsjail\b/i,
138
+ /\bfirejail\b/i,
139
+ /\bgvisor\b/i,
140
+
141
+ // Explicit path/resource restrictions with arrays
142
+ /allowed(?:Paths|Files|Dirs|Hosts|Urls|Commands)\s*[=:]\s*\[/i,
143
+ /(?:white|allow)list\s*[=:]\s*\[/i,
144
+ /(?:blocked|denied|forbidden)(?:Paths|Hosts|Commands)\s*[=:]\s*\[/i,
145
+
146
+ // Path validation functions
147
+ /validatePath\s*\(/i,
148
+ /isAllowedPath\s*\(/i,
149
+ /checkPathAccess\s*\(/i,
150
+ /resolvePath.*allowed/i,
151
+ /path\.resolve.*includes/i,
152
+
153
+ // Sandbox configuration objects
154
+ /sandbox\s*[=:]\s*(?:true|\{)/i,
155
+ /readonly\s*[=:]\s*true/i,
156
+ /readOnly\s*[=:]\s*true/i,
157
+
158
+ // Container/isolation patterns
159
+ /\b(?:docker|podman)\s+run\b.*--read-only/i,
160
+ /seccomp/i,
161
+ /capabilities\s*[=:]\s*\[\s*\]/i, // Empty capabilities = restricted
162
+
163
+ // Permission checking code
164
+ /if\s*\(\s*!?\s*(?:allowed|permitted|authorized)/i,
165
+ /(?:check|verify|validate)(?:Access|Permission|Capability)\s*\(/i,
166
+ ]
167
+
168
+ /**
169
+ * Patterns indicating weak/unverified restriction mentions (comments, TODOs)
170
+ */
171
+ const WEAK_RESTRICTION_PATTERNS = [
172
+ // Comments mentioning restrictions without implementation
173
+ /\/\/.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,
174
+ /\/\*.*(?:sandbox|restrict|allowlist|whitelist|todo).*\*\//i,
175
+ /#.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,
176
+
177
+ // TODOs and FIXMEs
178
+ /TODO.*(?:add|implement|enable).*(?:sandbox|restrict|allowlist)/i,
179
+ /FIXME.*(?:sandbox|restrict|security)/i,
180
+
181
+ // Variable names without assignment
182
+ /const\s+(?:sandbox|allowlist|whitelist)\s*;/i,
183
+ ]
184
+
185
+ /**
186
+ * Check if tool has strong/verified access restrictions
187
+ * These are actual implementations, not just mentions
188
+ */
189
+ function hasStrongRestrictions(context: string): boolean {
190
+ // Check for strong patterns
191
+ const hasStrong = STRONG_RESTRICTION_PATTERNS.some(p => p.test(context))
192
+ if (!hasStrong) return false
193
+
194
+ // Verify it's not just a weak mention
195
+ const isWeak = WEAK_RESTRICTION_PATTERNS.some(p => p.test(context))
196
+ return !isWeak
197
+ }
198
+
199
+ /**
200
+ * Check if tool has any access restrictions/allowlists (including weak mentions)
201
+ */
202
+ function hasAccessRestrictions(context: string): boolean {
203
+ const restrictionPatterns = [
204
+ /allowedPaths/i,
205
+ /allowedFiles/i,
206
+ /allowedDirs/i,
207
+ /allowedHosts/i,
208
+ /allowedUrls/i,
209
+ /allowedCommands/i,
210
+ /allowedOperations/i,
211
+ /whitelist/i,
212
+ /allowlist/i,
213
+ /permissions?:/i,
214
+ /capabilities:/i,
215
+ /restrictions?:/i,
216
+ /constraints?:/i,
217
+ /sandbox/i,
218
+ /readonly/i,
219
+ /readOnly/i,
220
+ ]
221
+
222
+ return restrictionPatterns.some(p => p.test(context))
223
+ }
224
+
225
+ // ============================================================================
226
+ // Pattern Definitions
227
+ // ============================================================================
228
+
229
+ type ToolRiskType = 'filesystem' | 'network' | 'code_execution' | 'database' | 'shell'
230
+
231
+ interface ToolPattern {
232
+ name: string
233
+ pattern: RegExp
234
+ riskType: ToolRiskType
235
+ baseSeverity: VulnerabilitySeverity
236
+ description: string
237
+ suggestedFix: string
238
+ requiresUserContext?: boolean
239
+ requiresTenantContext?: boolean
240
+ requiresRestrictions?: boolean
241
+ }
242
+
243
+ const OVERPERMISSIVE_TOOL_PATTERNS: ToolPattern[] = [
244
+ // ========== Filesystem Access Tools ==========
245
+ {
246
+ name: 'Unrestricted file read tool',
247
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:read|get).*file|(?:fs|filesystem).*(?:read|get)/gi,
248
+ riskType: 'filesystem',
249
+ baseSeverity: 'high',
250
+ description: 'Tool provides file system read access. Without restrictions, agents can access any file the process can read.',
251
+ suggestedFix: 'Add allowedPaths restriction. Example: { allowedPaths: ["/data/user-uploads"] }. Validate paths stay within allowed directories.',
252
+ requiresRestrictions: true,
253
+ },
254
+ {
255
+ name: 'Unrestricted file write tool',
256
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:write|create|save).*file|(?:fs|filesystem).*(?:write|create)/gi,
257
+ riskType: 'filesystem',
258
+ baseSeverity: 'high',
259
+ description: 'Tool provides file system write access. Agents could overwrite critical files or create malicious files.',
260
+ suggestedFix: 'Restrict to specific directories. Validate file extensions. Implement size limits. Consider using signed URLs instead of direct file access.',
261
+ requiresRestrictions: true,
262
+ },
263
+ {
264
+ name: 'File deletion tool',
265
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:delete|remove).*file|(?:fs|filesystem).*(?:delete|unlink|remove)/gi,
266
+ riskType: 'filesystem',
267
+ baseSeverity: 'high',
268
+ description: 'Tool provides file deletion capability. High risk of data loss if misused.',
269
+ suggestedFix: 'Implement soft-delete instead of hard delete. Require confirmation. Restrict to user-owned files only.',
270
+ requiresRestrictions: true,
271
+ requiresUserContext: true,
272
+ },
273
+
274
+ // ========== Network Access Tools ==========
275
+ {
276
+ name: 'Unrestricted HTTP/fetch tool',
277
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:http|fetch|request|api)|tool.*(?:fetch|request)\s*\(/gi,
278
+ riskType: 'network',
279
+ baseSeverity: 'medium',
280
+ description: 'Tool provides network/HTTP access. Without restrictions, agents could make requests to internal services (SSRF) or exfiltrate data.',
281
+ suggestedFix: 'Add allowedHosts configuration. Block internal/private IP ranges. Implement request signing for sensitive operations.',
282
+ requiresRestrictions: true,
283
+ },
284
+ {
285
+ name: 'Web scraping tool',
286
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:scrape|crawl|browse)/gi,
287
+ riskType: 'network',
288
+ baseSeverity: 'medium',
289
+ description: 'Tool provides web scraping capability. Could be used for SSRF or accessing internal resources.',
290
+ suggestedFix: 'Restrict to allowed domains. Block internal IP ranges. Implement rate limiting.',
291
+ requiresRestrictions: true,
292
+ },
293
+
294
+ // ========== Code Execution Tools ==========
295
+ {
296
+ name: 'Code execution tool',
297
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:execute|run|eval).*(?:code|script)|tool.*(?:eval|exec)\s*\(/gi,
298
+ riskType: 'code_execution',
299
+ baseSeverity: 'critical',
300
+ description: 'Tool provides code execution capability. This is extremely dangerous without sandboxing.',
301
+ suggestedFix: 'Use vm2, isolated-vm, or similar sandboxing. Implement timeout and memory limits. Restrict available APIs/modules.',
302
+ requiresRestrictions: true,
303
+ },
304
+ {
305
+ name: 'Python interpreter tool',
306
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*python.*(?:exec|run|interpret)|PythonREPL|python_repl/gi,
307
+ riskType: 'code_execution',
308
+ baseSeverity: 'critical',
309
+ description: 'Tool provides Python execution capability. Can execute arbitrary system commands.',
310
+ suggestedFix: 'Use RestrictedPython or sandboxed environments. Block dangerous modules (os, subprocess, socket). Implement resource limits.',
311
+ requiresRestrictions: true,
312
+ },
313
+
314
+ // ========== Shell/Command Tools ==========
315
+ {
316
+ name: 'Shell command tool',
317
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:shell|command|terminal|bash)|ShellTool|BashTool/gi,
318
+ riskType: 'shell',
319
+ baseSeverity: 'critical',
320
+ description: 'Tool provides shell command execution. Allows arbitrary system commands.',
321
+ suggestedFix: 'Implement strict command allowlisting. Use parameterized commands (execFile, not exec). Consider removing this capability entirely.',
322
+ requiresRestrictions: true,
323
+ },
324
+ {
325
+ name: 'System command tool',
326
+ pattern: /(?:@tool|defineTool|createTool)[^)]*(?:system|exec|spawn|subprocess)/gi,
327
+ riskType: 'shell',
328
+ baseSeverity: 'critical',
329
+ description: 'Tool with system command execution capability.',
330
+ suggestedFix: 'Restrict to specific commands via allowlist. Validate all arguments. Log all command executions.',
331
+ requiresRestrictions: true,
332
+ },
333
+
334
+ // ========== Database Tools ==========
335
+ {
336
+ name: 'Database query tool',
337
+ pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:query|sql|database)|tool.*(?:query|execute)\s*\(/gi,
338
+ riskType: 'database',
339
+ baseSeverity: 'high',
340
+ description: 'Tool provides database query access. Without scoping, agents could access any data.',
341
+ suggestedFix: 'Always scope queries to current user/tenant. Use row-level security (RLS). Implement read-only mode for most operations.',
342
+ requiresUserContext: true,
343
+ requiresTenantContext: true,
344
+ },
345
+ {
346
+ name: 'Raw SQL tool',
347
+ pattern: /(?:@tool|defineTool|createTool)[^)]*(?:raw.*sql|execute.*sql)/gi,
348
+ riskType: 'database',
349
+ baseSeverity: 'critical',
350
+ description: 'Tool allows raw SQL execution. High risk of SQL injection and unauthorized data access.',
351
+ suggestedFix: 'Use parameterized queries only. Implement query validation. Consider using an ORM instead of raw SQL.',
352
+ requiresUserContext: true,
353
+ requiresTenantContext: true,
354
+ },
355
+
356
+ // ========== M5: MCP Server Tools ==========
357
+ {
358
+ name: 'MCP server tool registration',
359
+ pattern: /(?:McpServer|Server)\s*\([^)]*\).*(?:setRequestHandler|tool|registerTool)|server\.tool\s*\(/gi,
360
+ riskType: 'code_execution',
361
+ baseSeverity: 'high',
362
+ description: 'MCP (Model Context Protocol) server registering tools. Verify tool capabilities are appropriately restricted.',
363
+ suggestedFix: 'Add capability restrictions to MCP server. Implement allowlists for file paths, network hosts, and commands.',
364
+ requiresRestrictions: true,
365
+ },
366
+ {
367
+ name: 'MCP tool with shell access',
368
+ pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:run|exec|shell|command)[^)]*|(?:exec|spawn|shell)\s*\()/gi,
369
+ riskType: 'shell',
370
+ baseSeverity: 'critical',
371
+ description: 'MCP tool with shell command execution capability. Extremely dangerous without restrictions.',
372
+ suggestedFix: 'Use allowlist of permitted commands. Never allow arbitrary command execution. Consider read-only alternatives.',
373
+ requiresRestrictions: true,
374
+ },
375
+ {
376
+ name: 'MCP file system tool',
377
+ pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:read|write|create|delete|list).*(?:file|dir)[^)]*|fs\.|readFile|writeFile)/gi,
378
+ riskType: 'filesystem',
379
+ baseSeverity: 'high',
380
+ description: 'MCP tool with file system access. Agents could access or modify arbitrary files.',
381
+ suggestedFix: 'Restrict to specific directories with allowedPaths. Implement path validation. Consider read-only access.',
382
+ requiresRestrictions: true,
383
+ },
384
+
385
+ // ========== M5: Vercel AI SDK Tools ==========
386
+ {
387
+ name: 'Vercel AI SDK tool definition',
388
+ pattern: /tool\s*\(\s*\{[^}]*(?:execute|parameters)/gi,
389
+ riskType: 'code_execution',
390
+ baseSeverity: 'medium',
391
+ description: 'Vercel AI SDK tool definition. Review the execute function for dangerous operations.',
392
+ suggestedFix: 'Validate tool parameters against expected schema. Implement proper access controls within execute function.',
393
+ requiresUserContext: true,
394
+ },
395
+ {
396
+ name: 'AI SDK tool with dangerous execute',
397
+ pattern: /tool\s*\(\s*\{[^}]*execute\s*:\s*async[^}]*(?:exec|spawn|eval|fs\.|fetch\s*\()[^}]*\}/gi,
398
+ riskType: 'code_execution',
399
+ baseSeverity: 'high',
400
+ description: 'Vercel AI SDK tool with potentially dangerous execute function (shell, eval, fs, or network access).',
401
+ suggestedFix: 'Add validation and restrictions in execute function. Implement allowlists for external operations.',
402
+ requiresRestrictions: true,
403
+ },
404
+ {
405
+ name: 'StreamableUI tool action',
406
+ pattern: /createStreamableUI.*tool.*\{.*action/gi,
407
+ riskType: 'code_execution',
408
+ baseSeverity: 'medium',
409
+ description: 'Streamable UI tool with server action. Ensure proper authorization before state mutations.',
410
+ suggestedFix: 'Verify user authentication and authorization before executing actions. Validate all inputs.',
411
+ requiresUserContext: true,
412
+ },
413
+ ]
414
+
415
+ /**
416
+ * Patterns for missing authorization in tools
417
+ */
418
+ const MISSING_AUTH_PATTERNS: ToolPattern[] = [
419
+ {
420
+ name: 'Tool without user context',
421
+ pattern: /(?:@tool|defineTool|createTool|\.registerTool|\.addTool)\s*\([^)]*(?:async\s+)?(?:function|\().*(?:create|update|delete|modify|write|send)/gi,
422
+ riskType: 'database',
423
+ baseSeverity: 'medium',
424
+ description: 'Tool performs write operations but may not verify user context. Actions could be performed as wrong user.',
425
+ suggestedFix: 'Pass userId as required parameter. Verify user owns/can access the resource before modification.',
426
+ requiresUserContext: true,
427
+ },
428
+ ]
429
+
430
+ // ============================================================================
431
+ // Main Detection Function
432
+ // ============================================================================
433
+
434
+ /**
435
+ * Main detection function for AI agent tool permission issues
436
+ */
437
+ export function detectAIAgentTools(
438
+ content: string,
439
+ filePath: string
440
+ ): Vulnerability[] {
441
+ const vulnerabilities: Vulnerability[] = []
442
+
443
+ // Skip non-applicable files
444
+ if (isScannerOrFixtureFile(filePath)) return vulnerabilities
445
+
446
+ // Only scan files that appear to have agent/tool definitions
447
+ if (!isAgentOrToolFile(filePath, content)) {
448
+ return vulnerabilities
449
+ }
450
+
451
+ const lines = content.split('\n')
452
+ const isTestFile = isTestOrMockFile(filePath)
453
+ const isExample = isExampleDirectory(filePath)
454
+ const isLibrary = isLibraryCode(filePath)
455
+
456
+ // Scan for overly permissive tool patterns
457
+ for (const pattern of OVERPERMISSIVE_TOOL_PATTERNS) {
458
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
459
+ let match
460
+
461
+ while ((match = regex.exec(content)) !== null) {
462
+ const lineNumber = content.substring(0, match.index).split('\n').length
463
+ const lineContent = lines[lineNumber - 1]?.trim() || ''
464
+
465
+ // Skip comments
466
+ if (isComment(lineContent)) continue
467
+
468
+ // Get tool context
469
+ const { context } = findToolDefinitionContext(content, lineNumber)
470
+
471
+ // Check for mitigations (strong vs weak)
472
+ const hasStrong = hasStrongRestrictions(context)
473
+ const hasWeak = hasAccessRestrictions(context)
474
+ const hasUserContext = hasUserContextVerification(context)
475
+ const hasTenantContext = hasTenantContextVerification(context)
476
+
477
+ // Determine if issue is fully mitigated
478
+ let isMitigated = true
479
+ let hasPartialMitigation = false
480
+ const missingMitigations: string[] = []
481
+
482
+ if (pattern.requiresRestrictions) {
483
+ if (hasStrong) {
484
+ // Strong restrictions = fully mitigated for this requirement
485
+ } else if (hasWeak) {
486
+ // Weak restrictions = partial mitigation
487
+ hasPartialMitigation = true
488
+ missingMitigations.push('verified access restrictions (found mentions but not implementation)')
489
+ isMitigated = false
490
+ } else {
491
+ isMitigated = false
492
+ missingMitigations.push('access restrictions')
493
+ }
494
+ }
495
+ if (pattern.requiresUserContext && !hasUserContext) {
496
+ isMitigated = false
497
+ missingMitigations.push('user context verification')
498
+ }
499
+ if (pattern.requiresTenantContext && !hasTenantContext) {
500
+ isMitigated = false
501
+ missingMitigations.push('tenant/org context verification')
502
+ }
503
+
504
+ // Skip if all required mitigations are present with strong verification
505
+ if (isMitigated) continue
506
+
507
+ // Calculate severity
508
+ let severity = pattern.baseSeverity
509
+ if (isTestFile) {
510
+ severity = 'info'
511
+ } else if (isExample) {
512
+ // Example/demo code - downgrade to info
513
+ severity = 'info'
514
+ } else if (isLibrary) {
515
+ // Library code - tool definitions are intentionally flexible
516
+ // Consumers add restrictions when they use the tools
517
+ severity = 'info'
518
+ } else if (hasPartialMitigation || hasUserContext || hasTenantContext) {
519
+ // Partial mitigation - downgrade
520
+ if (severity === 'critical') severity = 'high'
521
+ else if (severity === 'high') severity = 'medium'
522
+ }
523
+
524
+ // Build description
525
+ let description = pattern.description
526
+ if (missingMitigations.length > 0) {
527
+ description += ` Missing: ${missingMitigations.join(', ')}.`
528
+ }
529
+ if (isTestFile) {
530
+ description += ' (In test file.)'
531
+ } else if (isExample) {
532
+ description += ' (In example/demo directory - not production code.)'
533
+ } else if (isLibrary) {
534
+ description += ' (Library code - tool definitions are generic; consumers add restrictions.)'
535
+ }
536
+
537
+ vulnerabilities.push({
538
+ id: `ai-tool-${filePath}-${lineNumber}-${pattern.riskType}`,
539
+ filePath,
540
+ lineNumber,
541
+ lineContent,
542
+ severity,
543
+ category: 'ai_overpermissive_tool',
544
+ title: pattern.name,
545
+ description,
546
+ suggestedFix: pattern.suggestedFix,
547
+ confidence: 'medium',
548
+ layer: 2,
549
+ requiresAIValidation: true, // Always validate - context dependent
550
+ })
551
+ }
552
+ }
553
+
554
+ // Scan for missing authorization patterns
555
+ for (const pattern of MISSING_AUTH_PATTERNS) {
556
+ const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
557
+ let match
558
+
559
+ while ((match = regex.exec(content)) !== null) {
560
+ const lineNumber = content.substring(0, match.index).split('\n').length
561
+ const lineContent = lines[lineNumber - 1]?.trim() || ''
562
+
563
+ // Skip comments
564
+ if (isComment(lineContent)) continue
565
+
566
+ // Get tool context
567
+ const { context } = findToolDefinitionContext(content, lineNumber)
568
+
569
+ // Check if user context is verified
570
+ const hasUserContext = hasUserContextVerification(context)
571
+
572
+ // Skip if user context is present
573
+ if (hasUserContext) continue
574
+
575
+ let severity = pattern.baseSeverity
576
+ let description = pattern.description
577
+
578
+ if (isTestFile) {
579
+ severity = 'info'
580
+ description += ' (In test file.)'
581
+ }
582
+
583
+ vulnerabilities.push({
584
+ id: `ai-tool-auth-${filePath}-${lineNumber}`,
585
+ filePath,
586
+ lineNumber,
587
+ lineContent,
588
+ severity,
589
+ category: 'ai_overpermissive_tool',
590
+ title: pattern.name,
591
+ description,
592
+ suggestedFix: pattern.suggestedFix,
593
+ confidence: 'low', // Lower confidence - needs context
594
+ layer: 2,
595
+ requiresAIValidation: true,
596
+ })
597
+ }
598
+ }
599
+
600
+ return vulnerabilities
601
+ }