"use strict";
/**
 * Source Discovery — Identifies user-input sources in code files.
 *
 * Scans for framework-specific patterns (Express, Next.js, Flask, Django, etc.)
 * that represent user-controllable input entering the application.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.discoverSources = discoverSources;
const SOURCE_PATTERNS = [
    // --- Express/Node.js ---
    { pattern: /\breq\.body\b/, sourceType: 'http_body', framework: 'express', confidence: 'high' },
    { pattern: /\breq\.query\b/, sourceType: 'http_query', framework: 'express', confidence: 'high' },
    { pattern: /\breq\.params\b/, sourceType: 'http_params', framework: 'express', confidence: 'high' },
    { pattern: /\breq\.headers\b/, sourceType: 'http_headers', framework: 'express', confidence: 'high' },
    { pattern: /\breq\.cookies\b/, sourceType: 'http_headers', framework: 'express', confidence: 'high' },
    // --- Koa ---
    { pattern: /\bctx\.request\.body\b/, sourceType: 'http_body', framework: 'koa', confidence: 'high' },
    { pattern: /\bctx\.query\b/, sourceType: 'http_query', framework: 'koa', confidence: 'high' },
    // --- Next.js App Router ---
    { pattern: /\bsearchParams\b/, sourceType: 'http_query', framework: 'nextjs', confidence: 'medium' },
    { pattern: /\bformData\b/, sourceType: 'form_data', framework: 'nextjs', confidence: 'medium' },
    // --- Browser APIs ---
    { pattern: /\bdocument\.location\b/, sourceType: 'url_search_params', framework: 'browser', confidence: 'medium' },
    { pattern: /\bwindow\.location\b/, sourceType: 'url_search_params', framework: 'browser', confidence: 'medium' },
    { pattern: /\blocation\.search\b/, sourceType: 'url_search_params', framework: 'browser', confidence: 'medium' },
    { pattern: /\blocation\.hash\b/, sourceType: 'url_search_params', framework: 'browser', confidence: 'medium' },
    { pattern: /new\s+URLSearchParams\b/, sourceType: 'url_search_params', framework: 'browser', confidence: 'medium' },
    { pattern: /\bdocument\.cookie\b/, sourceType: 'http_headers', framework: 'browser', confidence: 'medium' },
    { pattern: /\bevent\.target\.value\b/, sourceType: 'form_data', framework: 'browser', confidence: 'medium' },
    // --- WebSocket ---
    { pattern: /\bws\.on\s*\(\s*['"]message['"]/, sourceType: 'websocket_message', framework: 'websocket', confidence: 'medium' },
    { pattern: /\bsocket\.on\s*\(\s*['"]message['"]/, sourceType: 'websocket_message', framework: 'websocket', confidence: 'medium' },
    // --- AWS Lambda ---
    { pattern: /\bevent\.body\b/, sourceType: 'http_body', framework: 'aws-lambda', confidence: 'high' },
    { pattern: /\bevent\.queryStringParameters\b/, sourceType: 'http_query', framework: 'aws-lambda', confidence: 'high' },
    // --- Python Flask ---
    { pattern: /\brequest\.args\b/, sourceType: 'http_query', framework: 'flask', confidence: 'high' },
    { pattern: /\brequest\.form\b/, sourceType: 'form_data', framework: 'flask', confidence: 'high' },
    { pattern: /\brequest\.json\b/, sourceType: 'http_body', framework: 'flask', confidence: 'high' },
    { pattern: /\brequest\.data\b/, sourceType: 'http_body', framework: 'flask', confidence: 'high' },
    // --- Python Django ---
    { pattern: /\brequest\.GET\b/, sourceType: 'http_query', framework: 'django', confidence: 'high' },
    { pattern: /\brequest\.POST\b/, sourceType: 'form_data', framework: 'django', confidence: 'high' },
    { pattern: /\brequest\.body\b/, sourceType: 'http_body', framework: 'django', confidence: 'high' },
    // --- CLI ---
    { pattern: /\bprocess\.argv\b/, sourceType: 'cli_args', framework: 'node', confidence: 'low' },
    { pattern: /\bsys\.argv\b/, sourceType: 'cli_args', framework: 'python', confidence: 'low' },
    { pattern: /\binput\s*\(/, sourceType: 'cli_args', framework: 'python', confidence: 'low' },
];
// ============================================================================
// Variable Extraction
// ============================================================================
/** Pattern for direct assignment: const/let/var NAME = ... */
const DIRECT_ASSIGNMENT = /\b(?:const|let|var)\s+(\w+)\s*=\s*(.+)/;
/** Pattern for destructuring: const/let/var { A, B } = ... */
const DESTRUCTURE_ASSIGNMENT = /\b(?:const|let|var)\s+\{\s*([^}]+)\}\s*=\s*(.+)/;
/** Pattern for array destructuring: const/let/var [ A, B ] = ... */
const ARRAY_DESTRUCTURE = /\b(?:const|let|var)\s+\[\s*([^\]]+)\]\s*=\s*(.+)/;
/**
 * Extract variable name(s) from an assignment line that contains a source expression.
 */
