"use strict";
/**
 * JSON.parse Detection
 *
 * Source-aware detection of JSON.parse usage with severity classification
 * based on the data source and error handling context.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.isLowRiskJSONParseFile = isLowRiskJSONParseFile;
exports.isTrustedSDKResponse = isTrustedSDKResponse;
exports.classifyJSONParseSource = classifyJSONParseSource;
exports.detectJSONParseSafe = detectJSONParseSafe;
const file_classifier_1 = require("../../../parse/file-classifier");
const control_flow_1 = require("./utils/control-flow");
const schema_validation_1 = require("./utils/schema-validation");
const BASE_CONFIDENCE = 0.35;
/**
 * Check if file path indicates a low-risk context for JSON.parse
 */
function isLowRiskJSONParseFile(filePath) {
    // Test/mock files - skip or info only
    if ((0, file_classifier_1.isTestOrMockFile)(filePath)) {
        return 'test_fixture';
    }
    // Settings/preferences components - internal UI state
    if (/\/(components|pages)\/(settings|preferences|config)/i.test(filePath)) {
        return 'ui_state';
    }
    // Provider/context files - typically storing state in localStorage
    if (/Provider\.(ts|tsx|js|jsx)$/i.test(filePath)) {
        return 'ui_state';
    }
    // Modal/Dialog components - typically internal state
    if (/(Modal|Dialog|Settings|Preferences)\.(ts|tsx|js|jsx)$/i.test(filePath)) {
        return 'ui_state';
    }
    // __mocks__ directory
    if (/__mocks__/i.test(filePath)) {
        return 'test_fixture';
    }
    // fixtures directory
    if (/\/(fixtures?|stubs?|mocks?)\//i.test(filePath)) {
        return 'test_fixture';
    }
    // scripts/tools directories (internal tooling)
    if (/\/(scripts?|tools?|cli)\//i.test(filePath)) {
        return 'internal';
    }
    // Migration files
    if (/migration/i.test(filePath)) {
        return 'migration';
    }
    // Config files
    if (/\/(config|settings|constants)\.(ts|js)/i.test(filePath)) {
        return 'config';
    }
    return null;
}
/**
 * Check if JSON.parse is parsing a trusted SDK response
 * These are well-defined responses from known APIs and are safe to parse
 */
