"use strict";
/**
 * Layer 2: AI Prompt Hygiene Detection
 * Detects prompt injection vulnerabilities and secrets in LLM prompts
 *
 * Covers:
 * - B1: Prompt & template hygiene (LLM01)
 * - B3: Secrets & sensitive data in prompts (LLM06)
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectAIPromptHygiene = detectAIPromptHygiene;
exports.isLLMContextFile = isLLMContextFile;
const file_classifier_1 = require("../../parse/file-classifier");
const BASE_CONFIDENCE = 0.40;
/**
 * Check if a file is in an LLM/AI context based on path and content
 */
function isLLMContextFile(filePath, content) {
    // File path indicators of AI/LLM code
    const llmPathPatterns = [
        /\/(ai|llm|chat|openai|anthropic|gpt|claude)\//i,
        /\/(assistants?|agents?|prompts?)\//i,
        /(chat|ai|llm|prompt|assistant|agent).*\.(ts|js|tsx|jsx|py)$/i,
    ];
    if (llmPathPatterns.some(p => p.test(filePath))) {
        return true;
    }
    // Content patterns suggesting LLM API usage
    const llmContentPatterns = [
        /\.create\s*\(\s*\{[^}]*messages\s*:/i, // OpenAI/Anthropic SDK
        /from\s+['"](@anthropic-ai|openai|langchain|llama[-_]?index)/i, // Imports
        /\bsystem\s*:\s*['"`]/i, // System message definition
        /role:\s*['"`](user|assistant|system)['"`]/i, // Message roles
        /\b(systemPrompt|userPrompt|assistantPrompt)\b/i, // Prompt variables
        /messages\s*:\s*\[/i, // Messages array
        /\.chat\.completions?\.create/i, // OpenAI chat completion
        /\.messages\.create/i, // Anthropic messages
        /ChatCompletion|MessageCreate/i, // SDK types
    ];
    return llmContentPatterns.some(p => p.test(content));
}
/**
 * Check if user input delimiter/fence patterns are present
 */
function hasPromptDelimiters(lineContent, contextLines) {
    const context = [lineContent, ...contextLines].join('\n');
    const delimiterPatterns = [
        /```/, // Triple backticks
        /<user>|<\/user>/i, // XML-style user tags
        /<human>|<\/human>/i, // Human tags
        /---+/, // Horizontal rules
        /\[USER\]|\[\/USER\]/i, // Bracket tags
        /\{\{user\}\}/i, // Template variable
        /###\s*User|###\s*Input/i, // Markdown headers
        /INPUT:|OUTPUT:/i, // Section markers
    ];
    return delimiterPatterns.some(p => p.test(context));
}
/**
 * Check if content looks like proper parameterization rather than concatenation
 */
function isProperlyParameterized(lineContent) {
    const safePatterns = [
        /\{\{.*\}\}/, // Handlebars/mustache templates
        /\{[a-zA-Z_]+\}/, // Python format strings (positional)
        /\$\{.*\}.*sanitize|escape/i, // Template with sanitization
        /placeholder|PLACEHOLDER/, // Explicit placeholders
    ];
    return safePatterns.some(p => p.test(lineContent));
}
/**
 * B1: Unsafe prompt interpolation patterns
 */
const UNSAFE_INTERPOLATION_PATTERNS = [
    // Template literals with user input in system prompts
    {
        name: 'User input in system prompt',
        pattern: /system\s*[=:]\s*`[^`]*\$\{.*(?:user|input|req|request|body|query|params|data).*\}[^`]*`/gi,
        severity: 'high',
        description: 'User input is directly interpolated into a system prompt. This creates a prompt injection vulnerability where attackers can manipulate the AI\'s behavior.',
        suggestedFix: 'Use clear delimiters (```, <user>, ---) between system instructions and user content. Consider using structured input rather than string interpolation.',
        checkDelimiters: true,
    },
    // String concatenation in prompt building
    {
        name: 'Prompt string concatenation with user input',
        pattern: /(?:system|prompt|instruction)\s*[=+]\s*.*\+\s*(?:user|input|req|request|body|query|params)(?:\.|Input|\[)/gi,
        severity: 'high',
        description: 'User input is concatenated into prompt strings. Attackers can inject malicious instructions.',
        suggestedFix: 'Use delimiters to clearly separate system instructions from user content. Example: ```user input here```',
        checkDelimiters: true,
    },
    // Messages array with dynamic user content in system role
    {
        name: 'Dynamic content in system message',
        pattern: /role:\s*['"`]system['"`]\s*,\s*content:\s*`[^`]*\$\{/gi,
        severity: 'medium',
        description: 'System message content includes dynamic values. If user-controlled, this enables prompt injection.',
        suggestedFix: 'Keep system messages static. Place user input in messages with role: "user" instead.',
        checkDelimiters: true,
    },
    // f-strings in Python with user input
    {
        name: 'Python f-string prompt with user input',
        pattern: /f['"][^'"]*\{.*(?:user|input|request|body).*\}[^'"]*['"]/gi,
        severity: 'high',
        description: 'User input in Python f-string prompt creates prompt injection risk.',
        suggestedFix: 'Use explicit delimiters: f"System instructions...\n---\n{user_input}\n---"',
        checkDelimiters: true,
    },
];
// ============================================================================
// Secret Patterns - Comprehensive provider-specific detection
// ============================================================================
/**
 * Provider-specific secret patterns with known prefixes
 * These are high-confidence patterns that don't need context matching
 */
const KNOWN_SECRET_PREFIXES = [
    // OpenAI
    { name: 'OpenAI API Key', pattern: /sk-[a-zA-Z0-9]{20,}/g, severity: 'critical' },
    { name: 'OpenAI Project Key', pattern: /sk-proj-[a-zA-Z0-9]{48,}/g, severity: 'critical' },
    // Anthropic
    { name: 'Anthropic API Key', pattern: /sk-ant-[a-zA-Z0-9-]{20,}/g, severity: 'critical' },
    { name: 'Anthropic Full Key', pattern: /sk-ant-api03-[a-zA-Z0-9_-]{90,}/g, severity: 'critical' },
    // GitHub
    { name: 'GitHub PAT', pattern: /ghp_[a-zA-Z0-9]{36,}/g, severity: 'critical' },
    { name: 'GitHub OAuth', pattern: /gho_[a-zA-Z0-9]{36,}/g, severity: 'critical' },
    { name: 'GitHub App Token', pattern: /ghu_[a-zA-Z0-9]{36,}/g, severity: 'critical' },
    { name: 'GitHub Refresh Token', pattern: /ghr_[a-zA-Z0-9]{36,}/g, severity: 'critical' },
    { name: 'GitHub Fine-grained PAT', pattern: /github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}/g, severity: 'critical' },
    // Stripe
    { name: 'Stripe Live Secret', pattern: /sk_live_[a-zA-Z0-9]{24,}/g, severity: 'critical' },
    { name: 'Stripe Test Secret', pattern: /sk_test_[a-zA-Z0-9]{24,}/g, severity: 'medium' },
    { name: 'Stripe Restricted Key', pattern: /rk_live_[a-zA-Z0-9]{24,}/g, severity: 'critical' },
    // AWS
    { name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/g, severity: 'critical' },
    { name: 'AWS Session Token', pattern: /ASIA[0-9A-Z]{16}/g, severity: 'critical' },
    // Google
    { name: 'Google API Key', pattern: /AIza[0-9A-Za-z-_]{35}/g, severity: 'high' },
    // Slack
    { name: 'Slack Bot Token', pattern: /xoxb-[0-9a-zA-Z-]{50,}/g, severity: 'critical' },
    { name: 'Slack User Token', pattern: /xoxp-[0-9a-zA-Z-]{50,}/g, severity: 'critical' },
    { name: 'Slack App Token', pattern: /xoxa-[0-9a-zA-Z-]{50,}/g, severity: 'critical' },
    { name: 'Slack Legacy Token', pattern: /xox[baprs]-[0-9a-zA-Z]{10,}/g, severity: 'critical' },
    // Twilio
    { name: 'Twilio API Key', pattern: /SK[a-f0-9]{32}/g, severity: 'critical' },
    { name: 'Twilio Account SID', pattern: /AC[a-f0-9]{32}/g, severity: 'high' },
    // SendGrid
    { name: 'SendGrid API Key', pattern: /SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}/g, severity: 'critical' },
    // Mailgun
    { name: 'Mailgun API Key', pattern: /key-[a-zA-Z0-9]{32}/g, severity: 'critical' },
    // NPM/PyPI
    { name: 'NPM Token', pattern: /npm_[a-zA-Z0-9]{36}/g, severity: 'critical' },
    { name: 'PyPI Token', pattern: /pypi-[a-zA-Z0-9]{32,}/g, severity: 'critical' },
    // Vercel/Netlify
    { name: 'Vercel Token', pattern: /vercel_[a-zA-Z0-9]{24,}/g, severity: 'critical' },
    { name: 'Netlify Token', pattern: /nfp_[a-zA-Z0-9]{40,}/g, severity: 'critical' },
    // Square
    { name: 'Square Access Token', pattern: /sq0csp-[a-zA-Z0-9-_]{43}/g, severity: 'critical' },
    { name: 'Square OAuth Secret', pattern: /sq0csp-[a-zA-Z0-9-_]{40,}/g, severity: 'critical' },
    // Shopify
    { name: 'Shopify Access Token', pattern: /shpat_[a-fA-F0-9]{32}/g, severity: 'critical' },
    { name: 'Shopify Private App', pattern: /shppa_[a-fA-F0-9]{32}/g, severity: 'critical' },
    // Datadog
    { name: 'Datadog API Key', pattern: /dd[a-z]{1}[a-f0-9]{39}/g, severity: 'critical' },
    // HuggingFace
    { name: 'HuggingFace Token', pattern: /hf_[a-zA-Z0-9]{34,}/g, severity: 'critical' },
    // Replicate
    { name: 'Replicate API Token', pattern: /r8_[a-zA-Z0-9]{37}/g, severity: 'critical' },
    // OpenRouter
    { name: 'OpenRouter Key', pattern: /sk-or-v1-[a-zA-Z0-9]{64}/g, severity: 'critical' },
    // Cohere
    { name: 'Cohere API Key', pattern: /[a-zA-Z0-9]{40}(?=.*cohere)/gi, severity: 'high' },
    // Private Keys
    { name: 'Private Key', pattern: /-----BEGIN\s+(?:RSA\s+|EC\s+|DSA\s+|OPENSSH\s+)?PRIVATE\s+KEY-----/g, severity: 'critical' },
    // JWT Tokens (full format)
    { name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/g, severity: 'high' },
    // Database URLs with credentials
    { name: 'Database URL', pattern: /(mongodb|postgres|mysql|redis|amqp)(\+srv)?:\/\/[^:]+:[^@\s]+@[^\s"']+/gi, severity: 'critical' },
    // Webhook URLs (often contain secrets)
    { name: 'Slack Webhook', pattern: /https:\/\/hooks\.slack\.com\/services\/T[a-zA-Z0-9_]+\/B[a-zA-Z0-9_]+\/[a-zA-Z0-9_]+/g, severity: 'high' },
    { name: 'Discord Webhook', pattern: /https:\/\/discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[a-zA-Z0-9_-]+/g, severity: 'high' },
];
/**
 * B3: Secrets in prompt context patterns (original context-aware patterns)
 * Note: Using [^\n;]* instead of [^;]* to prevent matching across lines
 */
const SECRETS_IN_PROMPTS_PATTERNS = [
    // API keys in message content (same line only)
    {
        name: 'API key in prompt content',
        pattern: /(?:messages|prompt|system|content)\s*[=:][^\n;]*(?:sk-[a-zA-Z0-9]{20,}|api[_-]?key\s*[:=]\s*['"][^'"]{16,}['"])/gi,
        severity: 'critical',
        description: 'API key appears to be hardcoded in prompt content. Keys in prompts may be logged, cached, or sent to model providers.',
        suggestedFix: 'Never include API keys in prompts. Use environment variables and keep them server-side only.',
    },
    // AWS keys in prompts
    {
        name: 'AWS credentials in prompt',
        pattern: /(?:messages|prompt|system|content)\s*[=:][^\n;]*(?:AKIA[A-Z0-9]{16}|aws[_-]?(?:secret|access)[_-]?key)/gi,
        severity: 'critical',
        description: 'AWS credentials detected in prompt content.',
        suggestedFix: 'Remove credentials from prompts. Use IAM roles or environment variables instead.',
    },
    // Database URLs with credentials
    {
        name: 'Database credentials in prompt',
        pattern: /(?:messages|prompt|system|content)[^\n]*(?:mongodb|postgres|mysql|redis):\/\/[^:]+:[^@]+@/gi,
        severity: 'critical',
        description: 'Database connection string with credentials in prompt. This exposes database access.',
        suggestedFix: 'Never include connection strings in prompts. Reference data by ID instead.',
    },
    // Passwords in prompt context
    {
        name: 'Password in prompt content',
        pattern: /(?:messages|prompt|content)\s*[=:][^\n]*(?:password|passwd|pwd)\s*[:=]\s*['"`][^'"`]{8,}/gi,
        severity: 'high',
        description: 'Password appears in prompt content. This may be logged or exposed to model providers.',
        suggestedFix: 'Remove passwords from prompts. Use authentication tokens or session references instead.',
    },
    // Private keys
    {
        name: 'Private key in prompt',
        pattern: /(?:messages|prompt|content)[^\n]*(?:-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----)/gi,
        severity: 'critical',
        description: 'Private key material detected in prompt context.',
        suggestedFix: 'Never include private keys in prompts. Sign data server-side instead.',
    },
    // Generic token patterns
    {
        name: 'Access token in prompt',
        pattern: /(?:messages|prompt|content)\s*[=:][^\n]*(?:access[_-]?token|auth[_-]?token|bearer)\s*[:=]\s*['"`][a-zA-Z0-9_.-]{20,}/gi,
        severity: 'high',
        description: 'Access token detected in prompt content. Tokens in prompts risk exposure.',
        suggestedFix: 'Do not include tokens in prompts. Pass token context through secure server-side channels.',
    },
];
// ============================================================================
// Variable Flow Detection - Secrets flowing into prompts
// ============================================================================
/**
 * Patterns for detecting secret variable declarations
 */
const SECRET_VARIABLE_PATTERNS = [
    // Direct assignment patterns
    /(?:const|let|var)\s+(\w*(?:key|token|secret|password|credential|apiKey|authToken|accessToken)\w*)\s*=\s*['"`]([^'"`]{16,})['"`]/gi,
    // Object property patterns
    /(\w*(?:key|token|secret|password|credential|apiKey|authToken|accessToken)\w*)\s*:\s*['"`]([^'"`]{16,})['"`]/gi,
];
/**
 * Patterns for detecting prompt variable usage
 */
const PROMPT_USAGE_PATTERNS = [
    // Template literal interpolation
    /`[^`]*\$\{(\w+)\}[^`]*`/g,
    // String concatenation
    /\+\s*(\w+)\s*(?:\+|$)/g,
    // f-string interpolation (Python)
    /f['"][^'"]*\{(\w+)\}[^'"]*['"]/g,
    // Format string
    /\.format\s*\([^)]*(\w+)[^)]*\)/g,
];
/**
 * Check if a variable name suggests it contains a secret
 */
function isSecretVariableName(varName) {
    const secretIndicators = [
        /api[_-]?key/i,
        /secret[_-]?key/i,
        /access[_-]?token/i,
        /auth[_-]?token/i,
        /password/i,
        /credential/i,
        /private[_-]?key/i,
        /bearer/i,
        /jwt/i,
        /oauth/i,
        /^sk_/i,
        /^pk_/i,
        /token$/i,
        /key$/i,
        /secret$/i,
    ];
    return secretIndicators.some(p => p.test(varName));
}
/**
 * Detect secrets flowing from variables into prompts (variable indirection)
 */
function detectSecretVariableFlow(content, filePath, isTestFile, lines) {
    const vulnerabilities = [];
    const _lines = lines ?? content.split('\n');
    // First pass: collect all secret variable declarations
    const secretVariables = new Map();
    for (let i = 0; i < _lines.length; i++) {
        const line = _lines[i];
        if ((0, file_classifier_1.isComment)(line))
            continue;
        for (const pattern of SECRET_VARIABLE_PATTERNS) {
            const regex = new RegExp(pattern.source, pattern.flags);
            let match;
            while ((match = regex.exec(line)) !== null) {
                const varName = match[1];
                const value = match[2];
                // Check if variable name suggests it's a secret
                if (isSecretVariableName(varName)) {
                    secretVariables.set(varName, { line: i + 1, value });
                }
            }
        }
    }
    // Second pass: find where these variables flow into prompts
    const promptContextPatterns = [
        /(?:system|prompt|message|content)\s*[:=]/i,
        /role:\s*['"`](?:system|user|assistant)['"`]/i,
        /\.chat\.completions?\.create/i,
        /\.messages\.create/i,
        /messages\s*:\s*\[/i,
    ];
    for (let i = 0; i < _lines.length; i++) {
        const line = _lines[i];
        if ((0, file_classifier_1.isComment)(line))
            continue;
        // Check if this line or nearby lines are in prompt context
        const contextWindow = _lines.slice(Math.max(0, i - 5), Math.min(_lines.length, i + 5)).join('\n');
        const isPromptContext = promptContextPatterns.some(p => p.test(contextWindow));
        if (!isPromptContext)
            continue;
        // Check for template interpolation of secret variables
        const templateMatch = line.match(/\$\{(\w+)\}/);
        if (templateMatch) {
            const varName = templateMatch[1];
            if (secretVariables.has(varName)) {
                const secretInfo = secretVariables.get(varName);
                let severity = 'high';
                let description = `Secret variable '${varName}' (defined at line ${secretInfo.line}) is interpolated into LLM prompt. This exposes the secret to the model provider.`;
                if (isTestFile) {
                    severity = 'low';
                    description += ' (in test file)';
                }
                vulnerabilities.push({
                    id: `secret-flow-${filePath}-${i + 1}-${varName}`,
                    filePath,
                    lineNumber: i + 1,
                    lineContent: line.trim(),
                    severity,
                    category: 'hardcoded_secret',
                    title: `Secret variable '${varName}' in prompt`,
                    description,
                    suggestedFix: `Remove the secret from the prompt. If the AI needs to use an API, make the call server-side instead of passing credentials to the model.`,
                    confidence: 'medium',
                    layer: 2,
                    source: 'ai_code',
                    requiresAIValidation: true,
                    baseConfidence: BASE_CONFIDENCE,
                });
            }
        }
        // Check for string concatenation with secret variables
        for (const [varName] of secretVariables) {
            if (line.includes(`+ ${varName}`) || line.includes(`${varName} +`) || line.includes(`+ ${varName} +`)) {
                const secretInfo = secretVariables.get(varName);
                let severity = 'high';
                let description = `Secret variable '${varName}' (defined at line ${secretInfo.line}) is concatenated into prompt. This exposes the secret to the model provider.`;
                if (isTestFile) {
                    severity = 'low';
                    description += ' (in test file)';
                }
                vulnerabilities.push({
                    id: `secret-concat-${filePath}-${i + 1}-${varName}`,
                    filePath,
                    lineNumber: i + 1,
                    lineContent: line.trim(),
                    severity,
                    category: 'hardcoded_secret',
                    title: `Secret variable '${varName}' concatenated in prompt`,
                    description,
                    suggestedFix: `Remove the secret from the prompt. If the AI needs to use an API, make the call server-side.`,
                    confidence: 'medium',
                    layer: 2,
                    source: 'ai_code',
                    requiresAIValidation: true,
                    baseConfidence: BASE_CONFIDENCE,
                });
            }
        }
    }
    return vulnerabilities;
}
// ============================================================================
// Phase 2: Indirect Prompt Injection Detection
// ============================================================================
/**
 * Check if content filtering/sanitization is present for external content
 */
function hasContentFiltering(content, lineNumber, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, lineNumber - 20);
    const contextEnd = Math.min(_lines.length, lineNumber + 10);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    const filteringPatterns = [
        /filterContent|sanitizeContent|cleanContent/i,
        /sanitizeContext|filterContext/i,
        /contentModeration|moderateContent/i,
        /stripInstructions|removeInstructions/i,
        /escapePrompt|sanitizePrompt/i,
        /validateInput|inputValidation/i,
    ];
    return filteringPatterns.some(p => p.test(context));
}
/**
 * Check if proper delimiters are used for external content
 */
function hasExternalContentDelimiters(content, lineNumber, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, lineNumber - 15);
    const contextEnd = Math.min(_lines.length, lineNumber + 15);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    const delimiterPatterns = [
        /<context>|<\/context>/i,
        /<document>|<\/document>/i,
        /<retrieved>|<\/retrieved>/i,
        /<external>|<\/external>/i,
        /```[^`]*context|context[^`]*```/i,
        /---\s*(?:context|document|retrieved)/i,
        /\[CONTEXT\]|\[\/CONTEXT\]/i,
        /\[DOCUMENT\]|\[\/DOCUMENT\]/i,
    ];
    return delimiterPatterns.some(p => p.test(context));
}
/**
 * Indirect prompt injection patterns - external content flowing to LLM context
 */
const INDIRECT_INJECTION_PATTERNS = [
    // ========== External Fetch to Prompt ==========
    {
        name: 'Fetched content in prompt',
        // Pattern looks for: fetch() -> then/await -> result flows into messages/content
        // Use word boundary \b to avoid matching function names like "validatedFetch"
        // The pattern looks for: actual fetch call -> await/then -> use in LLM messages
        pattern: /\bfetch\s*\(\s*[^)]+\)[\s\S]{0,80}(?:\.then|\.json)[\s\S]{0,150}(?:role:\s*['"`](?:system|user)['"`]|messages\s*:\s*\[)/gi,
        severity: 'high',
        description: 'Content fetched from external URL flows into LLM prompt. Malicious websites can embed instructions that hijack the model\'s behavior (indirect prompt injection).',
        suggestedFix: 'Wrap external content with clear delimiters: <external_content>...</external_content>. Implement content filtering to strip instruction-like patterns.',
        checkDelimiters: true,
    },
    {
        name: 'HTTP response in system prompt',
        pattern: /(?:axios|fetch|got|request)[\s\S]{0,150}(?:system|systemPrompt|instructions)\s*[:=+]/gi,
        severity: 'high',
        description: 'HTTP response content used in system prompt. External data in system prompts is especially dangerous as it can override model instructions.',
        suggestedFix: 'Never put external content in system prompts. Use user messages with clear delimiters for context. Implement content sanitization.',
        checkDelimiters: true,
    },
    // ========== RAG Vector Store to Prompt ==========
    {
        name: 'Vector store results in system message',
        pattern: /(?:vectorStore|similaritySearch|query|search|retrieve)[\s\S]{0,200}role:\s*['"`]system['"`]/gi,
        severity: 'high',
        description: 'Vector store search results injected into system message. Poisoned documents in the corpus can hijack model behavior.',
        suggestedFix: 'Place retrieved content in user messages, not system. Use delimiters: <retrieved_context>...</retrieved_context>. Implement document sanitization before indexing.',
        checkDelimiters: true,
    },
    {
        name: 'RAG retrieval directly in context',
        pattern: /(?:retriever\.invoke|retrieve|getRelevantDocuments)\s*\([^)]*\)[\s\S]{0,150}(?:context|prompt|messages)/gi,
        severity: 'high',
        description: 'Retrieved documents flow directly into LLM context. Adversarial documents can contain prompt injection payloads.',
        suggestedFix: 'Sanitize retrieved content before including in prompt. Use XML tags to clearly separate context from instructions.',
        checkDelimiters: true,
    },
    // ========== Document Loading to LLM ==========
    {
        name: 'Loaded documents in LLM chain',
        pattern: /(?:loadDocuments|DirectoryLoader|TextLoader|PDFLoader)[\s\S]{0,200}(?:chain|llm|invoke|call)/gi,
        severity: 'high',
        description: 'Documents loaded from files flow into LLM chain. Malicious files (PDFs, docs) can contain hidden prompt injection text.',
        suggestedFix: 'Scan loaded documents for instruction-like patterns. Use separate document processing pipeline with content filtering.',
        checkDelimiters: true,
    },
    {
        name: 'Document content interpolated',
        pattern: /\$\{.*(?:document|doc|file|page)(?:Content|Text|Data).*\}[\s\S]{0,50}(?:prompt|messages|llm)/gi,
        severity: 'medium',
        description: 'Document content interpolated into LLM prompt. Documents may contain adversarial instructions.',
        suggestedFix: 'Wrap document content with delimiters: ```document\\n${content}\\n```. Implement text sanitization.',
        checkDelimiters: true,
    },
    // ========== Web Scraping to Prompt ==========
    {
        name: 'Scraped content in prompt',
        pattern: /(?:scrape|crawl|spider|puppeteer|playwright|cheerio)[\s\S]{0,200}(?:prompt|messages|context|content\s*:)/gi,
        severity: 'high',
        description: 'Web-scraped content flows into LLM prompt. Malicious websites can embed instructions in their HTML content.',
        suggestedFix: 'Sanitize scraped content to remove instruction-like patterns. Use delimiters: <scraped_content url="...">...</scraped_content>',
        checkDelimiters: true,
    },
    {
        name: 'HTML content in LLM context',
        // Pattern: Reading HTML (.innerHTML) and then using it in prompt/messages
        // NOT: Writing LLM output TO innerHTML (that's output handling, different category)
        // Look for: getting innerHTML value -> flowing to prompt context
        pattern: /(?:\.innerHTML\s*[;,]|\.html\s*\(\s*\))[\s\S]{0,150}(?:role:\s*['"`](?:system|user)['"`]|messages\s*:\s*\[)/gi,
        severity: 'medium',
        description: 'HTML content from web pages used in LLM context. Web pages can contain hidden prompt injection in metadata, comments, or invisible text.',
        suggestedFix: 'Extract only relevant text content. Filter out scripts, comments, and metadata. Use content sanitization.',
        checkDelimiters: true,
    },
    // ========== Email/Message Content to Prompt ==========
    {
        name: 'Email content in prompt',
        pattern: /(?:email|message|inbox)(?:Content|Body|Text)[\s\S]{0,150}(?:prompt|messages|llm|analyze)/gi,
        severity: 'medium',
        description: 'Email or message content flows into LLM prompt. Attackers can craft emails with embedded prompt injection.',
        suggestedFix: 'Sanitize email content before LLM processing. Remove potentially malicious patterns. Use clear delimiters.',
        checkDelimiters: true,
    },
    // ========== Database Content to Prompt ==========
    {
        name: 'Database record in system prompt',
        pattern: /(?:findOne|findById|query|select)[\s\S]{0,150}(?:system|systemPrompt|instructions)\s*[:=]/gi,
        severity: 'medium',
        description: 'Database content used in system prompt. If users can modify database records, they can inject malicious instructions.',
        suggestedFix: 'Keep system prompts static. Place database content in user messages with delimiters. Validate data before use.',
        checkDelimiters: true,
    },
    // ========== Generic External Data Patterns ==========
    {
        name: 'External data concatenation',
        pattern: /(?:externalData|fetchedContent|scrapedData|retrievedText)\s*\+[\s\S]{0,50}(?:prompt|system|instructions)/gi,
        severity: 'medium',
        description: 'External data concatenated with prompt content without clear separation.',
        suggestedFix: 'Use structured prompts with XML/markdown delimiters to separate instructions from external content.',
        checkDelimiters: true,
    },
];
/**
 * Missing boundary patterns - prompts without clear user/system separation
 */
const MISSING_BOUNDARY_PATTERNS = [
    // Direct concatenation without any markers
    {
        name: 'Missing prompt boundaries',
        pattern: /(?:content|prompt)\s*[:=]\s*(?:systemInstructions?|instructions?)\s*\+\s*(?:userMessage|userInput|input)/gi,
        severity: 'medium',
        description: 'Prompt concatenates system instructions with user input without clear boundaries.',
        suggestedFix: 'Add delimiters between instructions and user content: "Instructions...\n---\n" + userInput + "\n---"',
    },
    // Template literals building prompts without delimiters
    {
        name: 'Unbounded template prompt',
        pattern: /`(?:You are|As an|Your task)[^`]{20,}\$\{(?!.*(?:```|<user|---|\[USER))/gi,
        severity: 'medium',
        description: 'Prompt template interpolates values without clear delimiter boundaries.',
        suggestedFix: 'Wrap interpolated user content with delimiters: ```${userInput}```',
    },
    // M5: RAG-specific prompt injection patterns
    {
        name: 'Retrieved context in system prompt',
        pattern: /role:\s*['"`]system['"`]\s*,\s*content:\s*`[^`]*\$\{.*(?:context|chunks|documents|retrieved|sources)/gi,
        severity: 'high',
        description: 'Retrieved documents injected into system prompt. Poisoned documents could hijack model behavior.',
        suggestedFix: 'Place retrieved context in user messages with clear delimiters. Use structured prompts separating instructions from data.',
        checkDelimiters: true,
    },
    {
        name: 'Mixed user input and retrieved context',
        pattern: /\$\{.*(?:userInput|query|question).*\}[^`]*\$\{.*(?:context|chunks|documents).*\}|\$\{.*(?:context|chunks|documents).*\}[^`]*\$\{.*(?:userInput|query|question).*\}/gi,
        severity: 'medium',
        description: 'User input and retrieved context concatenated without clear separation. Both could contain injection attempts.',
        suggestedFix: 'Clearly separate user input from retrieved context using XML tags or delimiters: <user_query>...</user_query><context>...</context>',
        checkDelimiters: true,
    },
    {
        name: 'RAG context directly interpolated',
        pattern: /(?:system|prompt)\s*[:=].*(?:retrievedContext|ragContext|documentContext|knowledgeBase)\s*(?:\+|,)/gi,
        severity: 'medium',
        description: 'RAG context directly concatenated into prompt. Could enable data poisoning attacks.',
        suggestedFix: 'Use structured prompt format with clear boundaries between instructions, context, and user input.',
        checkDelimiters: true,
    },
];
// ============================================================================
// Sprint 6: Model-Specific Injection Syntax Detection
// ============================================================================
/**
 * Model-specific injection markers that could manipulate prompt structure
 * These patterns detect when user input might contain control tokens
 */
const MODEL_SPECIFIC_INJECTION_PATTERNS = [
    // Claude/ChatML XML-style markers
    {
        name: 'Claude/ChatML injection markers in user input',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*<\|?(?:system|human|assistant|user)\|?>/gi,
        severity: 'high',
        description: 'User input may contain system/role markers that could manipulate prompt structure. Attackers can inject fake system or assistant messages.',
        suggestedFix: 'Strip or escape control tokens from user input: input.replace(/<\\|?(?:system|human|assistant|user)\\|?>/gi, "")',
    },
    // OpenAI ChatML markers
    {
        name: 'OpenAI ChatML control tokens',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*<\|im_(?:start|end)\|>/gi,
        severity: 'high',
        description: 'User input contains OpenAI ChatML control tokens (<|im_start|>, <|im_end|>) that could break message boundaries.',
        suggestedFix: 'Filter ChatML tokens from user input before processing: input.replace(/<\\|im_(?:start|end)\\|>/gi, "")',
    },
    // Anthropic Human/Assistant turn markers
    {
        name: 'Anthropic turn markers in user input',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*\\n\\n(?:Human|Assistant):\s*/gi,
        severity: 'medium',
        description: 'User input contains Anthropic turn markers (Human:, Assistant:) that could inject fake assistant responses.',
        suggestedFix: 'Sanitize turn markers from user input: input.replace(/\\n\\n(Human|Assistant):\\s*/gi, "")',
    },
    // Generic role injection attempts
    {
        name: 'Role injection in user input',
        pattern: /`[^`]*\$\{[^}]*(?:user|input|query)[^}]*\}[^`]*(?:system|assistant|Human:|Assistant:|<\|)/gi,
        severity: 'high',
        description: 'User input is interpolated near role markers without proper boundaries. Could enable role impersonation.',
        suggestedFix: 'Use strict message formatting and strip role-like patterns from user input.',
        checkDelimiters: true,
    },
    // Instruction override attempts in templates
    {
        name: 'Instruction override pattern',
        pattern: /`[^`]*\$\{[^}]*\}[^`]*(?:ignore\s+(?:all\s+)?previous|disregard\s+(?:your\s+)?(?:rules|instructions)|you\s+are\s+now)/gi,
        severity: 'medium',
        description: 'Template allows interpolation near common jailbreak phrases. User could inject instruction override attempts.',
        suggestedFix: 'Filter jailbreak patterns from user input before interpolation.',
        checkDelimiters: true,
    },
];
// ============================================================================
// Sprint 6: Encoding-Based Escape Detection
// ============================================================================
/**
 * Patterns for detecting encoding-based prompt injection bypasses
 */
const ENCODING_ESCAPE_PATTERNS = [
    // Base64 decoded content flowing to prompts
    {
        name: 'Base64 decoded content in prompt',
        pattern: /(?:atob|Buffer\.from|base64\.decode|b64decode)\s*\([^)]+\)[^;]*(?:\+|,)[^;]*(?:prompt|system|message|content)/gi,
        severity: 'medium',
        description: 'Decoded base64 content concatenated with prompts. Attackers can hide malicious instructions in base64 encoding to bypass filters.',
        suggestedFix: 'Validate and sanitize decoded content before including in prompts. Apply same security checks to decoded content.',
    },
    // URL decoded content in prompts
    {
        name: 'URL decoded content in prompt',
        pattern: /(?:unescape|decodeURIComponent|decodeURI|urllib\.parse\.unquote)\s*\([^)]+\)[^;]*(?:\+|,)[^;]*(?:prompt|system|message|content)/gi,
        severity: 'medium',
        description: 'URL decoded content flows into prompt. Encoded payloads can bypass input sanitization.',
        suggestedFix: 'Sanitize content after decoding. Apply prompt injection filters to the decoded output.',
    },
    // HTML entity decoded content
    {
        name: 'HTML decoded content in prompt',
        pattern: /(?:htmlDecode|decodeHTMLEntities|he\.decode|html\.unescape)\s*\([^)]+\)[^;]*(?:\+|,)[^;]*(?:prompt|system|message|content)/gi,
        severity: 'medium',
        description: 'HTML decoded content flows into prompt. HTML entities can hide malicious instructions.',
        suggestedFix: 'Apply prompt injection filters after HTML decoding.',
    },
    // JSON parsed content directly in prompt (could contain encoded payloads)
    {
        name: 'Unvalidated JSON in prompt',
        pattern: /JSON\.parse\s*\([^)]*(?:userInput|body|request|external)[^)]*\)[^;]*(?:\+|,)[^;]*(?:prompt|system|message)/gi,
        severity: 'medium',
        description: 'Parsed JSON content directly used in prompt. JSON values could contain encoded injection payloads.',
        suggestedFix: 'Validate JSON schema and sanitize string values before including in prompts.',
        checkDelimiters: true,
    },
    // Unicode escape sequences that could hide instructions
    {
        name: 'Unicode content in prompt',
        pattern: /(?:String\.fromCharCode|String\.fromCodePoint|chr\(|unichr\()\s*\([^)]+\)[^;]*(?:\+|,)[^;]*(?:prompt|system|message)/gi,
        severity: 'low',
        description: 'Unicode character construction flows into prompt. Could be used to hide malicious characters.',
        suggestedFix: 'Normalize and validate Unicode content before including in prompts.',
    },
];
// ============================================================================
// Sprint 6: Jailbreak Pattern Detection
// ============================================================================
/**
 * Common jailbreak preamble patterns that indicate injection attempts
 * These detect when user input flow might contain jailbreak phrases
 */
const JAILBREAK_INDICATOR_PATTERNS = [
    // Instruction override phrases flowing to LLM
    {
        name: 'Instruction override phrases in input flow',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*(?:ignore\s+(?:all\s+)?previous\s+(?:instructions|prompts)|disregard\s+(?:your\s+)?(?:rules|guidelines|instructions))/gi,
        severity: 'high',
        description: 'User input variable contains instruction override phrases. Classic jailbreak attempt detected.',
        suggestedFix: 'Implement jailbreak detection filter. Block or sanitize inputs containing instruction override patterns.',
    },
    // Role-playing jailbreak attempts
    {
        name: 'Role-playing jailbreak in input',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*(?:you\s+are\s+now\s+(?:a|an)\s+\w+|pretend\s+(?:you|to\s+be)\s+(?:are\s+)?(?:a|an|not)|act\s+as\s+(?:if|though)\s+you)/gi,
        severity: 'medium',
        description: 'User input contains role-playing jailbreak patterns. Attempts to make model assume a different persona.',
        suggestedFix: 'Filter role-manipulation phrases from user input. Implement persona consistency checks.',
    },
    // "From now on" style instruction changes
    {
        name: 'Instruction change phrases',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*(?:from\s+now\s+on\s+(?:you\s+will|ignore)|for\s+the\s+rest\s+of\s+this\s+(?:conversation|session))/gi,
        severity: 'medium',
        description: 'User input contains temporal instruction override attempts. Tries to change model behavior for the session.',
        suggestedFix: 'Sanitize phrases that attempt to change ongoing behavior.',
    },
    // Developer mode / DAN style jailbreaks
    {
        name: 'Developer mode jailbreak patterns',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*(?:developer\s+mode|DAN|Do\s+Anything\s+Now|jailbreak|no\s+restrictions)/gi,
        severity: 'high',
        description: 'User input contains known jailbreak terminology (DAN, developer mode). High-confidence malicious input.',
        suggestedFix: 'Block inputs containing known jailbreak terminology. Log for security review.',
    },
    // Hypothetical scenario framing
    {
        name: 'Hypothetical framing jailbreak',
        pattern: /(?:userInput|userMessage|input|message|query)\s*[=:][^\n]*(?:hypothetically|in\s+a\s+(?:fictional|imaginary)\s+(?:world|scenario)|what\s+if\s+you\s+(?:could|had\s+no))/gi,
        severity: 'low',
        description: 'User input uses hypothetical framing often used in jailbreak attempts. May be legitimate creative use.',
        suggestedFix: 'Apply additional scrutiny to hypothetically-framed requests. Consider context before blocking.',
    },
];
/**
 * Check if input sanitization is present for jailbreak patterns
 */
function hasJailbreakFiltering(content, lineNumber, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, lineNumber - 20);
    const contextEnd = Math.min(_lines.length, lineNumber + 10);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    const filteringPatterns = [
        /filterJailbreak|detectJailbreak|jailbreakFilter/i,
        /sanitizePrompt|filterPrompt|cleanPrompt/i,
        /blockInjection|preventInjection/i,
        /moderationApi|contentModeration/i,
        /instructionFilter|roleFilter/i,
        /guardRails|guardrail/i,
        /promptGuard|inputGuard/i,
    ];
    return filteringPatterns.some(p => p.test(context));
}
/**
 * Check if encoding sanitization is present
 */
function hasEncodingSanitization(content, lineNumber, lines) {
    const _lines = lines ?? content.split('\n');
    const contextStart = Math.max(0, lineNumber - 15);
    const contextEnd = Math.min(_lines.length, lineNumber + 5);
    const context = _lines.slice(contextStart, contextEnd).join('\n');
    const sanitizationPatterns = [
        /validateDecoded|sanitizeDecoded/i,
        /afterDecode.*sanitize|decode.*then.*filter/i,
        /normalizeInput|sanitizeInput/i,
        /schema\.parse|validate.*schema/i,
        /stripControlChars|removeControlTokens/i,
    ];
    return sanitizationPatterns.some(p => p.test(context));
}
// ============================================================================
// Detection Functions
// ============================================================================
/**
 * Get surrounding context lines for analysis
 */
function getSurroundingContext(content, lineIndex, windowSize = 10, lines) {
    const _lines = lines ?? content.split('\n');
    const start = Math.max(0, lineIndex - windowSize);
    const end = Math.min(_lines.length, lineIndex + windowSize);
    return _lines.slice(start, end);
}
/**
 * Main detection function for AI prompt hygiene issues
 */
function detectAIPromptHygiene(content, filePath, options) {
    const vulnerabilities = [];
    // Skip non-applicable files
    if ((0, file_classifier_1.isScannerOrFixtureFile)(filePath))
        return vulnerabilities;
    if ((0, file_classifier_1.isDocumentationFile)(filePath))
        return vulnerabilities;
    // Only scan files that appear to be in LLM context
    if (!isLLMContextFile(filePath, content)) {
        return vulnerabilities;
    }
    const lines = options?.parsed?.lines ?? content.split('\n');
    const isTestFile = (0, file_classifier_1.isTestOrMockFile)(filePath);
    // Scan for unsafe interpolation patterns (B1)
    for (const pattern of UNSAFE_INTERPOLATION_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            // Skip if properly parameterized
            if (isProperlyParameterized(lineContent))
                continue;
            // Check for delimiters if applicable
            let severity = pattern.severity;
            let description = pattern.description;
            const contextLines = getSurroundingContext(content, lineNumber - 1, 15, lines);
            if (pattern.checkDelimiters && hasPromptDelimiters(lineContent, contextLines)) {
                // Delimiters present - downgrade severity
                severity = 'info';
                description += ' (Note: Delimiters detected in context, which mitigates this risk.)';
            }
            // Downgrade test files
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-prompt-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name,
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: severity === 'info' ? 'low' : 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: severity !== 'info',
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // Scan for secrets in prompts (B3) - Original context-aware patterns
    for (const pattern of SECRETS_IN_PROMPTS_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            // Check if it's an env var reference (safe pattern)
            const isEnvRef = /process\.env|import\.meta\.env|os\.environ|getenv/i.test(lineContent);
            if (isEnvRef)
                continue;
            // Skip test variable names
            if (/(?:const|let|var)\s+(?:TEST|MOCK|EXAMPLE|DUMMY|FAKE|SAMPLE)[_A-Z0-9]*\s*=/i.test(lineContent))
                continue;
            if (/(?:const|let|var)\s+\w*(?:test|mock|example|dummy|fake|sample)\w*\s*=/i.test(lineContent))
                continue;
            // Skip placeholder/example values in the line
            if (/example|sample|demo|placeholder|your[_-]?api[_-]?key/i.test(lineContent))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            // Downgrade test files but still flag
            if (isTestFile) {
                severity = severity === 'critical' ? 'medium' : 'low';
                description += ' (in test file - still review for accidental commits)';
            }
            vulnerabilities.push({
                id: `ai-secret-prompt-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'hardcoded_secret', // Use existing category for consistency
                title: pattern.name + ' (in LLM context)',
                description: description + ' Secrets in prompts are especially risky as they may be logged, shared, or sent to external AI providers.',
                suggestedFix: pattern.suggestedFix,
                confidence: 'high',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: false, // Secrets don't need AI validation - they're definitive
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // ========== NEW: Direct secret detection with known prefixes ==========
    // Scan for any known secret patterns anywhere in prompt-related code
    const seenSecretLines = new Set(); // Avoid duplicates
    for (const secretDef of KNOWN_SECRET_PREFIXES) {
        const regex = new RegExp(secretDef.pattern.source, secretDef.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 if already reported on this line
            const lineKey = `${lineNumber}-${secretDef.name}`;
            if (seenSecretLines.has(lineNumber))
                continue;
            seenSecretLines.add(lineNumber);
            // Skip comments
            if ((0, file_classifier_1.isComment)(lineContent))
                continue;
            // Skip env var references
            if (/process\.env|import\.meta\.env|os\.environ|getenv/i.test(lineContent))
                continue;
            // Skip obvious placeholders/examples in the value
            const matchValue = match[0];
            if (/example|sample|demo|dummy|fake|mock|your[_-]|placeholder/i.test(matchValue))
                continue;
            if (/example|sample|demo|placeholder/i.test(lineContent))
                continue;
            // Skip values that contain "test" right after the prefix (e.g., sk-test..., ghp_test...)
            // These are clearly test/development keys, not production secrets
            if (/^(sk-|ghp_|gho_|sk_live_|sk_test_|xoxb-|SG\.)test/i.test(matchValue))
                continue;
            if (/[-_]test[-_0-9]/i.test(matchValue))
                continue;
            // Skip test variable names (e.g., TEST_API_KEY, MOCK_SECRET)
            if (/(?:const|let|var)\s+(?:TEST|MOCK|EXAMPLE|DUMMY|FAKE|SAMPLE)[_A-Z0-9]*\s*=/i.test(lineContent))
                continue;
            // Skip if variable name contains test/mock/example (broader check)
            if (/(?:const|let|var)\s+\w*(?:test|mock|example|dummy|fake|sample)\w*\s*=/i.test(lineContent))
                continue;
            let severity = secretDef.severity;
            let description = `${secretDef.name} detected in LLM-related code. This secret may be exposed to the model provider, logged, or cached.`;
            // Downgrade test files
            if (isTestFile) {
                severity = severity === 'critical' ? 'medium' : 'low';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-direct-secret-${filePath}-${lineNumber}-${secretDef.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'hardcoded_secret',
                title: `${secretDef.name} in LLM context`,
                description,
                suggestedFix: 'Remove the hardcoded secret. Use environment variables server-side. Never expose secrets to LLM prompts.',
                confidence: 'high',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: false,
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // ========== NEW: Variable flow detection ==========
    // Detect secrets flowing from variables into prompts
    const flowVulns = detectSecretVariableFlow(content, filePath, isTestFile, lines);
    vulnerabilities.push(...flowVulns);
    // Scan for missing boundary patterns (B1 continued)
    for (const pattern of MISSING_BOUNDARY_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            const contextLines = getSurroundingContext(content, lineNumber - 1, 10, lines);
            // Skip if delimiters are present
            if (hasPromptDelimiters(lineContent, contextLines))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-boundary-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name,
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: true,
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // Scan for indirect prompt injection patterns (Phase 2)
    for (const pattern of INDIRECT_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            // Check for content filtering/sanitization
            const hasFiltering = hasContentFiltering(content, lineNumber, lines);
            const hasDelimiters = hasExternalContentDelimiters(content, lineNumber, lines);
            if (hasFiltering && hasDelimiters) {
                // Both mitigations present - fully mitigated
                severity = 'info';
                description += ' (Content filtering and delimiters detected - mitigated.)';
            }
            else if (hasFiltering) {
                // Partial mitigation - filtering present
                severity = severity === 'high' ? 'medium' : 'low';
                description += ' (Content filtering detected.)';
            }
            else if (hasDelimiters) {
                // Partial mitigation - delimiters present
                severity = severity === 'high' ? 'medium' : 'low';
                description += ' (External content delimiters detected.)';
            }
            // Downgrade test files
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-indirect-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name + ' (Indirect Injection)',
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: severity === 'info' ? 'low' : 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: severity !== 'info',
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // ========== Sprint 6: Model-specific injection markers ==========
    for (const pattern of MODEL_SPECIFIC_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            const contextLines = getSurroundingContext(content, lineNumber - 1, 15, lines);
            // Check for delimiters/sanitization
            if (pattern.checkDelimiters && hasPromptDelimiters(lineContent, contextLines)) {
                severity = 'info';
                description += ' (Delimiters detected, risk mitigated.)';
            }
            // Check for jailbreak filtering
            if (hasJailbreakFiltering(content, lineNumber, lines)) {
                severity = severity === 'high' ? 'medium' : 'low';
                description += ' (Jailbreak filtering detected.)';
            }
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-model-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name,
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: severity === 'info' ? 'low' : 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: severity !== 'info' && severity !== 'low',
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // ========== Sprint 6: Encoding-based escape detection ==========
    for (const pattern of ENCODING_ESCAPE_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            const contextLines = getSurroundingContext(content, lineNumber - 1, 15, lines);
            // Check for encoding sanitization
            if (hasEncodingSanitization(content, lineNumber, lines)) {
                severity = 'info';
                description += ' (Encoding sanitization detected.)';
            }
            // Check for delimiters
            if (pattern.checkDelimiters && hasPromptDelimiters(lineContent, contextLines)) {
                severity = 'info';
                description += ' (Delimiters detected.)';
            }
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-encoding-escape-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name + ' (Encoding Bypass)',
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: severity !== 'info',
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    // ========== Sprint 6: Jailbreak pattern detection ==========
    for (const pattern of JAILBREAK_INDICATOR_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 ((0, file_classifier_1.isComment)(lineContent))
                continue;
            let severity = pattern.severity;
            let description = pattern.description;
            // Check for jailbreak filtering
            if (hasJailbreakFiltering(content, lineNumber, lines)) {
                severity = 'info';
                description += ' (Jailbreak filtering detected - mitigated.)';
            }
            if (isTestFile) {
                severity = 'info';
                description += ' (in test file)';
            }
            vulnerabilities.push({
                id: `ai-jailbreak-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
                filePath,
                lineNumber,
                lineContent,
                severity,
                category: 'ai_prompt_injection',
                title: pattern.name + ' (Jailbreak Risk)',
                description,
                suggestedFix: pattern.suggestedFix,
                confidence: severity === 'info' ? 'low' : 'medium',
                layer: 2,
                source: 'ai_code',
                requiresAIValidation: severity !== 'info' && severity !== 'low',
                baseConfidence: BASE_CONFIDENCE,
            });
        }
    }
    return vulnerabilities;
}
//# sourceMappingURL=prompt-hygiene.js.map