function extractVariables(line, sourceExpression) {
    const variables = [];
    // Try destructuring first: const { a, b } = req.body
    const destructureMatch = line.match(DESTRUCTURE_ASSIGNMENT);
    if (destructureMatch) {
        const rhs = destructureMatch[2].trim();
        if (rhs.includes(sourceExpression) || sourceMatchesRhs(rhs, sourceExpression)) {
            const names = destructureMatch[1].split(',').map(n => {
                // Handle renaming: `originalName: newName`
                const parts = n.trim().split(':');
                return (parts.length > 1 ? parts[1] : parts[0]).trim();
            }).filter(n => n.length > 0 && n !== '...');
            variables.push(...names);
        }
        return variables;
    }
    // Try array destructuring: const [ a, b ] = ...
    const arrayMatch = line.match(ARRAY_DESTRUCTURE);
    if (arrayMatch) {
        const rhs = arrayMatch[2].trim();
        if (rhs.includes(sourceExpression) || sourceMatchesRhs(rhs, sourceExpression)) {
            const names = arrayMatch[1].split(',').map(n => n.trim()).filter(n => n.length > 0 && n !== '...');
            variables.push(...names);
        }
        return variables;
    }
    // Try direct assignment: const name = req.body
    const directMatch = line.match(DIRECT_ASSIGNMENT);
    if (directMatch) {
        const rhs = directMatch[2].trim();
        if (rhs.includes(sourceExpression) || sourceMatchesRhs(rhs, sourceExpression)) {
            variables.push(directMatch[1]);
        }
    }
    return variables;
}
/**
 * Check if a source pattern's expression is part of the RHS of an assignment.
 */
function sourceMatchesRhs(rhs, sourceExpression) {
    // Handle cases like `req.body.email` matching the `req.body` source
    return rhs.includes(sourceExpression);
}
// ============================================================================
// Main Discovery Function
// ============================================================================
/**
 * Discover user-input sources in a file.
 *
 * Scans each line for framework-specific input patterns and extracts
 * the variable names that receive user-controlled data.
 */
function discoverSources(content, filePath) {
    const lines = content.split('\n');
    const sources = [];
    const seen = new Set();
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        const lineNumber = i + 1;
        // Skip comments
        const trimmed = line.trim();
        if (trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('/*')) {
            continue;
        }
        for (const sp of SOURCE_PATTERNS) {
            const match = sp.pattern.exec(line);
            if (!match)
                continue;
            const expression = match[0];
            // Extract variables assigned from this source
            const variables = extractVariables(line, expression);
            if (variables.length > 0) {
                for (const variable of variables) {
                    const key = `${lineNumber}:${variable}:${sp.sourceType}`;
                    if (seen.has(key))
                        continue;
                    seen.add(key);
                    sources.push({
                        line: lineNumber,
                        expression,
                        variable,
                        sourceType: sp.sourceType,
                        frameworkSource: sp.framework,
                        confidence: sp.confidence,
                    });
                }
            }
            else {
                // Source found but no variable assignment (e.g., inline usage like `db.query(req.query.id)`)
                // Still record it — the taint tracker and sink matcher can use the expression directly
                const key = `${lineNumber}:${expression}:${sp.sourceType}`;
                if (seen.has(key))
                    continue;
                seen.add(key);
                sources.push({
                    line: lineNumber,
                    expression,
                    variable: expression,
                    sourceType: sp.sourceType,
                    frameworkSource: sp.framework,
                    confidence: sp.confidence,
                });
            }
        }
    }
    return sources;
}
//# sourceMappingURL=source-discovery.js.map