/**
 * Layer 2: AI Agent Tool Permission Detection
 * Detects overly permissive agent tools and missing authorization checks
 *
 * Covers B4: Agent/tool orchestration logic
 *
 * Issues detected:
 * - Tools with unrestricted file system access
 * - Tools with unrestricted network access
 * - Tools with shell/code execution capability
 * - Tools without user/tenant context verification
 * - Database tools without proper scoping
 */

import type { Vulnerability, VulnerabilitySeverity } from '../../shared/types'
import type { ParsedFile } from '../../shared/parsed-file'
import {
  isComment,
  isTestOrMockFile,
  isScannerOrFixtureFile,
  isExampleDirectory,
  isLibraryCode,
} from '../../parse/file-classifier'

const BASE_CONFIDENCE = 0.50

// ============================================================================
// Agent/Tool Context Detection
// ============================================================================

/**
 * Check if file contains agent or tool definitions
 */
function isAgentOrToolFile(filePath: string, content: string): boolean {
  // File path indicators
  const agentPathPatterns = [
    /\/(agents?|tools?|functions?|actions?)\//i,
    /\/(mcp|langchain|llamaindex|autogen)\//i,
    /(agent|tool|function|action).*\.(ts|js|py)$/i,
  ]

  if (agentPathPatterns.some(p => p.test(filePath))) {
    return true
  }

  // Content patterns indicating tool/agent definitions
  const toolDefinitionPatterns = [
    /@tool/i,                             // Python decorator
    /def\s+\w+_tool\s*\(/i,              // Python tool function
    /defineTool\s*\(/i,                   // JS/TS tool definition
    /createTool\s*\(/i,                   // Tool creation
    /\.registerTool\s*\(/i,               // Tool registration
    /\.addTool\s*\(/i,                    // Adding tool to agent
    /tools\s*:\s*\[/i,                    // Tools array
    /FunctionTool|StructuredTool/i,       // LangChain tools
    /tool_choice|function_call/i,         // OpenAI function calling
    /Tool\s*\(\s*\{/i,                    // Tool configuration object
    /type:\s*['"`]function['"`]/i,        // OpenAI function type
    /mcpServer|McpServer/i,               // MCP server
  ]

  return toolDefinitionPatterns.some(p => p.test(content))
}

/**
 * Find tool definition boundaries (start and end lines)
 */
function findToolDefinitionContext(
  content: string,
  lineNumber: number,
  windowSize: number = 30
): { context: string; startLine: number; endLine: number } {
  const lines = content.split('\n')
  const startLine = Math.max(0, lineNumber - windowSize)
  const endLine = Math.min(lines.length, lineNumber + windowSize)

  return {
    context: lines.slice(startLine, endLine).join('\n'),
    startLine,
    endLine,
  }
}

// ============================================================================
// Authorization Detection
// ============================================================================

/**
 * Check if user context is verified in tool
 */
function hasUserContextVerification(context: string): boolean {
  const userContextPatterns = [
    /user[_.]?id/i,
    /userId/i,
    /currentUser/i,
    /req\.user/i,
    /request\.user/i,
    /session\.user/i,
    /getUser\s*\(/i,
    /getCurrentUser\s*\(/i,
    /authenticatedUser/i,
    /ctx\.user/i,
    /context\.user/i,
  ]

  return userContextPatterns.some(p => p.test(context))
}

/**
 * Check if tenant/organization context is verified
 */
function hasTenantContextVerification(context: string): boolean {
  const tenantContextPatterns = [
    /tenant[_.]?id/i,
    /tenantId/i,
    /org[_.]?id/i,
    /orgId/i,
    /organization[_.]?id/i,
    /workspace[_.]?id/i,
    /workspaceId/i,
    /team[_.]?id/i,
    /teamId/i,
    /account[_.]?id/i,
    /accountId/i,
  ]

  return tenantContextPatterns.some(p => p.test(context))
}

/**
 * Check if this is an MCP file with proper security controls
 * MCP files with sanitization and authorization should be downgraded
 */
function isMCPFileWithSafePatterns(content: string, filePath: string): boolean {
  // Check if this is an MCP file
  const mcpPatterns = [
    /McpServer/i,
    /@modelcontextprotocol/i,
    /server\.tool\s*\(/i,
    /@server\.tool/i,
    /\/mcp\//i,
  ]

  if (!mcpPatterns.some(p => p.test(content) || p.test(filePath))) {
    return false
  }

  // Check for content sanitization patterns
  const sanitizationPatterns = [
    /sanitize|DOMPurify|purify/i,
    /escapeHtml|escape_html/i,
    /validateSchema|schema\.parse|safeParse/i,
    /ALLOWED_TAGS/i,
    /filterHtml|cleanHtml/i,
  ]

  // Check for authorization patterns
  const authorizationPatterns = [
    /if\s*\([^)]*ownerId\s*[!=]==?/i,
    /if\s*\([^)]*userId\s*[!=]==?/i,
    /if\s*\([^)]*tenantId\s*[!=]==?/i,
    /throw.*Error.*(?:auth|Forbidden|Unauthorized)/i,
    /Not\s*authorized/i,
    /checkPermission|hasPermission|isAuthorized/i,
  ]

  const hasSanitization = sanitizationPatterns.some(p => p.test(content))
  const hasAuthorization = authorizationPatterns.some(p => p.test(content))

  // MCP file with BOTH sanitization AND authorization is safe
  return hasSanitization && hasAuthorization
}

/**
 * Patterns indicating strong/verified restrictions (actual implementation)
 */
const STRONG_RESTRICTION_PATTERNS = [
  // Sandboxing libraries and environments
  /\bvm2\b/i,
  /\bisolated-vm\b/i,
  /\bquickjs\b/i,
  /\bsandboxed\b/i,
  /\bRestrictedPython\b/i,
  /\bnsjail\b/i,
  /\bfirejail\b/i,
  /\bgvisor\b/i,

  // Explicit path/resource restrictions with arrays
  /allowed(?:Paths|Files|Dirs|Hosts|Urls|Commands)\s*[=:]\s*\[/i,
  /(?:white|allow)list\s*[=:]\s*\[/i,
  /(?:blocked|denied|forbidden)(?:Paths|Hosts|Commands)\s*[=:]\s*\[/i,

  // Path validation functions
  /validatePath\s*\(/i,
  /isAllowedPath\s*\(/i,
  /checkPathAccess\s*\(/i,
  /resolvePath.*allowed/i,
  /path\.resolve.*includes/i,

  // Sandbox configuration objects
  /sandbox\s*[=:]\s*(?:true|\{)/i,
  /readonly\s*[=:]\s*true/i,
  /readOnly\s*[=:]\s*true/i,

  // Container/isolation patterns
  /\b(?:docker|podman)\s+run\b.*--read-only/i,
  /seccomp/i,
  /capabilities\s*[=:]\s*\[\s*\]/i,  // Empty capabilities = restricted

  // Permission checking code
  /if\s*\(\s*!?\s*(?:allowed|permitted|authorized)/i,
  /(?:check|verify|validate)(?:Access|Permission|Capability)\s*\(/i,
]

/**
 * Patterns indicating weak/unverified restriction mentions (comments, TODOs)
 */
const WEAK_RESTRICTION_PATTERNS = [
  // Comments mentioning restrictions without implementation
  /\/\/.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,
  /\/\*.*(?:sandbox|restrict|allowlist|whitelist|todo).*\*\//i,
  /#.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,

  // TODOs and FIXMEs
  /TODO.*(?:add|implement|enable).*(?:sandbox|restrict|allowlist)/i,
  /FIXME.*(?:sandbox|restrict|security)/i,

  // Variable names without assignment
  /const\s+(?:sandbox|allowlist|whitelist)\s*;/i,
]

/**
 * Check if tool has strong/verified access restrictions
 * These are actual implementations, not just mentions
 */
function hasStrongRestrictions(context: string): boolean {
  // Check for strong patterns
  const hasStrong = STRONG_RESTRICTION_PATTERNS.some(p => p.test(context))
  if (!hasStrong) return false

  // Verify it's not just a weak mention
  const isWeak = WEAK_RESTRICTION_PATTERNS.some(p => p.test(context))
  return !isWeak
}

/**
 * Check if tool has any access restrictions/allowlists (including weak mentions)
 */
function hasAccessRestrictions(context: string): boolean {
  const restrictionPatterns = [
    /allowedPaths/i,
    /allowedFiles/i,
    /allowedDirs/i,
    /allowedHosts/i,
    /allowedUrls/i,
    /allowedCommands/i,
    /allowedOperations/i,
    /whitelist/i,
    /allowlist/i,
    /permissions?:/i,
    /capabilities:/i,
    /restrictions?:/i,
    /constraints?:/i,
    /sandbox/i,
    /readonly/i,
    /readOnly/i,
  ]

  return restrictionPatterns.some(p => p.test(context))
}

// ============================================================================
// Pattern Definitions
// ============================================================================

type ToolRiskType = 'filesystem' | 'network' | 'code_execution' | 'database' | 'shell'

interface ToolPattern {
  name: string
  pattern: RegExp
  riskType: ToolRiskType
  baseSeverity: VulnerabilitySeverity
  description: string
  suggestedFix: string
  requiresUserContext?: boolean
  requiresTenantContext?: boolean
  requiresRestrictions?: boolean
}

const OVERPERMISSIVE_TOOL_PATTERNS: ToolPattern[] = [
  // ========== Filesystem Access Tools ==========
  {
    name: 'Unrestricted file read tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:read|get).*file|(?:fs|filesystem).*(?:read|get)/gi,
    riskType: 'filesystem',
    baseSeverity: 'high',
    description: 'Tool provides file system read access. Without restrictions, agents can access any file the process can read.',
    suggestedFix: 'Add allowedPaths restriction. Example: { allowedPaths: ["/data/user-uploads"] }. Validate paths stay within allowed directories.',
    requiresRestrictions: true,
  },
  {
    name: 'Unrestricted file write tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:write|create|save).*file|(?:fs|filesystem).*(?:write|create)/gi,
    riskType: 'filesystem',
    baseSeverity: 'high',
    description: 'Tool provides file system write access. Agents could overwrite critical files or create malicious files.',
    suggestedFix: 'Restrict to specific directories. Validate file extensions. Implement size limits. Consider using signed URLs instead of direct file access.',
    requiresRestrictions: true,
  },
  {
    name: 'File deletion tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:delete|remove).*file|(?:fs|filesystem).*(?:delete|unlink|remove)/gi,
    riskType: 'filesystem',
    baseSeverity: 'high',
    description: 'Tool provides file deletion capability. High risk of data loss if misused.',
    suggestedFix: 'Implement soft-delete instead of hard delete. Require confirmation. Restrict to user-owned files only.',
    requiresRestrictions: true,
    requiresUserContext: true,
  },

  // ========== Network Access Tools ==========
  {
    name: 'Unrestricted HTTP/fetch tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:http|fetch|request|api)|tool.*(?:fetch|request)\s*\(/gi,
    riskType: 'network',
    baseSeverity: 'medium',
    description: 'Tool provides network/HTTP access. Without restrictions, agents could make requests to internal services (SSRF) or exfiltrate data.',
    suggestedFix: 'Add allowedHosts configuration. Block internal/private IP ranges. Implement request signing for sensitive operations.',
    requiresRestrictions: true,
  },
  {
    name: 'Web scraping tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:scrape|crawl|browse)/gi,
    riskType: 'network',
    baseSeverity: 'medium',
    description: 'Tool provides web scraping capability. Could be used for SSRF or accessing internal resources.',
    suggestedFix: 'Restrict to allowed domains. Block internal IP ranges. Implement rate limiting.',
    requiresRestrictions: true,
  },

  // ========== Code Execution Tools ==========
  {
    name: 'Code execution tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:execute|run|eval).*(?:code|script)|tool.*(?:eval|exec)\s*\(/gi,
    riskType: 'code_execution',
    baseSeverity: 'critical',
    description: 'Tool provides code execution capability. This is extremely dangerous without sandboxing.',
    suggestedFix: 'Use vm2, isolated-vm, or similar sandboxing. Implement timeout and memory limits. Restrict available APIs/modules.',
    requiresRestrictions: true,
  },
  {
    name: 'Python interpreter tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*python.*(?:exec|run|interpret)|PythonREPL|python_repl/gi,
    riskType: 'code_execution',
    baseSeverity: 'critical',
    description: 'Tool provides Python execution capability. Can execute arbitrary system commands.',
    suggestedFix: 'Use RestrictedPython or sandboxed environments. Block dangerous modules (os, subprocess, socket). Implement resource limits.',
    requiresRestrictions: true,
  },

  // ========== Shell/Command Tools ==========
  {
    name: 'Shell command tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:shell|command|terminal|bash)|ShellTool|BashTool/gi,
    riskType: 'shell',
    baseSeverity: 'critical',
    description: 'Tool provides shell command execution. Allows arbitrary system commands.',
    suggestedFix: 'Implement strict command allowlisting. Use parameterized commands (execFile, not exec). Consider removing this capability entirely.',
    requiresRestrictions: true,
  },
  {
    name: 'System command tool',
    pattern: /(?:@tool|defineTool|createTool)[^)]*(?:system|exec|spawn|subprocess)/gi,
    riskType: 'shell',
    baseSeverity: 'critical',
    description: 'Tool with system command execution capability.',
    suggestedFix: 'Restrict to specific commands via allowlist. Validate all arguments. Log all command executions.',
    requiresRestrictions: true,
  },

  // ========== Database Tools ==========
  {
    name: 'Database query tool',
    pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:query|sql|database)|tool.*(?:query|execute)\s*\(/gi,
    riskType: 'database',
    baseSeverity: 'high',
    description: 'Tool provides database query access. Without scoping, agents could access any data.',
    suggestedFix: 'Always scope queries to current user/tenant. Use row-level security (RLS). Implement read-only mode for most operations.',
    requiresUserContext: true,
    requiresTenantContext: true,
  },
  {
    name: 'Raw SQL tool',
    pattern: /(?:@tool|defineTool|createTool)[^)]*(?:raw.*sql|execute.*sql)/gi,
    riskType: 'database',
    baseSeverity: 'critical',
    description: 'Tool allows raw SQL execution. High risk of SQL injection and unauthorized data access.',
    suggestedFix: 'Use parameterized queries only. Implement query validation. Consider using an ORM instead of raw SQL.',
    requiresUserContext: true,
    requiresTenantContext: true,
  },

  // ========== M5: MCP Server Tools ==========
  {
    name: 'MCP server tool registration',
    pattern: /(?:McpServer|Server)\s*\([^)]*\).*(?:setRequestHandler|tool|registerTool)|server\.tool\s*\(/gi,
    riskType: 'code_execution',
    baseSeverity: 'high',
    description: 'MCP (Model Context Protocol) server registering tools. Verify tool capabilities are appropriately restricted.',
    suggestedFix: 'Add capability restrictions to MCP server. Implement allowlists for file paths, network hosts, and commands.',
    requiresRestrictions: true,
  },
  {
    name: 'MCP tool with shell access',
    pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:run|exec|shell|command)[^)]*|(?:exec|spawn|shell)\s*\()/gi,
    riskType: 'shell',
    baseSeverity: 'critical',
    description: 'MCP tool with shell command execution capability. Extremely dangerous without restrictions.',
    suggestedFix: 'Use allowlist of permitted commands. Never allow arbitrary command execution. Consider read-only alternatives.',
    requiresRestrictions: true,
  },
  {
    name: 'MCP file system tool',
    pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:read|write|create|delete|list).*(?:file|dir)[^)]*|fs\.|readFile|writeFile)/gi,
    riskType: 'filesystem',
    baseSeverity: 'high',
    description: 'MCP tool with file system access. Agents could access or modify arbitrary files.',
    suggestedFix: 'Restrict to specific directories with allowedPaths. Implement path validation. Consider read-only access.',
    requiresRestrictions: true,
  },

  // ========== M5: Vercel AI SDK Tools ==========
  {
    name: 'Vercel AI SDK tool definition',
    pattern: /tool\s*\(\s*\{[^}]*(?:execute|parameters)/gi,
    riskType: 'code_execution',
    baseSeverity: 'medium',
    description: 'Vercel AI SDK tool definition. Review the execute function for dangerous operations.',
    suggestedFix: 'Validate tool parameters against expected schema. Implement proper access controls within execute function.',
    requiresUserContext: true,
  },
  {
    name: 'AI SDK tool with dangerous execute',
    pattern: /tool\s*\(\s*\{[^}]*execute\s*:\s*async[^}]*(?:exec|spawn|eval|fs\.|fetch\s*\()[^}]*\}/gi,
    riskType: 'code_execution',
    baseSeverity: 'high',
    description: 'Vercel AI SDK tool with potentially dangerous execute function (shell, eval, fs, or network access).',
    suggestedFix: 'Add validation and restrictions in execute function. Implement allowlists for external operations.',
    requiresRestrictions: true,
  },
  {
    name: 'StreamableUI tool action',
    pattern: /createStreamableUI.*tool.*\{.*action/gi,
    riskType: 'code_execution',
    baseSeverity: 'medium',
    description: 'Streamable UI tool with server action. Ensure proper authorization before state mutations.',
    suggestedFix: 'Verify user authentication and authorization before executing actions. Validate all inputs.',
    requiresUserContext: true,
  },
]

// ============================================================================
// Phase 2: Excessive Agency Detection
// ============================================================================

interface ExcessiveAgencyPattern {
  name: string
  pattern: RegExp
  baseSeverity: VulnerabilitySeverity
  description: string
  suggestedFix: string
  framework?: 'crewai' | 'autogen' | 'langchain' | 'generic'
}

/**
 * Check if agent has proper iteration limits
 */
function hasIterationLimits(context: string): boolean {
  const limitPatterns = [
    /maxIterations\s*[:=]\s*\d{1,2}\b/i,  // 1-99
    /max_iterations\s*[:=]\s*\d{1,2}\b/i,
    /iteration_limit\s*[:=]\s*\d{1,2}\b/i,
    /max_steps\s*[:=]\s*\d{1,2}\b/i,
  ]
  return limitPatterns.some(p => p.test(context))
}

/**
 * Check if agent has timeout configured
 */
function hasTimeoutConfigured(context: string): boolean {
  const timeoutPatterns = [
    /timeout\s*[:=]\s*[1-9]\d*/i,  // Positive number
    /max_execution_time\s*[:=]\s*[1-9]/i,
    /execution_timeout\s*[:=]\s*[1-9]/i,
  ]
  return timeoutPatterns.some(p => p.test(context))
}

/**
 * Check if human-in-the-loop is enabled
 */
function hasHumanInLoop(context: string): boolean {
  const humanPatterns = [
    /humanInLoop\s*[:=]\s*true/i,
    /human_in_loop\s*[:=]\s*True/i,
    /requireApproval\s*[:=]\s*true/i,
    /require_approval\s*[:=]\s*True/i,
    /human_input_mode\s*[:=]\s*['"`](?:ALWAYS|TERMINATE)['"`]/i,
    /confirm_before\s*[:=]\s*true/i,
  ]
  return humanPatterns.some(p => p.test(context))
}

/**
 * Check if Docker is configured for code execution
 */
function hasDockerConfigured(context: string): boolean {
  const dockerPatterns = [
    /use_docker\s*[:=]\s*True/i,
    /docker\s*[:=]\s*true/i,
    /container\s*[:=]\s*true/i,
    /sandboxed\s*[:=]\s*true/i,
  ]
  return dockerPatterns.some(p => p.test(context))
}

/**
 * Check if budget/cost limits are configured
 */
function hasBudgetLimits(context: string): boolean {
  const budgetPatterns = [
    /budgetLimit|budget_limit/i,
    /costLimit|cost_limit/i,
    /max_cost|maxCost/i,
    /spending_limit/i,
    /token_limit|tokenLimit/i,
  ]
  return budgetPatterns.some(p => p.test(context))
}

/**
 * Phase 5: LLM Output Flow Patterns
 * Detect when LLM-generated content flows into dangerous operations
 */
const LLM_OUTPUT_FLOW_PATTERNS: ExcessiveAgencyPattern[] = [
  // ========== LLM Output in Tool Names/Paths ==========
  {
    name: 'LLM output used as tool name',
    pattern: /(?:tools?\[|getTools?\s*\(|callTool\s*\(|invokeTool\s*\(|executeTool\s*\()\s*(?:response|result|output|completion|message|content|llm|ai|model|gpt|claude)\.(?:content|text|tool|toolName|function|name|choice)/gi,
    baseSeverity: 'critical',
    description: 'LLM output used directly as tool name for invocation. An adversarial prompt could cause the agent to call arbitrary tools, bypassing intended restrictions.',
    suggestedFix: 'Validate tool names against a static allowlist: const ALLOWED_TOOLS = [\'read\', \'write\'] as const; if (!ALLOWED_TOOLS.includes(toolName)) throw new Error("Invalid tool")',
    framework: 'generic',
  },
  {
    name: 'LLM output in file path',
    pattern: /(?:fs|file|path|fsp)\.(?:readFile|writeFile|unlink|rm|mkdir|readdir|access|stat|copyFile|rename)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:path|filePath|file|filename|directory|dir)/gi,
    baseSeverity: 'critical',
    description: 'LLM output used directly as file path. Path traversal or arbitrary file access could occur via prompt injection.',
    suggestedFix: 'Validate paths against allowed directories: if (!path.startsWith(ALLOWED_BASE_DIR)) throw new Error("Invalid path"). Use path.resolve() and verify the result stays within bounds.',
    framework: 'generic',
  },
  {
    name: 'LLM output in shell command',
    pattern: /(?:exec|spawn|execFile|execSync|spawnSync)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:command|cmd|script|code|executable|program)/gi,
    baseSeverity: 'critical',
    description: 'LLM output used directly as shell command. Remote code execution via prompt injection.',
    suggestedFix: 'Never use LLM output in shell commands. If necessary, use a strict allowlist of permitted commands and validate arguments.',
    framework: 'generic',
  },
  {
    name: 'LLM output in URL/endpoint',
    pattern: /(?:fetch|axios|http|request|got)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:url|endpoint|href|uri|link|host)/gi,
    baseSeverity: 'high',
    description: 'LLM output used directly as URL or endpoint. SSRF risk via prompt injection.',
    suggestedFix: 'Validate URLs against allowed hosts. Use URL allowlists and block internal IP ranges.',
    framework: 'generic',
  },
  {
    name: 'LLM response destructured into tool call',
    pattern: /(?:const|let|var)\s*\{\s*(?:tool|toolName|function|functionName|action|method)\s*\}\s*=\s*(?:response|result|output|completion|message|llm|ai|model)/gi,
    baseSeverity: 'high',
    description: 'Tool name destructured from LLM response. This pattern suggests dynamic tool selection based on LLM output.',
    suggestedFix: 'Validate extracted tool names against a static allowlist before invocation.',
    framework: 'generic',
  },
  {
    name: 'Dynamic property access with LLM output',
    pattern: /(?:tools|handlers|actions|functions|methods)\s*\[\s*(?:response|result|output|completion|message|content|llm|ai)(?:\.|(?:\s*\[['"`]?(?:tool|name|function|action)))/gi,
    baseSeverity: 'high',
    description: 'Dynamic object property access using LLM output. Could access unintended tools or methods.',
    suggestedFix: 'Use explicit tool dispatch with allowlist validation: if (toolName in SAFE_TOOLS) { SAFE_TOOLS[toolName]() }',
    framework: 'generic',
  },
]

/**
 * Phase 5: Tool Permission Accumulation Patterns
 * Detect unbounded tool registration and permission growth
 */
const TOOL_ACCUMULATION_PATTERNS: ExcessiveAgencyPattern[] = [
  // ========== Unbounded Tool Registration ==========
  {
    name: 'Unbounded tool registration',
    pattern: /(?:agent|tools?|registry)\.(?:registerTool|addTool|push|add|set)\s*\(\s*(?:user|request|req|input|body|data|param)\.(?:tool|function|action|capability)/gi,
    baseSeverity: 'high',
    description: 'Tools registered dynamically from user input without bounds. Users could accumulate unlimited capabilities over time.',
    suggestedFix: 'Use a static allowlist: const ALLOWED_TOOLS = [...] and validate against it. Implement tool count limits.',
    framework: 'generic',
  },
  {
    name: 'Tool array push without limit check',
    pattern: /tools\.push\s*\([^)]+\)(?![\s\S]{0,50}(?:length\s*[<>]|limit|max|ALLOWED|whitelist|allowlist))/gi,
    baseSeverity: 'medium',
    description: 'Tools added to array without checking count limits. Tool list could grow unboundedly.',
    suggestedFix: 'Add limit check: if (tools.length >= MAX_TOOLS) throw new Error("Tool limit reached")',
    framework: 'generic',
  },
  {
    name: 'Dynamic tool loading from user config',
    pattern: /(?:require|import|loadModule|dynamicImport)\s*\(\s*(?:user|request|req|input|body|config)\.(?:tool|module|plugin|extension)/gi,
    baseSeverity: 'critical',
    description: 'Tool modules loaded dynamically from user-controlled paths. Could load arbitrary code.',
    suggestedFix: 'Use a static module registry. Validate module paths against an allowlist.',
    framework: 'generic',
  },
  {
    name: 'Permission grant without authorization check',
    pattern: /(?:grant|add|enable)(?:Permission|Capability|Access)\s*\(\s*[^)]*\)(?![\s\S]{0,30}(?:if|auth|permission|role|admin|isAdmin))/gi,
    baseSeverity: 'high',
    description: 'Permissions granted without visible authorization check. Users could escalate their own privileges.',
    suggestedFix: 'Add authorization check: if (!user.hasRole("admin")) throw new Error("Unauthorized")',
    framework: 'generic',
  },
  {
    name: 'Tool inheritance without restriction',
    pattern: /(?:inherit|extend|merge)(?:Tools|Capabilities|Permissions)\s*\(\s*(?:parent|base|source)\.tools/gi,
    baseSeverity: 'medium',
    description: 'Agent inherits tools from parent without filtering. Could inherit more permissions than intended.',
    suggestedFix: 'Explicitly list inherited tools instead of blanket inheritance. Use allowlist for permitted inherited capabilities.',
    framework: 'generic',
  },
]

/**
 * Phase 5: Database Write Scoping Patterns
 * Detect database writes that may lack proper user scoping
 */
const DB_WRITE_SCOPING_PATTERNS: ExcessiveAgencyPattern[] = [
  // ========== Database Writes Without User Scoping ==========
  {
    name: 'DB insert without userId',
    pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insert|create|save|add)\s*\(\s*\{(?![^}]*(?:userId|user_id|ownerId|owner_id|createdBy|created_by|authorId|author_id))[^}]*(?:content|data|text|body|message)\s*:/gi,
    baseSeverity: 'high',
    description: 'Database insert with content field but no user ID. AI-generated content may not be properly attributed to user.',
    suggestedFix: 'Add user context: db.insert({ content: aiGenerated, userId: ctx.user.id })',
    framework: 'generic',
  },
  {
    name: 'DB insert with AI content unscopedp',
    pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insert|create)\s*\(\s*\{[^}]*:\s*(?:response|result|output|completion|message|ai|llm|model)\.(?:content|text|data|output|result)/gi,
    baseSeverity: 'high',
    description: 'AI-generated content inserted into database. Ensure proper user scoping and content validation.',
    suggestedFix: 'Add user context and validate content: db.insert({ content: validated, userId: ctx.user.id, createdAt: Date.now() })',
    framework: 'generic',
  },
  {
    name: 'Bulk write without tenant filter',
    pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insertMany|createMany|bulkCreate|bulkInsert)\s*\([^)]*\)(?![\s\S]{0,50}(?:tenantId|tenant_id|orgId|org_id|organizationId))/gi,
    baseSeverity: 'medium',
    description: 'Bulk database write without visible tenant scoping. Multi-tenant data isolation may be at risk.',
    suggestedFix: 'Add tenant filter to all bulk operations: records.map(r => ({ ...r, tenantId: ctx.tenant.id }))',
    framework: 'generic',
  },
  {
    name: 'Update without ownership check',
    pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:update|updateOne|updateMany)\s*\(\s*\{[^}]*id\s*:/gi,
    baseSeverity: 'medium',
    description: 'Database update by ID without visible ownership verification. Agent could modify other users\' data.',
    suggestedFix: 'Add ownership check: db.update({ where: { id, userId: ctx.user.id }, data: { ... } })',
    framework: 'generic',
  },
  {
    name: 'Delete without user scoping',
    pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:delete|deleteOne|deleteMany|destroy|remove)\s*\(\s*\{[^}]*id\s*:/gi,
    baseSeverity: 'high',
    description: 'Database delete by ID without user scoping. Agent could delete other users\' data.',
    suggestedFix: 'Add user scoping: db.delete({ where: { id, userId: ctx.user.id } })',
    framework: 'generic',
  },
]

/**
 * Phase 6 Task 1: Tool Parameter Injection Patterns
 * Detect LLM output flowing to tool parameters (not just tool names)
 */
const TOOL_PARAMETER_INJECTION_PATTERNS: ExcessiveAgencyPattern[] = [
  // LLM output in tool parameters
  {
    name: 'LLM output in tool parameters',
    pattern: /tool\s*\(\s*\{[^}]*:\s*(response|output|result|content|message|llmOutput|aiResponse|completion)(\.\w+)*\s*[,}]/gi,
    baseSeverity: 'high',
    description: 'Tool parameters derived from unvalidated LLM output can be manipulated via prompt injection. Attackers could modify tool behavior through crafted responses.',
    suggestedFix: 'Validate and sanitize LLM output before passing as tool parameters. Use schema validation (zod, yup) to ensure expected structure.',
    framework: 'generic',
  },
  // Tool args assigned directly from LLM output
  {
    name: 'Tool args from LLM output',
    pattern: /\bargs\s*=\s*(response|output|result|content|message|llmOutput|aiResponse|completion)(\.\w+)*/gi,
    baseSeverity: 'high',
    description: 'Tool arguments assigned directly from LLM output enable parameter injection. Malicious prompts could inject unexpected arguments.',
    suggestedFix: 'Use schema validation (zod, yup) on LLM output before passing to tools: const validatedArgs = toolArgsSchema.parse(llmOutput)',
    framework: 'generic',
  },
  // Spread LLM output into tool call
  {
    name: 'LLM output spread into tool call',
    pattern: /(?:executeTool|callTool|invokeTool|runTool)\s*\([^)]*\.\.\.(?:response|output|result|content|llmOutput|aiResponse)/gi,
    baseSeverity: 'critical',
    description: 'LLM output spread directly into tool invocation. All LLM-provided fields pass through unvalidated.',
    suggestedFix: 'Destructure and validate specific fields: const { field1, field2 } = schema.parse(llmOutput); executeTool({ field1, field2 })',
    framework: 'generic',
  },
  // Dynamic property access for tool params
  {
    name: 'Dynamic tool param from LLM',
    pattern: /toolParams?\s*\[\s*(response|output|result|llmOutput|aiResponse)\./gi,
    baseSeverity: 'high',
    description: 'Tool parameter accessed dynamically from LLM output. Could access unintended parameters.',
    suggestedFix: 'Use explicit parameter extraction with validation: const param = validateParam(llmOutput.expectedField)',
    framework: 'generic',
  },
  // JSON.parse of LLM output for tool params
  {
    name: 'JSON parsed LLM output as tool params',
    pattern: /JSON\.parse\s*\(\s*(response|output|result|content|llmOutput|aiResponse|completion)(?:\.\w+)?\s*\)[^;]*(?:tool|execute|invoke|call)/gi,
    baseSeverity: 'high',
    description: 'LLM output JSON-parsed and used as tool parameters. Parsed structure could contain malicious fields.',
    suggestedFix: 'Validate parsed JSON against expected schema: const params = toolParamsSchema.parse(JSON.parse(llmOutput))',
    framework: 'generic',
  },
]

/**
 * Phase 6 Task 2: Tool Error Message Injection Patterns
 * Detect raw error exposure to LLM that could leak system information or enable injection
 */
const TOOL_ERROR_INJECTION_PATTERNS: ExcessiveAgencyPattern[] = [
  // Raw error message in tool response
  {
    name: 'Raw error in tool response',
    pattern: /catch\s*\([^)]*\)\s*\{[^}]*(return|resolve)\s*\([^)]*error\.(message|stack|toString)/gi,
    baseSeverity: 'medium',
    description: 'Raw error messages returned to LLM could leak system information (paths, credentials, internal state) or be used for prompt injection attacks.',
    suggestedFix: 'Return sanitized, generic error messages to LLM. Log detailed errors server-side: catch (e) { logger.error(e); return { error: "Operation failed" } }',
    framework: 'generic',
  },
  // Error object in tool return
  {
    name: 'Error object in tool return',
    pattern: /return\s*\{[^}]*error\s*:\s*(?:e|err|error)(?:\s*,|\s*\})/gi,
    baseSeverity: 'medium',
    description: 'Error object returned directly to LLM. Full error objects may contain sensitive stack traces or internal details.',
    suggestedFix: 'Return only error message or generic status: return { error: "Failed to process request", code: "OPERATION_FAILED" }',
    framework: 'generic',
  },
  // Stack trace in response
  {
    name: 'Stack trace in tool response',
    pattern: /return\s*\{[^}]*(?:stack|stackTrace|trace)\s*:\s*(?:e|err|error)\./gi,
    baseSeverity: 'high',
    description: 'Stack trace returned to LLM. Stack traces expose internal code paths, file structures, and potentially sensitive data.',
    suggestedFix: 'Never return stack traces to LLM. Log them server-side for debugging: logger.error({ stack: e.stack }); return { error: "Internal error" }',
    framework: 'generic',
  },
  // Exception details in resolve/reject
  {
    name: 'Exception details in promise resolution',
    pattern: /(?:resolve|reject)\s*\(\s*\{[^}]*(?:exception|error|e)\s*:\s*(?:e|err|error)(?:\.message|\.stack)?/gi,
    baseSeverity: 'medium',
    description: 'Exception details passed in promise resolution. Error information flows to LLM context.',
    suggestedFix: 'Sanitize error information before resolving: resolve({ success: false, error: sanitizeError(e) })',
    framework: 'generic',
  },
  // String interpolation with error
  {
    name: 'Error interpolated in response string',
    pattern: /return\s*[`'"].*\$\{(?:e|err|error)(?:\.message|\.stack)?\}.*[`'"]/gi,
    baseSeverity: 'medium',
    description: 'Error details interpolated into response string. Raw error text could contain sensitive information.',
    suggestedFix: 'Use generic error messages: return `Operation failed: ${getGenericErrorMessage(e.code)}`',
    framework: 'generic',
  },
]

/**
 * Phase 5: Recursive Agent Patterns
 * Detect unbounded agent recursion and self-spawning patterns
 */
const RECURSIVE_AGENT_PATTERNS: ExcessiveAgencyPattern[] = [
  // ========== Unbounded Agent Recursion ==========
  {
    name: 'Recursive agent call without depth limit',
    pattern: /(?:async\s+)?function\s+(?:run|execute|process|handle)?Agent\s*\([^)]*\)\s*\{[\s\S]{0,200}(?:run|execute|process|handle)?Agent\s*\((?![^)]*depth|[^)]*level|[^)]*recursion)/gi,
    baseSeverity: 'high',
    description: 'Agent function calls itself without visible depth parameter. Could recurse indefinitely.',
    suggestedFix: 'Add depth limit: async function runAgent(task, depth = 0) { if (depth > MAX_DEPTH) throw new Error("Max depth"); await runAgent(subtask, depth + 1) }',
    framework: 'generic',
  },
  {
    name: 'Agent spawns sub-agent without limit',
    pattern: /(?:spawn|create|launch|start)(?:Agent|Worker|Task)\s*\([^)]*\)(?![\s\S]{0,50}(?:depth|level|count|limit|max|MAX))/gi,
    baseSeverity: 'medium',
    description: 'Sub-agent spawned without visible depth or count limit. Could lead to unbounded agent proliferation.',
    suggestedFix: 'Track agent depth/count: if (agentCount >= MAX_AGENTS || depth > MAX_DEPTH) throw new Error("Agent limit reached")',
    framework: 'generic',
  },
  {
    name: 'Recursive task processing without bounds',
    pattern: /(?:result|response|output)\.(?:subtasks?|children|next|followUp)\s*\.(?:forEach|map|for)\s*\([^)]*(?:process|run|execute)(?:Task|Agent)/gi,
    baseSeverity: 'high',
    description: 'Tasks processed recursively based on agent output. Agent could generate unlimited subtasks.',
    suggestedFix: 'Limit subtask count: const subtasks = result.subtasks.slice(0, MAX_SUBTASKS). Track total processed tasks.',
    framework: 'generic',
  },
  {
    name: 'Self-improvement loop without termination',
    pattern: /while\s*\([^)]*(?:improve|optimize|refine|enhance)[^)]*\)\s*\{[\s\S]{0,100}(?:agent|model|llm)/gi,
    baseSeverity: 'high',
    description: 'Agent self-improvement loop without clear termination. Could run indefinitely.',
    suggestedFix: 'Add termination conditions: while (iterations < MAX_ITERATIONS && !satisfactory) { ... iterations++ }',
    framework: 'generic',
  },
  {
    name: 'CrewAI agent delegation without depth',
    pattern: /\.delegate\s*\(\s*[^)]*\)(?![\s\S]{0,30}(?:max_delegation|delegation_limit|depth))/gi,
    baseSeverity: 'medium',
    description: 'CrewAI agent delegation without depth limit. Agents could delegate indefinitely to each other.',
    suggestedFix: 'Set delegation limits in agent config: Agent(..., max_delegation_depth=3)',
    framework: 'crewai',
  },
  {
    name: 'LangGraph recursive edge without limit',
    pattern: /\.add_edge\s*\([^)]*,\s*(?:SAME_NODE|self|current_node)/gi,
    baseSeverity: 'medium',
    description: 'LangGraph edge points back to same node without visible limit. Could create infinite loops.',
    suggestedFix: 'Add iteration tracking and conditional edges with max_iterations check.',
    framework: 'langchain',
  },
]

/**
 * Excessive agency patterns for unbounded agent autonomy
 */
const EXCESSIVE_AGENCY_PATTERNS: ExcessiveAgencyPattern[] = [
  // ========== Generic Unbounded Loops ==========
  {
    name: 'Unbounded agent loop',
    pattern: /while\s*\(\s*(?:true|1|True)\s*\)[\s\S]{0,100}(?:agent|step|run|execute|iterate)/gi,
    baseSeverity: 'high',
    description: 'Agent runs in an unbounded loop without explicit termination condition. This can lead to infinite execution, resource exhaustion, or runaway costs.',
    suggestedFix: 'Add maxIterations limit: while (iterations < maxIterations) { ... }. Consider adding timeout and cost limits.',
    framework: 'generic',
  },
  {
    name: 'No iteration limit configured',
    pattern: /maxIterations\s*[:=]\s*(?:-1|null|undefined|None|Infinity|float\s*\(\s*['"`]inf)/gi,
    baseSeverity: 'high',
    description: 'Agent configured with no iteration limit. This allows unbounded execution which can consume excessive resources.',
    suggestedFix: 'Set a reasonable iteration limit: maxIterations: 10 (adjust based on your use case).',
    framework: 'generic',
  },
  {
    name: 'No timeout configured',
    pattern: /timeout\s*[:=]\s*(?:-1|0|null|undefined|None|false|False)/gi,
    baseSeverity: 'medium',
    description: 'Agent timeout is disabled or set to zero. Long-running agents can hang indefinitely.',
    suggestedFix: 'Configure a reasonable timeout: timeout: 300000 (5 minutes). Adjust based on expected execution time.',
    framework: 'generic',
  },
  {
    name: 'Auto-approve without human oversight',
    pattern: /(?:autoApprove|auto_approve)\s*[:=]\s*(?:true|True)/gi,
    baseSeverity: 'high',
    description: 'Agent auto-approves actions without human review. Combined with destructive capabilities, this is dangerous.',
    suggestedFix: 'Enable human-in-the-loop for sensitive operations: autoApprove: false, or implement approval workflows for destructive actions.',
    framework: 'generic',
  },
  {
    name: 'Human-in-loop disabled',
    pattern: /(?:humanInLoop|human_in_loop)\s*[:=]\s*(?:false|False)/gi,
    baseSeverity: 'medium',
    description: 'Human oversight is explicitly disabled. The agent can take actions without human review.',
    suggestedFix: 'Enable human-in-the-loop for sensitive operations: humanInLoop: true. Add confirmation prompts for destructive actions.',
    framework: 'generic',
  },

  // ========== CrewAI Specific ==========
  {
    name: 'CrewAI unsafe code execution mode',
    pattern: /code_execution_mode\s*[:=]\s*['"`]unsafe['"`]/gi,
    baseSeverity: 'critical',
    description: 'CrewAI agent configured with unsafe code execution mode. This allows arbitrary code execution without sandboxing.',
    suggestedFix: 'Use safe mode: code_execution_mode="safe". This runs code in a restricted environment.',
    framework: 'crewai',
  },
  {
    name: 'CrewAI code execution without Docker',
    pattern: /allow_code_execution\s*[:=]\s*True(?![\s\S]{0,50}use_docker\s*[:=]\s*True)/gi,
    baseSeverity: 'high',
    description: 'CrewAI agent allows code execution without Docker containerization. Code runs directly on the host system.',
    suggestedFix: 'Enable Docker for code execution: Agent(..., allow_code_execution=True, code_execution_config={"use_docker": True})',
    framework: 'crewai',
  },

  // ========== AutoGen Specific ==========
  {
    name: 'AutoGen use_docker=False',
    pattern: /(?:code_execution_config|LocalCommandLineCodeExecutor)\s*[\s\S]{0,50}use_docker\s*[:=]\s*False/gi,
    baseSeverity: 'critical',
    description: 'AutoGen code execution configured without Docker. Code executes directly on the host, enabling full system access.',
    suggestedFix: 'Use Docker: code_execution_config={"use_docker": True}. Or use DockerCommandLineCodeExecutor for sandboxed execution.',
    framework: 'autogen',
  },
  {
    name: 'AutoGen NEVER human input mode',
    pattern: /human_input_mode\s*[:=]\s*['"`]NEVER['"`]/gi,
    baseSeverity: 'high',
    description: 'AutoGen agent configured to never request human input. Agent can execute indefinitely without human oversight.',
    suggestedFix: 'Use "ALWAYS" or "TERMINATE" for human_input_mode. "TERMINATE" allows agent to complete but requests input on termination.',
    framework: 'autogen',
  },
  {
    name: 'AutoGen UserProxyAgent without reply limit',
    pattern: /UserProxyAgent\s*\((?![^)]*max_consecutive_auto_reply)[^)]*\)/gi,
    baseSeverity: 'medium',
    description: 'AutoGen UserProxyAgent without max_consecutive_auto_reply limit. Agent can auto-reply indefinitely.',
    suggestedFix: 'Add reply limit: UserProxyAgent(..., max_consecutive_auto_reply=10). Adjust limit based on expected conversation length.',
    framework: 'autogen',
  },

  // ========== LangChain Specific ==========
  {
    name: 'LangChain AgentExecutor without limits',
    pattern: /AgentExecutor\s*\([^)]*(?!max_iterations)[^)]*\)/gi,
    baseSeverity: 'medium',
    description: 'LangChain AgentExecutor without max_iterations. Agent can loop indefinitely on complex tasks.',
    suggestedFix: 'Set iteration limit: AgentExecutor(..., max_iterations=15). Add early_stopping_method="generate" to gracefully stop.',
    framework: 'langchain',
  },

  // ========== Overpermissioned Agents ==========
  {
    name: 'Agent with excessive tools',
    pattern: /tools\s*[:=]\s*\[(?:[^\]]*,){10,}[^\]]*\]/gi,
    baseSeverity: 'medium',
    description: 'Agent configured with more than 10 tools. Overpermissioned agents have larger attack surface if compromised via prompt injection.',
    suggestedFix: 'Follow principle of least privilege. Split into specialized agents with focused tool sets. Remove unused tools.',
    framework: 'generic',
  },
]

/**
 * Patterns for missing authorization in tools
 */
const MISSING_AUTH_PATTERNS: ToolPattern[] = [
  {
    name: 'Tool without user context',
    pattern: /(?:@tool|defineTool|createTool|\.registerTool|\.addTool)\s*\([^)]*(?:async\s+)?(?:function|\().*(?:create|update|delete|modify|write|send)/gi,
    riskType: 'database',
    baseSeverity: 'medium',
    description: 'Tool performs write operations but may not verify user context. Actions could be performed as wrong user.',
    suggestedFix: 'Pass userId as required parameter. Verify user owns/can access the resource before modification.',
    requiresUserContext: true,
  },
]

// ============================================================================
// Main Detection Function
// ============================================================================

/**
 * Main detection function for AI agent tool permission issues
 */
export function detectAIAgentTools(
  content: string,
  filePath: string,
  options?: { parsed?: ParsedFile }
): Vulnerability[] {
  const vulnerabilities: Vulnerability[] = []

  // Skip non-applicable files
  if (isScannerOrFixtureFile(filePath)) return vulnerabilities

  // Only scan files that appear to have agent/tool definitions
  if (!isAgentOrToolFile(filePath, content)) {
    return vulnerabilities
  }

  const lines = options?.parsed?.lines ?? content.split('\n')
  const isTestFile = isTestOrMockFile(filePath)
  const isExample = isExampleDirectory(filePath)
  const isLibrary = isLibraryCode(filePath)

  // Scan for overly permissive tool patterns
  for (const pattern of OVERPERMISSIVE_TOOL_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get tool context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for mitigations (strong vs weak)
      const hasStrong = hasStrongRestrictions(context)
      const hasWeak = hasAccessRestrictions(context)
      const hasUserContext = hasUserContextVerification(context)
      const hasTenantContext = hasTenantContextVerification(context)

      // Determine if issue is fully mitigated
      let isMitigated = true
      let hasPartialMitigation = false
      const missingMitigations: string[] = []

      if (pattern.requiresRestrictions) {
        if (hasStrong) {
          // Strong restrictions = fully mitigated for this requirement
        } else if (hasWeak) {
          // Weak restrictions = partial mitigation
          hasPartialMitigation = true
          missingMitigations.push('verified access restrictions (found mentions but not implementation)')
          isMitigated = false
        } else {
          isMitigated = false
          missingMitigations.push('access restrictions')
        }
      }
      if (pattern.requiresUserContext && !hasUserContext) {
        isMitigated = false
        missingMitigations.push('user context verification')
      }
      if (pattern.requiresTenantContext && !hasTenantContext) {
        isMitigated = false
        missingMitigations.push('tenant/org context verification')
      }

      // Skip if all required mitigations are present with strong verification
      if (isMitigated) continue

      // Calculate severity
      let severity = pattern.baseSeverity
      const isMCPSafe = isMCPFileWithSafePatterns(content, filePath)

      if (isTestFile) {
        severity = 'info'
      } else if (isExample) {
        // Example/demo code - downgrade to info
        severity = 'info'
      } else if (isLibrary) {
        // Library code - tool definitions are intentionally flexible
        // Consumers add restrictions when they use the tools
        severity = 'info'
      } else if (isMCPSafe) {
        // MCP file with proper security controls (sanitization + authorization)
        severity = 'info'
      } else if (hasPartialMitigation || hasUserContext || hasTenantContext) {
        // Partial mitigation - downgrade
        if (severity === 'critical') severity = 'high'
        else if (severity === 'high') severity = 'medium'
      }

      // Build description
      let description = pattern.description
      if (missingMitigations.length > 0) {
        description += ` Missing: ${missingMitigations.join(', ')}.`
      }
      if (isTestFile) {
        description += ' (In test file.)'
      } else if (isExample) {
        description += ' (In example/demo directory - not production code.)'
      } else if (isLibrary) {
        description += ' (Library code - tool definitions are generic; consumers add restrictions.)'
      } else if (isMCPSafe) {
        description += ' (MCP file with sanitization and authorization controls detected.)'
      }

      vulnerabilities.push({
        id: `ai-tool-${filePath}-${lineNumber}-${pattern.riskType}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_overpermissive_tool',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: true, // Always validate - context dependent
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Scan for missing authorization patterns
  for (const pattern of MISSING_AUTH_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get tool context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check if user context is verified
      const hasUserContext = hasUserContextVerification(context)

      // Skip if user context is present
      if (hasUserContext) continue

      let severity = pattern.baseSeverity
      let description = pattern.description

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      }

      vulnerabilities.push({
        id: `ai-tool-auth-${filePath}-${lineNumber}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_overpermissive_tool',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'low', // Lower confidence - needs context
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: true,
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Scan for excessive agency patterns (Phase 2)
  for (const pattern of EXCESSIVE_AGENCY_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get surrounding context for mitigation checks
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for mitigations
      let isMitigated = false
      let isPartiallyMitigated = false
      let description = pattern.description

      // Check iteration limits
      if (hasIterationLimits(context)) {
        isPartiallyMitigated = true
        description += ' (Iteration limits detected nearby.)'
      }

      // Check timeout configuration
      if (hasTimeoutConfigured(context)) {
        isPartiallyMitigated = true
        description += ' (Timeout configured.)'
      }

      // Check human-in-the-loop
      if (hasHumanInLoop(context)) {
        isPartiallyMitigated = true
        description += ' (Human-in-loop enabled.)'
      }

      // Check Docker for code execution patterns
      if (pattern.framework === 'crewai' || pattern.framework === 'autogen') {
        if (hasDockerConfigured(context)) {
          isMitigated = true
          description += ' (Docker containerization detected.)'
        }
      }

      // Check budget limits
      if (hasBudgetLimits(context)) {
        isPartiallyMitigated = true
        description += ' (Budget limits configured.)'
      }

      // Calculate severity
      let severity = pattern.baseSeverity
      const isMCPSafe = isMCPFileWithSafePatterns(content, filePath)

      if (isMitigated) {
        severity = 'info'
      } else if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      } else if (isPartiallyMitigated) {
        // Downgrade if partial mitigations present
        if (severity === 'critical') severity = 'high'
        else if (severity === 'high') severity = 'medium'
        else if (severity === 'medium') severity = 'low'
      }

      // Skip fully mitigated or info-level in non-agent files
      if (isMitigated && severity === 'info') continue

      vulnerabilities.push({
        id: `ai-agency-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: severity === 'info' ? 'low' : 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info' && severity !== 'low',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 5: Scan for LLM output flow patterns (Task 1)
  for (const pattern of LLM_OUTPUT_FLOW_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for validation/allowlist mitigations
      const hasValidation = /(?:allowlist|whitelist|ALLOWED_|validTools|VALID_TOOLS|allowedTools|validateTool|isValidTool|includes|has)\s*\(/i.test(context)
      const hasAllowlistCheck = /if\s*\(\s*!?\s*(?:ALLOWED|VALID|SAFE|permitted).*(?:includes|has|indexOf)/i.test(context)

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasValidation || hasAllowlistCheck) {
        severity = severity === 'critical' ? 'medium' : 'low'
        description += ' (Validation/allowlist detected nearby - verify it covers this case.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-llm-flow-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: severity === 'critical' ? 'high' : 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 5: Scan for tool permission accumulation patterns (Task 2)
  for (const pattern of TOOL_ACCUMULATION_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Skip UI array building patterns (not actual AI tool registration)
      if (pattern.name === 'Tool array push without limit check') {
        // Check if this is in a selector or UI configuration builder
        const isUIPattern =
          // In selectors (zustand/redux pattern)
          /selectors?\.ts$/i.test(filePath) ||
          // In store configuration
          /store\/.*\/selectors/i.test(filePath) ||
          // Building manifest/config arrays
          /manifest\s*:/i.test(lineContent) ||
          /identifier\s*:/i.test(lineContent) ||
          // Map/forEach building UI arrays
          /\.map\s*\([^)]*=>\s*\{[\s\S]{0,100}tools\.push/i.test(content.substring(Math.max(0, match.index - 200), match.index + 100))

        if (isUIPattern) {
          continue // Skip - this is building a UI configuration array
        }
      }

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for limits and authorization
      const hasLimits = /(?:max|limit|MAX_|LIMIT_|\.length\s*[<>])/i.test(context)
      const hasAuthCheck = /(?:if\s*\(.*(?:auth|permission|role|isAdmin|canRegister)|throw.*(?:Unauthorized|Forbidden))/i.test(context)

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasLimits) {
        severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low'
        description += ' (Limit check detected nearby.)'
      }
      if (hasAuthCheck) {
        severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low'
        description += ' (Authorization check detected.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-tool-accum-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info' && severity !== 'low',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 5: Scan for database write scoping patterns (Task 3)
  for (const pattern of DB_WRITE_SCOPING_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for user/tenant scoping
      const hasUserScoping = hasUserContextVerification(context)
      const hasTenantScoping = hasTenantContextVerification(context)

      // Skip if properly scoped
      if (hasUserScoping && hasTenantScoping) continue

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasUserScoping || hasTenantScoping) {
        severity = severity === 'high' ? 'medium' : 'low'
        description += hasUserScoping ? ' (User context detected.)' : ' (Tenant context detected.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-db-scoping-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 5: Scan for recursive agent patterns (Task 4)
  for (const pattern of RECURSIVE_AGENT_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Skip CRUD/data operations that are NOT AI agent spawning
      // These are false positives in apps where "agent" means "chat assistant configuration"
      if (pattern.name === 'Agent spawns sub-agent without limit') {
        const crudPatterns = [
          // Service/SDK method calls - database CRUD for agent configurations
          /(?:service|Service|sdk|SDK|store|Store|runtime|Runtime)\.(?:create|get|update|delete)Agent/i,
          /\.agents\.createAgent/i, // sdk.agents.createAgent
          /agentService\.createAgent/i,
          /agentState\.createAgent/i,
          /marketSDK\.agents\.createAgent/i,
          // React event handlers creating UI entities
          /onClick\s*=\s*\{\s*\(\s*\)\s*=>\s*createAgent/i,
          // Store action patterns
          /await\s+(?:state|store)\w*\.createAgent/i,
          // Builder/Runtime patterns for UI
          /agentBuilder(?:Runtime)?\.createAgent/i,
          /groupAgentBuilderRuntime\.createAgent/i,
        ]
        if (crudPatterns.some(p => p.test(lineContent))) {
          continue // Skip - this is a data CRUD operation, not AI agent spawning
        }
      }

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for depth/count limits
      const hasDepthLimit = /(?:depth|level|recursion)\s*[<>]|MAX_DEPTH|maxDepth|max_depth/i.test(context)
      const hasCountLimit = /(?:count|iterations?)\s*[<>]|MAX_(?:AGENTS|TASKS|ITERATIONS)/i.test(context)

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasDepthLimit || hasCountLimit) {
        severity = severity === 'high' ? 'medium' : 'low'
        description += hasDepthLimit ? ' (Depth limit detected.)' : ' (Count limit detected.)'
      }

      // Check for iteration/timeout limits
      if (hasIterationLimits(context) || hasTimeoutConfigured(context)) {
        severity = severity === 'high' ? 'medium' : severity === 'medium' ? 'low' : severity
        description += ' (Iteration/timeout limits configured.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-recursive-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info' && severity !== 'low',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 6: Scan for tool parameter injection patterns (Task 1)
  for (const pattern of TOOL_PARAMETER_INJECTION_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for validation/schema patterns
      const hasValidation = /(?:zod|yup|joi|schema|validate|safeParse|\.parse\(|validateSchema)/i.test(context)
      const hasSanitization = /(?:sanitize|clean|escape|filter|strip)/i.test(context)

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasValidation) {
        severity = 'low'
        description += ' (Schema validation detected nearby - verify it covers LLM output.)'
      } else if (hasSanitization) {
        severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low'
        description += ' (Sanitization detected nearby.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-tool-param-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: severity === 'critical' ? 'high' : 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info' && severity !== 'low',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  // Phase 6: Scan for tool error message injection patterns (Task 2)
  for (const pattern of TOOL_ERROR_INJECTION_PATTERNS) {
    const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
    let match

    while ((match = regex.exec(content)) !== null) {
      const lineNumber = content.substring(0, match.index).split('\n').length
      const lineContent = lines[lineNumber - 1]?.trim() || ''

      // Skip comments
      if (isComment(lineContent)) continue

      // Get surrounding context
      const { context } = findToolDefinitionContext(content, lineNumber)

      // Check for error sanitization patterns
      const hasSanitizedError = /(?:sanitizeError|genericError|safeError|errorMessage\s*=\s*['"`])/i.test(context)
      const hasLogging = /(?:logger|console)\.\w+\s*\([^)]*(?:error|err|e)\)/i.test(context)

      let description = pattern.description
      let severity = pattern.baseSeverity

      if (hasSanitizedError) {
        severity = 'info'
        description += ' (Error sanitization detected.)'
      } else if (hasLogging) {
        severity = severity === 'high' ? 'medium' : 'low'
        description += ' (Server-side logging detected - verify error is sanitized in response.)'
      }

      if (isTestFile) {
        severity = 'info'
        description += ' (In test file.)'
      } else if (isExample) {
        severity = 'info'
        description += ' (In example/demo directory.)'
      } else if (isLibrary) {
        severity = 'info'
        description += ' (Library code.)'
      }

      vulnerabilities.push({
        id: `ai-tool-error-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
        filePath,
        lineNumber,
        lineContent,
        severity,
        category: 'ai_excessive_agency',
        title: pattern.name,
        description,
        suggestedFix: pattern.suggestedFix,
        confidence: 'medium',
        layer: 2,
        source: 'ai_code' as const,
        requiresAIValidation: severity !== 'info' && severity !== 'low',
        baseConfidence: BASE_CONFIDENCE,
      })
    }
  }

  return vulnerabilities
}
