"use strict";
/**
 * Layer 2: Dangerous Function Call Analysis
 *
 * Detects usage of dangerous functions that can lead to security vulnerabilities.
 * This module orchestrates detection across multiple specialized modules.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DANGEROUS_FUNCTIONS = void 0;
exports.detectDangerousFunctions = detectDangerousFunctions;
const file_classifier_1 = require("../../../parse/file-classifier");
// Pattern definitions
const patterns_1 = require("./patterns");
// Child process detection
const child_process_1 = require("./child-process");
// DOM/XSS detection
const dom_xss_1 = require("./dom-xss");
// JSON.parse detection
const json_parse_1 = require("./json-parse");
// Math.random detection
const math_random_1 = require("./math-random");
// Request validation detection
const request_validation_1 = require("./request-validation");
// Utilities
const control_flow_1 = require("./utils/control-flow");
const schema_validation_1 = require("./utils/schema-validation");
const helpers_1 = require("./utils/helpers");
// Re-export types and patterns for external use
var patterns_2 = require("./patterns");
Object.defineProperty(exports, "DANGEROUS_FUNCTIONS", { enumerable: true, get: function () { return patterns_2.DANGEROUS_FUNCTIONS; } });
const BASE_CONFIDENCE = 0.40;
/**
 * Main detection function for dangerous function calls
 */
