"use strict";
/**
 * Function Classifier — Analyze exported functions to determine dangerous operations.
 *
 * For each exported function, extracts its body and scans for sink patterns
 * and source patterns to build a security profile. Used by cross-file taint
 * analysis to determine if calling an imported function can reach a sink.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.classifyExportedFunctions = classifyExportedFunctions;
exports.buildFunctionProfiles = buildFunctionProfiles;
const utils_1 = require("./route-discovery/utils");
const sink_patterns_1 = require("./sink-patterns");
// Source patterns indicating the function returns user data, with associated source types
const RETURN_SOURCE_PATTERNS = [
    { pattern: /return\s+.*\breq\.body\b/, sourceType: 'http_body' },
    { pattern: /return\s+.*\breq\.query\b/, sourceType: 'http_query' },
    { pattern: /return\s+.*\breq\.params\b/, sourceType: 'http_params' },
    { pattern: /return\s+.*\brequest\.args\b/, sourceType: 'http_query' },
    { pattern: /return\s+.*\brequest\.form\b/, sourceType: 'form_data' },
    { pattern: /return\s+.*\brequest\.json\b/, sourceType: 'http_body' },
    { pattern: /return\s+.*\brequest\.GET\b/, sourceType: 'http_query' },
    { pattern: /return\s+.*\brequest\.POST\b/, sourceType: 'http_body' },
    { pattern: /return\s+.*\bsearchParams\b/, sourceType: 'url_search_params' },
    { pattern: /return\s+.*\bformData\b/, sourceType: 'form_data' },
];
// Sanitiser patterns
const SANITISER_PATTERNS = [
    /\bsanitize\w*\s*\(/i,
    /\bescape\w*\s*\(/i,
    /\bDOMPurify\.sanitize\s*\(/,
    /\.parse\s*\(/, // zod, joi
    /\.validate\s*\(/,
    /\bNumber\s*\(/,
    /\bparseInt\s*\(/,
    /\bparseFloat\s*\(/,
    /\bpath\.normalize\s*\(/,
];
// ============================================================================
// Function Classification
// ============================================================================
/**
 * Classify exported functions in a file by their security profile.
 */
function classifyExportedFunctions(content, filePath, fileExports) {
    const profiles = [];
    const lines = content.split('\n');
    for (const exp of fileExports) {
        if (exp.kind !== 'function' && exp.kind !== 'variable')
            continue;
        // Extract function body
        const startLine = Math.max(0, exp.line - 1);
        const body = (0, utils_1.extractBlock)(lines, startLine);
        if (!body || body.length < 5)
            continue;
        // Extract parameter names from the function signature
        const paramNames = extractParamNames(lines[startLine] || '');
        // Scan body for sinks
        const bodyLines = body.split('\n');
        const sinkTypes = new Set();
        const dangerousParams = new Set();
        for (const bodyLine of bodyLines) {
            const trimmed = bodyLine.trim();
            if (trimmed.startsWith('//') || trimmed.startsWith('*'))
                continue;
            for (const sp of sink_patterns_1.SINK_PATTERNS) {
                if (sp.pattern.test(bodyLine)) {
                    sinkTypes.add(sp.sinkType);
                    // Check which params appear on this sink line
                    for (const param of paramNames) {
                        const paramRegex = new RegExp(`\\b${(0, sink_patterns_1.escapeRegex)(param)}\\b`);
                        if (paramRegex.test(bodyLine)) {
                            dangerousParams.add(param);
                        }
                    }
                }
            }
        }
        // Check if function returns user data and extract source type
        const matchedReturnSource = RETURN_SOURCE_PATTERNS.find(p => p.pattern.test(body));
        const returnsUserData = !!matchedReturnSource;
        const returnSourceType = matchedReturnSource?.sourceType;
        // Check for sanitisation
        const hasSanitisation = SANITISER_PATTERNS.some(p => p.test(body));
        if (sinkTypes.size > 0 || returnsUserData) {
            profiles.push({
                name: exp.name,
                filePath,
                reachesSinks: [...sinkTypes],
                returnsUserData,
                returnSourceType,
                hasSanitisation,
                paramNames,
                dangerousParams: [...dangerousParams],
                line: exp.line,
            });
        }
    }
    return profiles;
}
/**
 * Build function profiles for all files that are imported by at least one other file.
 */
function buildFunctionProfiles(files, graph) {
    const profileMap = new Map();
    for (const file of files) {
        const node = graph.nodes.get(file.path);
        if (!node)
            continue;
        // Only process files that are imported by at least one other file
        if (node.importedBy.size === 0)
            continue;
        if (node.exports.length === 0)
            continue;
        const profiles = classifyExportedFunctions(file.content, file.path, node.exports);
        if (profiles.length > 0) {
            profileMap.set(file.path, profiles);
        }
    }
    return profileMap;
}
// ============================================================================
// Helpers
// ============================================================================
function extractParamNames(signatureLine) {
    // Match function params: function foo(a, b, c) or (a, b) => or async (a: Type, b) =>
    const paramMatch = signatureLine.match(/\(([^)]*)\)/);
    if (!paramMatch)
        return [];
    return paramMatch[1]
        .split(',')
        .map(p => {
        // Strip type annotations, defaults, destructuring braces
        const cleaned = p.trim()
            .replace(/\s*[:=].*/g, '') // remove type annotations and defaults
            .replace(/[{}[\]]/g, '') // remove destructuring
            .replace(/\.\.\./g, '') // remove spread
            .trim();
        return cleaned;
    })
        .filter(p => p.length > 0 && /^\w+$/.test(p));
}
//# sourceMappingURL=function-classifier.js.map