// engine/runtime-security/checkers/prompt-injection.ts — SPEC-084 AC-03
// Detects prompt injection patterns in text inputs using regex + heuristics.
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
const PATTERNS_PATH = resolve(MODULE_DIR, '../../../config/injection-patterns.json');
let cachedPatterns = null;
function loadPatterns() {
    if (cachedPatterns) {
        return cachedPatterns;
    }
    try {
        const raw = readFileSync(PATTERNS_PATH, 'utf-8');
        const parsed = JSON.parse(raw);
        cachedPatterns = parsed.patterns;
        return cachedPatterns;
    }
    catch {
        return [];
    }
}
// Unicode homoglyph detection: characters that look like ASCII but aren't
const HOMOGLYPH_RANGES = /[\u0400-\u04FF\u2000-\u206F\uFF00-\uFFEF\u200B-\u200F\u202A-\u202E]/;
// Role delimiter patterns used by various LLM APIs
const ROLE_DELIMITERS = [
    /<\|im_start\|>/i,
    /<\|im_end\|>/i,
    /\[INST\]/i,
    /\[\/INST\]/i,
    /<<SYS>>/i,
    /<\/SYS>>/i,
    /\bsystem\s*:/i,
    /\bassistant\s*:/i,
    /\buser\s*:/i,
];
function computeInjectionScore(text, patterns, customPatterns) {
    let score = 0;
    const lowerText = text.toLowerCase();
    // Check configurable patterns
    const allPatterns = [...patterns, ...customPatterns];
    for (const p of allPatterns) {
        if (lowerText.includes(p.toLowerCase())) {
            score += 0.25;
        }
    }
    // Check role delimiters (higher weight)
    for (const regex of ROLE_DELIMITERS) {
        if (regex.test(text)) {
            score += 0.35;
        }
    }
    // Check homoglyphs
    if (HOMOGLYPH_RANGES.test(text)) {
        score += 0.15;
    }
    return Math.min(score, 1.0);
}
function isTextInput(value) {
    return typeof value === 'string' && value.length > 10;
}
export const promptInjectionChecker = {
    type: 'prompt-injection',
    check(_toolName, inputs, config) {
        const threats = [];
        const patterns = loadPatterns();
        const customPatterns = config.customInjectionPatterns;
        for (const [key, value] of Object.entries(inputs)) {
            if (!isTextInput(value)) {
                continue;
            }
            const score = computeInjectionScore(value, patterns, customPatterns);
            if (score > 0.7) {
                threats.push({
                    type: 'prompt-injection',
                    severity: 'critical',
                    message: `High-risk prompt injection detected (score: ${score.toFixed(2)})`,
                    field: key,
                });
            }
            else if (score > 0.3) {
                threats.push({
                    type: 'prompt-injection',
                    severity: 'warning',
                    message: `Possible prompt injection detected (score: ${score.toFixed(2)})`,
                    field: key,
                });
            }
            else if (score > 0) {
                threats.push({
                    type: 'prompt-injection',
                    severity: 'info',
                    message: `Low-risk injection pattern detected (score: ${score.toFixed(2)})`,
                    field: key,
                });
            }
        }
        return threats;
    },
};
/** Exported for testing */
export { computeInjectionScore, loadPatterns };
//# sourceMappingURL=prompt-injection.js.map