function detectDangerousFunctions(content, filePath, options) {
    const vulnerabilities = [];
    // Skip scanner/fixture files to avoid self-detection
    if ((0, file_classifier_1.isScannerOrFixtureFile)(filePath)) {
        return vulnerabilities;
    }
    const lines = options?.parsed?.lines ?? content.split('\n');
    const isTestFile = (0, file_classifier_1.isTestOrMockFile)(filePath);
    lines.forEach((line, index) => {
        // Skip comment lines
        if ((0, file_classifier_1.isComment)(line))
            return;
        for (const funcPattern of patterns_1.DANGEROUS_FUNCTIONS) {
            // Check language filter
            if (!(0, patterns_1.matchesLanguage)(filePath, funcPattern.languages))
                continue;
            const regex = new RegExp(funcPattern.pattern.source, funcPattern.pattern.flags);
            if (regex.test(line)) {
                // Special handling for innerHTML patterns
                if (funcPattern.name === 'innerHTML assignment' ||
                    funcPattern.name === 'dangerouslySetInnerHTML') {
                    handleInnerHTMLPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Note: JSON.parse is now handled by standalone detectJSONParseSafe() function
                // which provides better source-aware severity classification
                // Special handling for eval and Function constructor
                if (funcPattern.name === 'eval() usage' ||
                    funcPattern.name === 'Function constructor') {
                    if (handleEvalPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities)) {
                        break;
                    }
                    continue;
                }
                // Special handling for child_process exec - verify it's not RegExp.exec
                if (funcPattern.name === 'child_process exec') {
                    if (handleChildProcessPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines)) {
                        break;
                    }
                    continue;
                }
                // Special handling for SQL patterns - check for whitelist validation
                if (funcPattern.name === 'Raw SQL query construction' ||
                    funcPattern.name === 'SQL template literal') {
                    handleSQLPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Special handling for dynamic file paths - check for path traversal protection
                if (funcPattern.name === 'Dynamic file path' ||
                    funcPattern.name === 'Path traversal risk') {
                    handleFilePathPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Special handling for Math.random
                if (funcPattern.name === 'Math.random for security') {
                    handleMathRandomPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities);
                    break;
                }
                // Special handling for Python subprocess/os.system
                if (funcPattern.name === 'os.system/subprocess (Python)') {
                    handlePythonSubprocessPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Special handling for regex patterns - check for escaped input
                if (funcPattern.name === 'Potentially unsafe regex') {
                    handleRegexPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Special handling for spread operator with user input
                if (funcPattern.name === 'Spread operator with user input') {
                    handleSpreadPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines);
                    break;
                }
                // Standard handling for all other patterns
                handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
                break; // Only report once per line
            }
        }
    });
    // Additional standalone checks (not in DANGEROUS_FUNCTIONS array)
    // JSON.parse source-aware detection
    (0, json_parse_1.detectJSONParseSafe)(content, filePath, isTestFile, vulnerabilities);
    // request.json() / req.json() schema validation suggestion
    (0, request_validation_1.detectRequestJsonValidation)(content, filePath, isTestFile, vulnerabilities);
    return vulnerabilities;
}
/**
 * Handle innerHTML/dangerouslySetInnerHTML patterns
 */
function handleInnerHTMLPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    // Check if this is a style element (CSS injection is not XSS)
    if ((0, dom_xss_1.isStyleElementInnerHTML)(line, content, index, lines)) {
        // Style elements with CSS are safe - don't report anything
        // CSS cannot execute JavaScript, so there's no XSS risk
        return;
    }
    // Check if this uses static content only - skip entirely (safe)
    if ((0, dom_xss_1.isStaticHTMLContent)(line, content, index, lines)) {
        return; // Static HTML is safe - no finding needed
    }
    // Check if DOMPurify or similar sanitization is used - skip entirely (safe)
    if ((0, dom_xss_1.hasDOMPurifySanitization)(line, content, index, lines)) {
        return; // Sanitized HTML is safe - no finding needed
    }
    // Check if this is a static bootstrap script (e.g., theme/font loader) - skip entirely (safe)
    if ((0, dom_xss_1.isStaticBootstrapScript)(line, content, index, lines)) {
        return; // Static bootstrap scripts are safe - no finding needed
    }
    // Check if this uses output from trusted HTML rendering libraries (Shiki, highlight.js, marked, etc.)
    // These libraries produce sanitized HTML output
    if ((0, dom_xss_1.isTrustedLibraryHTMLOutput)(line, content, index, lines)) {
        return; // Trusted library output is safe - no finding needed
    }
    // Check if this is in LLM prompt context (not XSS - it's prompt injection)
    if ((0, dom_xss_1.isLLMPromptContext)(line, content, filePath)) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-prompt-injection`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'ai_pattern',
            title: 'Potential prompt injection risk',
            description: 'User content is being used in an LLM prompt context. This is NOT XSS (the content goes to an AI, not a DOM). However, untrusted content in prompts may lead to prompt injection attacks.',
            suggestedFix: 'Consider input validation, content filtering, or structured prompts to limit prompt injection risk.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Dynamic content - full severity, needs AI validation
    let severity = funcPattern.severity;
    if (isTestFile) {
        severity = 'low';
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description +
            ' This appears to use dynamic content which increases XSS risk.' +
            (isTestFile ? ' (in test file)' : ''),
        suggestedFix: funcPattern.suggestedFix,
        confidence: isTestFile ? 'low' : 'high',
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural', requiresAIValidation: true, // Dynamic HTML needs validation
    });
}
/**
 * Handle eval and Function constructor patterns
 * Returns true if a finding was added, false otherwise
 */
function handleEvalPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities) {
    // Check if "eval" or "Function" appears inside a string literal
    // e.g., const docs = "Don't use eval() in production"
    // This is NOT an actual eval call, just documentation/comments
    const evalInsideStringPattern = /(['"`])(?:[^\\]|\\.)*?\beval\s*\(.*?\1/;
    const functionInsideStringPattern = /(['"`])(?:[^\\]|\\.)*?\bFunction\s*\(.*?\1/;
    if (evalInsideStringPattern.test(line) || functionInsideStringPattern.test(line)) {
        return true; // Skip - this is just a string mentioning eval, not actual eval()
    }
    // Suppress entirely in test files - test files legitimately test eval behavior
    if (isTestFile) {
        return true; // Skip reporting entirely
    }
    // Check if eval is inside a test assertion (expect(), test(), it(), describe())
    const testAssertionPattern = /\b(expect|test|it|describe)\s*\(/;
    if (testAssertionPattern.test(line)) {
        return true; // Skip reporting - this is testing eval behavior
    }
    // Check if inputs are static literals (low risk) - skip entirely
    if ((0, helpers_1.hasOnlyStaticInputs)(line, content, index)) {
        return true; // Static eval is safe enough - no finding needed
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity: funcPattern.severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description,
        suggestedFix: funcPattern.suggestedFix,
        confidence: 'high',
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural', requiresAIValidation: true, // Code execution patterns need validation
    });
    return true;
}
/**
 * Handle child_process exec patterns
 * Returns true if a finding was added, false otherwise
 */
function handleChildProcessPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    // First check if this is actually from child_process (not RegExp.exec)
    const isExecMatch = /\bexec\s*\(/.test(line);
    const isOtherMatch = /\b(execSync|spawn|spawnSync|execFile)\s*\(/.test(line);
    if (isExecMatch && !isOtherMatch) {
        // This matched 'exec(' - verify it's from child_process
        if (!(0, child_process_1.isChildProcessExec)(content, line)) {
            // This is RegExp.exec or similar - skip
            return false;
        }
    }
    else if (isOtherMatch) {
        // This matched spawn/execSync/etc - verify child_process import
        if (!(0, child_process_1.isChildProcessSpawn)(content, line)) {
            // No child_process import - skip
            return false;
        }
    }
    // Check if arguments are validated via allowlist
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, index - 15);
    const contextEnd = Math.min(_lines.length, index + 5);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    // Detect allowlist validation patterns before exec/spawn
    const hasArgAllowlist = /allowedArgs\.includes\s*\(/i.test(context) ||
        /if\s*\(\s*!?allowedArgs\.includes/i.test(context) ||
        /if\s*\(\s*!?\w+Args\.includes/i.test(context) ||
        /validArgs\.includes/i.test(context) ||
        // ALLOWED_COMMANDS pattern (common naming convention)
        /ALLOWED_\w+\.includes\s*\(/i.test(context) ||
        /if\s*\(\s*!?ALLOWED_\w+\.includes/i.test(context) ||
        // allowedCommands, validCommands, safeCommands
        /allowed(?:Commands?|Cmds?)\.includes\s*\(/i.test(context) ||
        /valid(?:Commands?|Cmds?)\.includes\s*\(/i.test(context) ||
        /safe(?:Commands?|Cmds?)\.includes\s*\(/i.test(context) ||
        // Generic whitelist/allowlist check
        /(?:whitelist|allowlist)\.includes\s*\(/i.test(context);
    // execFile with hardcoded command is safe (safer than exec)
    const isExecFileWithHardcodedCmd = /execFile\s*\(\s*['"][^'"]+['"]/i.test(line);
    if (hasArgAllowlist || isExecFileWithHardcodedCmd) {
        return true; // Allowlisted or execFile with hardcoded command - safe
    }
    if ((0, helpers_1.hasOnlyStaticInputs)(line, content, index)) {
        return true; // Static command is safe - no finding needed
    }
    // Check for build/script context with hardcoded command + args array
    const isBuildScript = /(build|generate|format|lint|setup|deploy|migrate|compile)/i.test(filePath) ||
        /\/(scripts?|tools?|bin)\//i.test(filePath);
    if (isBuildScript) {
        // spawnSync("cmd", ["arg1", "arg2"]) with string literal command is safe in build scripts
        const hasHardcodedCommand = /spawn(?:Sync)?\s*\(\s*['"][^'"]+['"]/.test(line);
        if (hasHardcodedCommand) {
            vulnerabilities.push({
                id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
                filePath,
                lineNumber: index + 1,
                lineContent: line.trim(),
                severity: 'info',
                category: 'dangerous_function',
                title: funcPattern.name + ' (build script)',
                description: 'Shell command execution in build/tooling script with hardcoded command. Build scripts are developer-controlled.',
                suggestedFix: 'Ensure this script is not exposed to untrusted input.',
                confidence: 'low',
                baseConfidence: BASE_CONFIDENCE,
                layer: 2,
                source: 'structural',
            });
            return true;
        }
    }
    // Check for desktop app or MCP server context
    // These contexts legitimately spawn processes
    const isDesktopApp = (0, file_classifier_1.isDesktopAppContext)(filePath);
    const isMcpServer = (0, file_classifier_1.isMcpServerContext)(filePath);
    if (isDesktopApp || isMcpServer) {
        // Desktop apps and MCP servers legitimately spawn processes
        // Still report but with reduced severity and context
        const contextType = isDesktopApp ? 'desktop app' : 'MCP server';
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'medium', // Reduced from high
            category: 'dangerous_function',
            title: `${funcPattern.name} (${contextType})`,
            description: `${funcPattern.description} (Expected in ${contextType} context - verify input validation)`,
            suggestedFix: 'Ensure command arguments from IPC are validated against an allowlist.',
            confidence: 'medium',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return true;
    }
    // Dynamic command - report with standard severity
    let severity = funcPattern.severity;
    let confidence = 'high';
    if (isTestFile) {
        if (severity === 'critical') {
            severity = 'medium';
        }
        else if (severity === 'high') {
            severity = 'low';
        }
        else {
            severity = 'info';
        }
        confidence = 'low';
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description + (isTestFile ? ' (in test file)' : ''),
        suggestedFix: funcPattern.suggestedFix,
        confidence,
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural',
    });
    return true;
}
/**
 * Handle SQL injection patterns
 */
function handleSQLPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    // Check for whitelist validation - skip entirely (safe)
    if ((0, schema_validation_1.hasSQLWhitelistValidation)(content, index)) {
        return; // Whitelist validated - safe, no finding needed
    }
    // Check for ORM methods (not raw SQL) - skip entirely (safe)
    // Prisma: prisma.user.findMany({ where: {...} })
    // Sequelize: Model.findAll({ where: {...} })
    // TypeORM: repository.find({ where: {...} })
    const ormMethodPattern = /\.(findMany|findUnique|findFirst|findAll|find|create|update|delete|upsert)\s*\(\s*\{/i;
    if (ormMethodPattern.test(line)) {
        return; // ORM method - safe, no finding needed
    }
    // Check for parameterized queries - skip entirely (safe)
    // e.g., db.query('SELECT * FROM users WHERE id = $1', [userId])
    const parameterizedQueryPattern = /\.\s*(query|execute)\s*\(\s*['"`][^${}]+['"`]\s*,\s*\[/;
    if (parameterizedQueryPattern.test(line)) {
        return; // Parameterized query - safe, no finding needed
    }
    // Knex .raw() with ? placeholders and array binding - this IS parameterized
    // e.g., db.raw(`"table"."col" + ?`, [value]) or db.raw('SELECT ... WHERE id = ?', [id])
    const knexRawParameterized = /\.raw\s*\(\s*[`'"]/i.test(line) &&
        /\?\s*[`'"]\s*,\s*\[/.test(line);
    if (knexRawParameterized) {
        return; // Knex .raw() with ? placeholders is parameterized - safe
    }
    // Knex .raw() with only const enum/table name interpolation (not user input)
    // e.g., db.raw(`"${TableName.Users}"."col"`) where TableName is a const enum
    const knexRawConstInterpolation = /\.raw\s*\(\s*`/.test(line) &&
        /\$\{[A-Z][A-Za-z]*\.[A-Z]/.test(line);
    if (knexRawConstInterpolation) {
        const interpolations = line.match(/\$\{([^}]+)\}/g) || [];
        const allConst = interpolations.every(i => /^\$\{[A-Z_][A-Z_a-z]*\./.test(i));
        if (allConst) {
            return; // Only const enum interpolation - safe
        }
    }
    // Knex .raw() for SET statement_timeout (infrastructure, not user input)
    // e.g., trx.raw(`SET statement_timeout = ${QUERY_TIMEOUT_MS}`)
    const isSetStatement = /\.raw\s*\(\s*[`'"]SET\s+/i.test(line);
    if (isSetStatement) {
        return; // SET statements are infrastructure config, not queries with user data
    }
    // DROP TRIGGER / DDL statements from migration/schema files
    const isDDLStatement = /\.raw\s*\(\s*[`'"](DROP|CREATE|ALTER)\s+/i.test(line) &&
        /(migration|schema|seed)/i.test(filePath);
    if (isDDLStatement) {
        return; // DDL in migration/schema files - not user-facing
    }
    // Check for Prisma tagged template literal - these ARE parameterized (safe)
    // Prisma's $queryRaw`...${var}...` treats ${} as parameterized values, not string interpolation
    // e.g., prisma.$queryRaw`SELECT * FROM users WHERE id = ${userId}`
    const prismaTaggedTemplatePattern = /\$queryRaw\s*`[^`]*\$\{/i;
    if (prismaTaggedTemplatePattern.test(line)) {
        return; // Prisma tagged template - parameterized and safe, no finding needed
    }
    // Check for schema-validated input (zod .enum() for table/column names)
    // e.g., z.enum(['users', 'posts']).parse(input) followed by SQL
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, index - 20);
    const contextEnd = index;
    const previousContext = _lines.slice(contextStart, contextEnd).join('\n');
    // Detect zod enum validation for SQL identifiers
    const hasSchemaValidation = /z\s*\.\s*enum\s*\(\s*\[['"][^'"]+['"]/i.test(previousContext) ||
        /\.parse\s*\(\s*JSON\.parse/.test(previousContext) ||
        // Allow validated table/column names from parsed schema
        /schema\.parse/.test(previousContext) ||
        /const\s+parsed\s*=\s*schema/.test(previousContext);
    if (hasSchemaValidation) {
        return; // Schema-validated SQL identifiers - safe, no finding needed
    }
    // No whitelist - report with standard severity
    let severity = funcPattern.severity;
    let confidence = 'high';
    if (isTestFile) {
        if (severity === 'critical') {
            severity = 'medium';
        }
        else if (severity === 'high') {
            severity = 'low';
        }
        else {
            severity = 'info';
        }
        confidence = 'low';
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description + (isTestFile ? ' (in test file)' : ''),
        suggestedFix: funcPattern.suggestedFix,
        confidence,
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural',
    });
}
/**
 * Handle dynamic file path patterns
 */
function handleFilePathPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    // Check for desktop app context (Electron, Tauri, etc.)
    // Desktop apps legitimately access filesystem
    const isDesktopApp = (0, file_classifier_1.isDesktopAppContext)(filePath);
    // Check for file loader context
    // File loaders legitimately access filesystem to process files
    const isFileLoader = (0, file_classifier_1.isFileLoaderContext)(filePath);
    // Desktop apps and file loaders are expected to access filesystem
    if (isDesktopApp || isFileLoader) {
        const contextType = isDesktopApp ? 'desktop app' : 'file loader';
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: `${funcPattern.name} (${contextType})`,
            description: `Dynamic file path in ${contextType} context. File system access is expected functionality. Verify path inputs are validated.`,
            suggestedFix: 'Ensure file paths are validated and constrained to expected directories.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Check file context for CLI/tooling (lower risk)
    const isCLITool = /\/(cli|scripts?|tools?|bin)\//i.test(filePath) ||
        /cli\.(ts|js)$/i.test(filePath);
    // Check for GitHub Action context (workflow-controlled paths)
    const isGitHubAction = /\/(github-action|actions?)\//i.test(filePath) ||
        /action\.(ts|js)$/i.test(filePath);
    // Check for utility/helper file context (called by trusted code)
    const isUtilityFile = /\/(utils?|helpers?|lib|common|shared)\//i.test(filePath) ||
        /(util(s)?|helper(s)?|checksum|hash)\.(ts|js)$/i.test(filePath);
    // Check for server infrastructure/config files (transport, signing, credentials)
    // These files read/write config-controlled paths, not user input
    const isServerInfrastructureFile = /\/(transports?|signing|credentials?|certificates?|certs?)\//i.test(filePath) ||
        /\/(config|infrastructure|provisioning)\//i.test(filePath) ||
        /(transport|signer|credential|certificate)\.(ts|js)$/i.test(filePath);
    // Get surrounding context for protection check
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, index - 10);
    const contextEnd = Math.min(_lines.length, index + 10);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    // Check if path comes from directory iteration (fs.readdir, fs.readdirSync)
    // These paths are filesystem-controlled, not user input
    const hasDirectoryIteration = /\b(readdir|readdirSync|opendir|opendirSync)\s*\(/.test(content) &&
        (/for\s*\(\s*(const|let|var)\s+\w+\s+of/.test(context) ||
            /\.forEach\s*\(/.test(context) ||
            /\.map\s*\(/.test(context) || // array.map() iteration
            /pMap\s*\(/.test(context) || // p-map library (parallel map)
            /Promise\.all\s*\(/.test(context) || // Promise.all mapping
            /entry\.(name|isFile|isDirectory)/.test(context) ||
            /dirent\.(name|isFile|isDirectory)/.test(context));
    if ((0, helpers_1.hasPathTraversalProtection)(context, line)) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (protected)',
            description: 'Dynamic file path with path traversal protection detected. Verify the protection is complete and covers all attack vectors.',
            suggestedFix: 'Ensure path normalization and base directory checks are applied consistently.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Directory iteration paths are filesystem-controlled (not user input)
    if (hasDirectoryIteration) {
        // Skip entirely - paths from fs.readdir are not user-controlled
        return;
    }
    // Check for Object.entries/keys/values over hardcoded objects
    // Pattern: for (const [key, val] of Object.entries(STATIC_OBJ))
    const hasHardcodedObjectIteration = (() => {
        // Look for Object.entries/keys/values in context
        const hasObjectIteration = /Object\.(entries|keys|values)\s*\(/.test(context);
        if (!hasObjectIteration)
            return false;
        // Check if the object being iterated is defined as a const literal nearby
        // Pattern: const objName = { ... }; ... Object.entries(objName)
        const objectMatch = context.match(/Object\.(entries|keys|values)\s*\(\s*(\w+)\s*\)/);
        if (!objectMatch)
            return false;
        const objName = objectMatch[2];
        // Check if objName is defined as a const object literal in the file
        const isConstObject = new RegExp(`const\\s+${objName}\\s*=\\s*\\{`).test(content);
        return isConstObject;
    })();
    if (hasHardcodedObjectIteration) {
        // Skip entirely - iterating over hardcoded object, not user input
        return;
    }
    // GitHub Action paths are workflow-controlled (not arbitrary user input)
    if (isGitHubAction) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (GitHub Action)',
            description: 'Dynamic file path in GitHub Action. Paths are typically controlled by workflow configuration, not arbitrary user input.',
            suggestedFix: 'Verify paths come from trusted action inputs or environment variables.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // CLI tools with dynamic paths are lower risk (trusted operator)
    if (isCLITool) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (CLI tool)',
            description: 'Dynamic file path in CLI tool. CLI tools typically have trusted operators, but consider adding path validation if user input is involved.',
            suggestedFix: 'Add path validation if accepting paths from untrusted sources.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Utility/helper files with function parameters are lower risk (called by trusted code)
    // Check if path variable appears to be a function parameter, not from request
    const hasRequestData = /req\.(params|query|body)|request\.(params|query|body)/i.test(context);
    if (isUtilityFile && !hasRequestData) {
        // Skip entirely - utility functions receive paths from trusted callers
        return;
    }
    // Server infrastructure files (signing, transport, credentials) use config-controlled paths
    // These paths come from environment variables or internal configuration, not user input
    if (isServerInfrastructureFile && !hasRequestData) {
        // Check if path comes from environment variables or function parameters
        const hasEnvVarPath = /process\.env\.|import\.meta\.env\.|env\s*\(/i.test(context);
        const hasConfigPath = /config\.|settings\.|credentials?\./i.test(context);
        const hasCertPath = /certPath|keyPath|credentialsPath|googleApplicationCredentials/i.test(context);
        if (hasEnvVarPath || hasConfigPath || hasCertPath) {
            // Skip entirely - paths from env vars/config are not user-controlled
            return;
        }
    }
    // Check if file path variable comes from environment variable wrapper function
    // Common pattern: env('VAR_NAME') || 'default', process.env.VAR, etc.
    const hasEnvVarSource = /env\s*\(\s*['"][^'"]+['"]\s*\)|process\.env\.\w+|import\.meta\.env\.\w+/i.test(context);
    const hasOnlyConfigSource = hasEnvVarSource && !hasRequestData;
    if (hasOnlyConfigSource) {
        // Path comes from environment variable, not user input - skip
        return;
    }
    // Standard handling for unprotected paths
    let severity = funcPattern.severity;
    let confidence = 'high';
    if (isTestFile) {
        if (severity === 'critical') {
            severity = 'medium';
        }
        else if (severity === 'high') {
            severity = 'low';
        }
        else {
            severity = 'info';
        }
        confidence = 'low';
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description + (isTestFile ? ' (in test file)' : ''),
        suggestedFix: funcPattern.suggestedFix,
        confidence,
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural',
    });
}
/**
 * Handle Math.random patterns with context-aware severity
 */
function handleMathRandomPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities) {
    // Skip entirely for certain contexts
    if ((0, math_random_1.shouldSkipMathRandom)(content, filePath, index)) {
        return;
    }
    // Analyze context
    const functionName = (0, control_flow_1.extractFunctionContext)(content, index);
    const functionIntent = (0, math_random_1.classifyFunctionIntent)(functionName);
    const toStringPattern = (0, math_random_1.analyzeToStringPattern)(line);
    const variableName = (0, math_random_1.extractMathRandomVariableName)(line);
    const variableRisk = (0, math_random_1.classifyVariableNameRisk)(variableName);
    const context = (0, math_random_1.analyzeMathRandomContext)(content, filePath, index);
    // Determine severity based on all factors
    let severity;
    let confidence;
    let description;
    let suggestedFix;
    let explanation = '';
    // Variable name indicates security risk - check this FIRST before toString patterns
    // This ensures 'secret', 'token', 'key' etc. are always flagged as high
    if (variableRisk === 'high') {
        severity = 'high';
        confidence = 'high';
        // Update context description to indicate security context
        context.contextDescription = 'security-sensitive variable';
        description = `Math.random() assigned to security-sensitive variable '${variableName}'. Math.random() is NOT cryptographically secure.`;
        suggestedFix =
            'Use crypto.randomBytes() or crypto.getRandomValues() for security-sensitive values.';
    }
    // Security-sensitive contexts get high severity
    else if (context.inSecurityContext || functionIntent === 'security') {
        severity = 'high';
        confidence = 'high';
        description =
            'Math.random() is being used in a security-sensitive context. This is NOT cryptographically secure and should be replaced.';
        suggestedFix =
            'Use crypto.randomBytes() for Node.js or crypto.getRandomValues() for browsers.';
    }
    // Test contexts get info severity
    else if (context.inTestContext) {
        severity = 'info';
        confidence = 'low';
        description =
            'Math.random() in test context. Acceptable for test data generation.';
        suggestedFix = 'No change needed for test data.';
    }
    // UUID/CAPTCHA generation - legitimate use
    else if (functionIntent === 'uuid' || functionIntent === 'captcha') {
        severity = 'info';
        confidence = 'low';
        description = `Math.random() used for ${functionIntent === 'uuid' ? 'ID generation' : 'CAPTCHA/puzzle'} (not security-sensitive).`;
        suggestedFix =
            'For truly unique IDs, consider crypto.randomUUID(). For security tokens, use crypto.randomBytes().';
    }
    // Demo/seed data - legitimate use
    else if (functionIntent === 'demo') {
        severity = 'info';
        confidence = 'low';
        description =
            'Math.random() for demo/seed data generation. Acceptable for non-production data.';
        suggestedFix = 'No change needed for demo/seed data.';
    }
    // Short UI IDs (.toString(36).substring(2,9)) - info
    else if (toStringPattern.intent === 'short-ui-id') {
        severity = 'info';
        confidence = 'low';
        explanation = ` (${toStringPattern.truncationLength || '?'}-char string)`;
        // Override context description for UI IDs
        context.contextDescription = 'UI identifier generation';
        description = `Math.random() generating short UI identifier${explanation}. Acceptable for React keys, temp IDs.`;
        suggestedFix =
            'For security tokens, use crypto.randomBytes(). For unique IDs, crypto.randomUUID().';
    }
    // Business IDs (.toString(36) with medium truncation) - low
    else if (toStringPattern.intent === 'business-id') {
        severity = 'low';
        confidence = 'low';
        explanation = variableName ? ` (variable: ${variableName})` : '';
        description = `Math.random() generating business identifier${explanation}. Verify this is not used for security purposes.`;
        suggestedFix =
            'For business IDs, crypto.randomUUID() is preferred. For security tokens, use crypto.randomBytes().';
    }
    // Full token (.toString(36) without truncation) - severity based on variable name
    else if (toStringPattern.intent === 'full-token') {
        // Note: high-risk variable names are already handled above
        if (variableRisk === 'low') {
            severity = 'low';
            confidence = 'low';
        }
        else {
            severity = 'medium';
            confidence = 'medium';
        }
        explanation = variableName ? ` (variable: ${variableName})` : '';
        description = `Math.random() generating full-length random string${explanation}. This pattern is often used for security tokens.`;
        suggestedFix =
            'Use crypto.randomBytes() for security tokens. Use crypto.randomUUID() for unique IDs.';
    }
    // UI/cosmetic context - info (skeleton widths, animations, visual effects)
    else if (context.inUIContext) {
        severity = 'info';
        confidence = 'low';
        description =
            'Math.random() in UI/cosmetic context. Acceptable for visual effects, skeleton loading, animations.';
        suggestedFix = 'No change needed for UI/cosmetic randomness.';
    }
    // Business logic context - low
    else if (context.inBusinessLogicContext) {
        severity = 'low';
        confidence = 'low';
        description =
            'Math.random() in business logic context (backoff, sampling, experiments). Verify this is not for security.';
        suggestedFix =
            'If used for security, replace with crypto.randomBytes(). Otherwise, usage is acceptable.';
    }
    // Unknown context - medium
    else {
        severity = 'medium';
        confidence = 'medium';
        description =
            'Math.random() is being used. Verify this is not for security-critical purposes like tokens, session IDs, or cryptographic operations.';
        suggestedFix =
            'If used for security, replace with crypto.randomBytes(). For unique IDs, use crypto.randomUUID()';
    }
    // Update title with context
    const title = `Math.random() in ${context.contextDescription}${explanation}`;
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title,
        description,
        suggestedFix,
        confidence,
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural',
    });
}
/**
 * Extract the full Python function call block starting from the trigger line.
 * Uses paren-balancing to collect up to `maxLines` forward, capturing multi-line calls.
 * Returns the joined block string.
 */
function extractPythonCallBlock(lines, startIndex, maxLines = 10) {
    let depth = 0;
    let started = false;
    const blockLines = [];
    for (let i = startIndex; i < Math.min(lines.length, startIndex + maxLines); i++) {
        const ln = lines[i];
        blockLines.push(ln);
        for (const ch of ln) {
            if (ch === '(') {
                depth++;
                started = true;
            }
            else if (ch === ')') {
                depth--;
            }
        }
        // Once we've opened at least one paren and balanced back to 0, we're done
        if (started && depth <= 0)
            break;
    }
    return blockLines.join('\n');
}
/**
 * Check if a Python list (as a string) contains only static string literals.
 * Returns true if every element is a plain string literal (no f-strings, no variables).
 */
function isPythonListAllStatic(listContent) {
    // Remove the outer brackets
    const inner = listContent.replace(/^\[/, '').replace(/\]$/, '').trim();
    if (!inner)
        return true; // empty list
    // Split on commas (rough — good enough for typical subprocess args)
    const elements = inner.split(',').map(e => e.trim()).filter(e => e.length > 0);
    for (const el of elements) {
        // Must be a plain string literal: 'foo', "bar", or """...""" / '''...'''
        // Reject f-strings, variables, function calls
        if (/^f['"`]/.test(el))
            return false; // f-string
        if (/^['"]/.test(el) && /['"]$/.test(el))
            continue; // simple string literal
        if (/^"""/.test(el) || /^'''/.test(el))
            continue; // triple-quoted
        return false; // variable, function call, or other expression
    }
    return true;
}
/**
 * Handle Python subprocess/os.system patterns with multi-line awareness.
 *
 * Decision tree:
 * 1. os.system(...)                              → HIGH (always dangerous)
 * 2. shell=True in call block?                   → HIGH
 * 3. First arg is inline list [...]?
 *    a. All string literals, no f-strings        → SKIP (safe)
 *    b. Has f-strings or variables               → LOW (list args prevent shell injection)
 * 4. First arg is a variable name?
 *    a. Resolved to list nearby, all static      → SKIP
 *    b. Resolved to list nearby, has dynamics    → LOW
 *    c. Can't resolve                            → LOW (unresolved, flag for review)
 * 5. f-string as direct arg (not in list)?       → HIGH (command injection)
 * 6. Everything else                             → HIGH (fallback)
 */
function handlePythonSubprocessPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    // 1. os.system is always dangerous - no safe usage
    if (/os\.system\s*\(/i.test(line)) {
        handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
        return;
    }
    const _lines = lines ?? content.split('\n');
    // Extract the full multi-line call block (up to 10 lines forward)
    const callBlock = extractPythonCallBlock(_lines, index);
    // 2. Check for shell=True across the entire call block
    const hasShellTrue = /shell\s*=\s*True/i.test(callBlock);
    if (hasShellTrue) {
        handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
        return;
    }
    // 3. Check for inline list args in the call block (not just same line)
    const inlineListMatch = callBlock.match(/subprocess\.(run|call|check_output|Popen)\s*\(\s*\[([\s\S]*?)\]/i);
    if (inlineListMatch) {
        const listContent = '[' + inlineListMatch[2] + ']';
        if (isPythonListAllStatic(listContent)) {
            // 3a. All static string literals → SKIP (safe)
            return;
        }
        // 3b. Has f-strings or variables → LOW (list args prevent shell injection)
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: isTestFile ? 'info' : 'low',
            category: 'dangerous_function',
            title: funcPattern.name + ' (list args)',
            description: 'subprocess with list arguments (safer than shell=True). Some arguments contain variables or f-strings — verify they are validated.',
            suggestedFix: 'Ensure dynamic arguments are validated and sanitized.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // 4. Check for variable reference as first arg
    // Pattern: subprocess.run(args, ...) or subprocess.check_output(cmd, ...)
    const varArgMatch = callBlock.match(/subprocess\.(run|call|check_output|Popen)\s*\(\s*([a-zA-Z_]\w*)\s*[,)]/i);
    if (varArgMatch) {
        const varName = varArgMatch[2];
        // Look backwards up to 15 lines for assignment: varName = [...]
        const searchStart = Math.max(0, index - 15);
        const previousLines = _lines.slice(searchStart, index + 1).join('\n');
        // Match varName = [...] assignment (possibly multi-line)
        const assignmentPattern = new RegExp(varName + '\\s*=\\s*\\[([\\s\\S]*?)\\]', 'i');
        const assignmentMatch = previousLines.match(assignmentPattern);
        if (assignmentMatch) {
            const listContent = '[' + assignmentMatch[1] + ']';
            if (isPythonListAllStatic(listContent)) {
                // 4a. Variable resolves to all-static list → SKIP
                return;
            }
            // 4b. Variable resolves to list with dynamic elements → LOW
            vulnerabilities.push({
                id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
                filePath,
                lineNumber: index + 1,
                lineContent: line.trim(),
                severity: isTestFile ? 'info' : 'low',
                category: 'dangerous_function',
                title: funcPattern.name + ' (list args via variable)',
                description: `subprocess called with variable '${varName}' which resolves to a list. List arguments prevent shell injection, but some elements are dynamic.`,
                suggestedFix: 'Ensure dynamic list elements are validated and sanitized.',
                confidence: 'low',
                baseConfidence: BASE_CONFIDENCE,
                layer: 2,
                source: 'structural',
            });
            return;
        }
        // 4c. Can't resolve the variable — flag for review at LOW
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: isTestFile ? 'info' : 'low',
            category: 'dangerous_function',
            title: funcPattern.name + ' (unresolved variable)',
            description: `subprocess called with variable '${varName}' — could not resolve its value nearby. If it is a list, shell injection risk is low.`,
            suggestedFix: 'Verify the variable is a list (not a string) and arguments are validated.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // 5. f-string as direct arg (not inside a list) → HIGH (command injection)
    const hasFStringDirectArg = /subprocess\.(run|call|check_output|Popen)\s*\(\s*f['"`]/i.test(callBlock);
    if (hasFStringDirectArg) {
        handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
        return;
    }
    // 6. Everything else → HIGH (fallback)
    handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
}
/**
 * Handle regex patterns - check for escaped input
 * Pattern: new RegExp(escapedInput) or new RegExp(input.replaceAll(...escaped...))
 */
function handleRegexPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, index - 15);
    const contextEnd = Math.min(_lines.length, index + 3);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    // Check for RegExp object property access (.source, .flags)
    // This indicates input is already a validated RegExp, not user string
    // e.g., new RegExp(existingRegex.source, existingRegex.flags)
    const isRegExpFromRegExp = /\.source\s*[,)\s]/.test(line);
    if (isRegExpFromRegExp) {
        return; // Safe - .source only exists on RegExp objects (already validated)
    }
    // Check for escaping ON THE SAME LINE as new RegExp() - this is a strong signal
    const sameLineEscapingPatterns = [
        /\.replaceAll\s*\([^)]*\)\s*[,)]/i, // .replaceAll(...)) - escaping before RegExp
        /escape\w*\s*\([^)]*\)\s*[,)]/i, // escapeRegExp(input)) - function result used
        /\.replace\s*\([^,]+,[^)]+\)\s*[,)]/i, // .replace(..., ...) followed by closing
    ];
    if (sameLineEscapingPatterns.some(p => p.test(line))) {
        return; // Safe - escaping applied on same line before RegExp construction
    }
    // Check previous 5 lines for escaping assignment (extended from 3 to catch multi-line patterns)
    const prevLinesStart = Math.max(0, index - 5);
    const prevLines = _lines.slice(prevLinesStart, index + 1).join('\n');
    // Check for escaping patterns before new RegExp
    const escapingPatterns = [
        // Direct escaping function calls
        /escapeRegExp\s*\(/i, // escapeRegExp(input)
        /escapeString\s*\(/i, // escapeString(input)
        /escape\s*\(\s*pattern/i, // escape(pattern)
        /escapeForRegex\s*\(/i, // escapeForRegex(input)
        /regexEscape\s*\(/i, // regexEscape(input)
        // replaceAll with regex escape pattern - original strict patterns
        /\.replaceAll\s*\(\s*\/\[.*\\\\\]\s*\/[gi]*\s*,\s*['"`]\\\\?\$&['"`]\s*\)/, // .replaceAll(/[special]/g, '\\$&')
        /\.replace\s*\(\s*\/\[.*\\\\\]\s*\/[gi]*\s*,\s*['"`]\\\\?\$&['"`]\s*\)/, // .replace(/[special]/g, '\\$&')
        // More permissive $& replacement detection (the escape marker)
        // $& is the regex replacement marker that inserts the matched string - used for escaping
        /\.replace(?:All)?[\s\S]*?['"`]\\*\$&['"`]/, // .replace/.replaceAll with $& anywhere in call
        /\.replaceAll?[^;]*\$&/, // .replace/.replaceAll until semicolon with $&
        // Lodash/underscore escapeRegExp
        /_\.escapeRegExp\s*\(/, // _.escapeRegExp(input)
        /lodash.*escapeRegExp/i, // lodash.escapeRegExp
        // Variable assignment with escaping (check previous lines)
        /escaped\w*\s*=.*\.replace/i, // escapedInput = input.replace(...)
        /safe\w*\s*=.*escape/i, // safePattern = escapeRegExp(...)
    ];
    // Check both previous lines and full context
    const hasEscaping = escapingPatterns.some(p => p.test(line) || p.test(prevLines) || p.test(context));
    // Check for try-catch wrapping (ReDoS contained)
    const hasTryCatch = /try\s*\{[^}]*new\s+RegExp/i.test(context) ||
        (context.includes('try {') && _lines.slice(Math.max(0, index - 5), index + 1).some(l => /try\s*\{/.test(l)));
    // Check for configuration-based patterns (trusted input)
    const isConfigBased = /config\./i.test(line) ||
        /settings\./i.test(line) ||
        /rules\./i.test(line) ||
        /options\.\w+Pattern/i.test(line) ||
        /urlPattern/i.test(line) ||
        /routePattern/i.test(line);
    // Escaped input is safe - skip entirely
    if (hasEscaping) {
        return;
    }
    // Config-based patterns are trusted - skip
    if (isConfigBased) {
        return;
    }
    // Check if regex source is an object property (app-controlled data, not user input)
    // Patterns: obj.pattern, item.regex, l.urlRegExp, entry.matchPattern
    const objectPropertySource = /new\s+RegExp\s*\(\s*\w+\.\w*(regex|pattern|regexp|match|rule|expression|filter)\w*/i.test(line);
    if (objectPropertySource) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (app-controlled)',
            description: 'Dynamic regex from object property. If the regex source is app-defined (not user input), ReDoS risk is minimal.',
            suggestedFix: 'Ensure regex patterns come from trusted, validated sources.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Check if regex source is from array iteration over app data
    // Pattern: for (const item of items) { new RegExp(item.xxx) }
    const isArrayIterationContext = /for\s*\(\s*(const|let|var)\s+\w+\s+(of|in)\s+/.test(context) &&
        /new\s+RegExp\s*\(\s*\w+\./.test(line);
    if (isArrayIterationContext) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (iteration)',
            description: 'Dynamic regex in array iteration. If iterating over app-defined data, ReDoS risk is minimal.',
            suggestedFix: 'Ensure regex patterns come from trusted sources, not user input.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Try-catch wrapped - lower severity (ReDoS contained)
    if (hasTryCatch) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (try-catch wrapped)',
            description: 'Dynamic regex with try-catch error handling. ReDoS attacks are contained but may still cause performance issues.',
            suggestedFix: 'Consider using safe-regex library or adding timeout for regex operations.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // Standard handling for unprotected regex
    handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
}
/**
 * Handle spread operator with user input patterns
 * Checks for schema validation (Fastify, Zod, tRPC) that strips unknown properties
 */
function handleSpreadPattern(funcPattern, line, content, index, filePath, isTestFile, vulnerabilities, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, index - 30);
    const contextEnd = index;
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    // Fastify/Hapi schema validation on route - body is pre-validated
    // Pattern: schema: { body: someSchema } before handler
    const hasRouteSchemaValidation = /schema\s*:\s*\{[^}]*body\s*:\s*\w+/i.test(context) ||
        /body\s*:\s*\w+Schema/i.test(context);
    // Express + Zod/Joi/Yup middleware validation
    const hasMiddlewareValidation = /validate\s*\(\s*\w+Schema\s*\)/i.test(context) ||
        /\.parse\s*\(\s*req\.body\s*\)/i.test(context) ||
        /celebrate\s*\(/i.test(context);
    // tRPC input validation
    const hasTRPCValidation = /\.input\s*\(\s*z\./i.test(context) ||
        /\.input\s*\(\s*\w+Schema\s*\)/i.test(context);
    if (hasRouteSchemaValidation || hasMiddlewareValidation || hasTRPCValidation) {
        vulnerabilities.push({
            id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity: 'info',
            category: 'dangerous_function',
            title: funcPattern.name + ' (schema-validated)',
            description: 'Request body is spread but has schema validation. Schema validation strips unknown properties, reducing mass assignment risk.',
            suggestedFix: 'Ensure schema validation is strict (no .passthrough() in Zod, no additionalProperties in JSON Schema).',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
        return;
    }
    // No schema validation - standard handling
    handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities);
}
/**
 * Handle standard patterns without special logic
 */
function handleStandardPattern(funcPattern, line, index, filePath, isTestFile, vulnerabilities) {
    let severity = funcPattern.severity;
    let confidence = 'high';
    if (isTestFile) {
        if (severity === 'critical') {
            severity = 'medium';
        }
        else if (severity === 'high') {
            severity = 'low';
        }
        else {
            severity = 'info';
        }
        confidence = 'low';
    }
    vulnerabilities.push({
        id: `dangerous-func-${filePath}-${index + 1}-${funcPattern.name}`,
        filePath,
        lineNumber: index + 1,
        lineContent: line.trim(),
        severity,
        category: 'dangerous_function',
        title: funcPattern.name,
        description: funcPattern.description + (isTestFile ? ' (in test file)' : ''),
        suggestedFix: funcPattern.suggestedFix,
        confidence,
        baseConfidence: BASE_CONFIDENCE,
        layer: 2,
        source: 'structural',
    });
}
//# sourceMappingURL=index.js.map