function isTrustedSDKResponse(lineContent, content) {
    const trustedPatterns = [
        // OpenAI SDK responses
        /JSON\.parse\s*\(\s*(?:response|completion|result|message)\.(?:content|text|data)/i,
        /JSON\.parse\s*\(\s*(?:openai|anthropic|client)\./i,
        // Fetch response.json() result (already parsed by fetch)
        /JSON\.parse\s*\(\s*await\s+.*\.json\s*\(\s*\)\s*\)/i,
        // SDK method results
        /JSON\.parse\s*\(\s*(?:result|response)\.(?:choices|content|data|body)\[/i,
        // AI SDK streaming results
        /JSON\.parse\s*\(\s*(?:chunk|delta|part)\.(?:content|text)/i,
    ];
    if (trustedPatterns.some(p => p.test(lineContent))) {
        return true;
    }
    // Check surrounding context for SDK usage
    const sdkContextPatterns = [
        /openai\..*\.create/i,
        /anthropic\..*\.create/i,
        /\.chat\.completions/i,
        /\.messages\.create/i,
    ];
    return sdkContextPatterns.some(p => p.test(content));
}
/**
 * Classify the source of data being passed to JSON.parse
 */
function classifyJSONParseSource(lineContent, filePath) {
    // First check file path for low-risk contexts
    const fileBasedSource = isLowRiskJSONParseFile(filePath);
    if (fileBasedSource) {
        return fileBasedSource;
    }
    // User input - potentially dangerous
    const userInputPatterns = [
        /JSON\.parse\s*\(\s*(req|request)\.(body|query|params)/i,
        /JSON\.parse\s*\(\s*event\.(body|queryStringParameters)/i, // AWS Lambda
        /JSON\.parse\s*\(\s*ctx\.(request|body|query)/i, // Koa
        /JSON\.parse\s*\(\s*(input|userInput|rawInput|payload)/i,
        /JSON\.parse\s*\(\s*body\b/i, // Generic 'body' often means request body
    ];
    if (userInputPatterns.some(p => p.test(lineContent))) {
        return 'user_input';
    }
    // localStorage/sessionStorage - client-side storage
    const storagePatterns = [
        /JSON\.parse\s*\(\s*localStorage\.getItem/i,
        /JSON\.parse\s*\(\s*sessionStorage\.getItem/i,
        /JSON\.parse\s*\(\s*window\.localStorage/i,
        /JSON\.parse\s*\(\s*storage\.get/i,
        /JSON\.parse\s*\(\s*saved\b/i, // Common pattern: const saved = localStorage.getItem(...); JSON.parse(saved)
        /JSON\.parse\s*\(\s*stored\b/i,
    ];
    if (storagePatterns.some(p => p.test(lineContent))) {
        return 'local_storage';
    }
    // Database results - internal data
    const databasePatterns = [
        /JSON\.parse\s*\(\s*(row|result|record|doc|document)\./i,
        /JSON\.parse\s*\(\s*\w+\.(data|json|metadata|embedding)\)/i,
        /JSON\.parse\s*\(\s*\w+\[['"]?\w+['"]?\]\.(data|json|embedding)/i,
        /JSON\.parse\s*\(\s*item\.\w+\)/i, // ORM iteration: items.map(item => JSON.parse(item.field))
        /JSON\.parse\s*\(\s*\w+\.content\)/i, // Parsing content field from DB
    ];
    if (databasePatterns.some(p => p.test(lineContent))) {
        return 'database';
    }
    // Editor state, internal caches, UI state
    const internalPatterns = [
        /JSON\.parse\s*\(\s*(state|cache|stored|saved|cached)/i,
        /JSON\.parse\s*\(\s*this\.(state|cache|data)/i,
        /JSON\.parse\s*\(\s*\w+State\)/i,
        /JSON\.parse\s*\(\s*editorState/i,
        /JSON\.parse\s*\(\s*parsed\b/i, // JSON.parse(parsed) - likely already validated
        /JSON\.parse\s*\(\s*settings\b/i, // Settings data
        /JSON\.parse\s*\(\s*preferences\b/i,
    ];
    if (internalPatterns.some(p => p.test(lineContent))) {
        return 'internal';
    }
    // Node content in editor apps (e.g., noda-os nodes have JSON content)
    if (/JSON\.parse\s*\(\s*(node|note|document|entry)\.(content|body|data)\)/i.test(lineContent)) {
        return 'database';
    }
    return 'unknown';
}
/**
 * Detect JSON.parse usage with source-aware severity
 * Much smarter than simple pattern matching - considers try/catch and data source
 */
function detectJSONParseSafe(content, filePath, isTestFile, vulnerabilities) {
    const lines = content.split('\n');
    const jsonParsePattern = /JSON\.parse\s*\(/gi;
    // Track instances per file to aggregate noisy patterns
    const instances = [];
    lines.forEach((line, index) => {
        if ((0, file_classifier_1.isComment)(line))
            return;
        jsonParsePattern.lastIndex = 0;
        if (!jsonParsePattern.test(line))
            return;
        const jsonSource = classifyJSONParseSource(line, filePath);
        // Skip migration files entirely - they're internal tooling
        if (jsonSource === 'migration')
            return;
        // Skip test fixtures entirely - they're intentionally parsing test data
        if (jsonSource === 'test_fixture')
            return;
        // Skip trusted SDK responses - these are well-defined and safe to parse
        if (isTrustedSDKResponse(line, content))
            return;
        // Check if JSON.parse is inside a try-catch block
        const insideTryCatch = (0, control_flow_1.isInsideTryCatch)(content, index) || (0, control_flow_1.hasTryCatchNearby)(content, index);
        // Check if schema validation is applied after JSON.parse
        const hasSchemaValidation = (0, schema_validation_1.hasSchemaValidationNearby)(content, index);
        // If inside try-catch with safe source, suppress entirely - this is perfectly fine
        if (insideTryCatch &&
            ['local_storage', 'database', 'config', 'internal', 'ui_state'].includes(jsonSource)) {
            return;
        }
        // If schema validation is present, this is properly handled
        if (hasSchemaValidation) {
            return;
        }
        // UI state (settings, providers, modals) - very low risk, aggregate or skip
        if (jsonSource === 'ui_state') {
            // Only track for aggregation, don't report individually
            instances.push({
                lineNumber: index + 1,
                lineContent: line.trim(),
                source: jsonSource,
            });
            return;
        }
        // Determine severity based on source and error handling
        let severity;
        let description;
        let suggestedFix;
        let confidence = 'medium';
        if (insideTryCatch) {
            // Already has error handling
            switch (jsonSource) {
                case 'user_input':
                    severity = 'low';
                    description =
                        'JSON.parse on user input is wrapped in try-catch. Consider adding schema validation (zod/yup) to validate the parsed structure.';
                    suggestedFix =
                        'Add schema validation after parsing: const validated = schema.parse(JSON.parse(input))';
                    confidence = 'low';
                    break;
                default:
                    // With try-catch and non-user source, this is fine - don't report
                    return;
            }
        }
        else {
            // No try-catch
            switch (jsonSource) {
                case 'user_input':
                    severity = 'medium';
                    description =
                        'JSON.parse on user input without schema validation. Malformed input will crash; malicious input may have unexpected shape.';
                    suggestedFix =
                        'Use a schema validation library (zod, yup, joi): try { const data = schema.parse(JSON.parse(body)) } catch (e) { return 400 }';
                    confidence = 'high';
                    break;
                case 'local_storage':
                    severity = 'info';
                    description =
                        'JSON.parse on localStorage data. Consider adding try-catch for robustness against corrupted data.';
                    suggestedFix =
                        'Wrap in try-catch to handle corrupted localStorage gracefully.';
                    confidence = 'low';
                    break;
                case 'database':
                    // Database content parsing is very common and low-risk
                    instances.push({
                        lineNumber: index + 1,
                        lineContent: line.trim(),
                        source: jsonSource,
                    });
                    return; // Will be aggregated below
                case 'config':
                case 'internal':
                    severity = 'info';
                    description = `JSON.parse on ${jsonSource.replace('_', ' ')} data without error handling. Low risk but consider defensive coding.`;
                    suggestedFix = 'Consider adding try-catch for robustness.';
                    confidence = 'low';
                    break;
                default:
                    // Unknown source - track for potential aggregation
                    instances.push({
                        lineNumber: index + 1,
                        lineContent: line.trim(),
                        source: jsonSource,
                    });
                    return; // Will be evaluated below based on aggregation
            }
        }
        // Downgrade test files
        if (isTestFile) {
            severity = 'info';
            confidence = 'low';
            description += ' (in test file)';
        }
        vulnerabilities.push({
            id: `json-parse-${filePath}-${index + 1}`,
            filePath,
            lineNumber: index + 1,
            lineContent: line.trim(),
            severity,
            category: 'dangerous_function',
            title: 'JSON.parse usage',
            description,
            suggestedFix,
            confidence,
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
    });
    // Aggregate low-risk JSON.parse instances if there are many
    if (instances.length >= 3) {
        // Create single aggregated finding instead of N individual findings
        const lineNumbers = instances.map(i => i.lineNumber).slice(0, 5);
        const moreText = instances.length > 5 ? `... (${instances.length} total)` : '';
        vulnerabilities.push({
            id: `json-parse-aggregated-${filePath}`,
            filePath,
            lineNumber: instances[0].lineNumber,
            lineContent: `${instances.length} instances across this file`,
            severity: 'info',
            category: 'dangerous_function',
            title: `JSON.parse usage (${instances.length} instances)`,
            description: `JSON.parse detected. Consider adding error handling and schema validation if parsing user input.${isTestFile ? ' (in test file)' : ''}\n\nFound ${instances.length} occurrences at lines: ${lineNumbers.join(', ')}${moreText}`,
            suggestedFix: 'Add try-catch for error handling. If parsing user input, add schema validation.',
            confidence: 'low',
            baseConfidence: BASE_CONFIDENCE,
            layer: 2,
            source: 'structural',
        });
    }
    else if (instances.length > 0 && instances.length < 3) {
        // Report individually for small counts
        for (const instance of instances) {
            vulnerabilities.push({
                id: `json-parse-${filePath}-${instance.lineNumber}`,
                filePath,
                lineNumber: instance.lineNumber,
                lineContent: instance.lineContent,
                severity: 'info',
                category: 'dangerous_function',
                title: 'JSON.parse usage',
                description: `JSON.parse on ${instance.source.replace('_', ' ')} data without error handling. Low risk but consider defensive coding.${isTestFile ? ' (in test file)' : ''}`,
                suggestedFix: 'Consider adding try-catch for robustness.',
                confidence: 'low',
                baseConfidence: BASE_CONFIDENCE,
                layer: 2,
                source: 'structural',
            });
        }
    }
}
//# sourceMappingURL=json-parse.js.map