/**
 * Tests for Web Application Tactics Module
 *
 * @module __tests__/agents/adversary/tactics/web-app
 */
import { describe, it, expect } from "vitest";
import { webAppTactic } from "../../../../agents/adversary/tactics/web-app.js";
// ============================================================================
// Test Helpers
// ============================================================================
function createFileContext(content, relativePath = "test.tsx") {
    return {
        path: `/project/${relativePath}`,
        relativePath,
        content,
        language: "typescript",
        lines: content.split("\n"),
    };
}
function createConfig() {
    return {
        model: "claude-sonnet-4-20250514",
        aggressiveness: "active",
        focusAreas: ["web-app"],
        maxAnalysisTime: 300000,
        generatePoC: true,
    };
}
// ============================================================================
// XSS Tests
// ============================================================================
describe("Web App Tactics - XSS Detection", () => {
    const config = createConfig();
    it("should detect stored XSS via dangerouslySetInnerHTML", async () => {
        const code = `
export function Comment({ data }) {
  return (
    <div dangerouslySetInnerHTML={{ __html: data.comment }} />
  );
}`;
        const file = createFileContext(code, "components/Comment.tsx");
        const findings = await webAppTactic.analyzeFile(file, config);
        expect(findings.length).toBeGreaterThan(0);
        const xssFindings = findings.filter((f) => f.patternId === "xss-stored-dangerouslysetinnerhtml");
        expect(xssFindings.length).toBeGreaterThan(0);
        expect(xssFindings[0].severity).toBe("critical");
        expect(xssFindings[0].cweIds).toContain("CWE-79");
    });
    it("should not flag sanitized dangerouslySetInnerHTML", async () => {
        const code = `
import DOMPurify from 'dompurify';

export function Comment({ data }) {
  return (
    <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(data.comment) }} />
  );
}`;
        const file = createFileContext(code, "components/Comment.tsx");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId === "xss-stored-dangerouslysetinnerhtml");
        expect(xssFindings.length).toBe(0);
    });
    it("should detect stored XSS via innerHTML", async () => {
        const code = `
function renderComment(comment) {
  const div = document.getElementById('comment');
  div.innerHTML = req.body.commentText;
}`;
        const file = createFileContext(code, "public/script.js");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId === "xss-stored-innerhtml");
        expect(xssFindings.length).toBeGreaterThan(0);
        expect(xssFindings[0].severity).toBe("critical");
    });
    it("should detect reflected XSS via query parameter", async () => {
        const code = `
app.get('/search', (req, res) => {
  res.send('<h1>Results for: ' + req.query.q + '</h1>');
});`;
        const file = createFileContext(code, "routes/search.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId === "xss-reflected-query-param");
        expect(xssFindings.length).toBeGreaterThan(0);
        expect(xssFindings[0].severity).toBe("high");
    });
    it("should detect DOM XSS via location.hash", async () => {
        const code = `
function updateContent() {
  document.getElementById('content').innerHTML = location.hash.slice(1);
}`;
        const file = createFileContext(code, "public/app.js");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId === "xss-dom-location-hash");
        expect(xssFindings.length).toBeGreaterThan(0);
        expect(xssFindings[0].severity).toBe("high");
    });
    it("should detect DOM XSS via postMessage", async () => {
        const code = `
window.addEventListener('message', (event) => {
  document.body.innerHTML = event.data;
});`;
        const file = createFileContext(code, "public/iframe.js");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId === "xss-dom-postmessage");
        expect(xssFindings.length).toBeGreaterThan(0);
        expect(xssFindings[0].severity).toBe("high");
    });
});
// ============================================================================
// CSRF Tests
// ============================================================================
describe("Web App Tactics - CSRF Detection", () => {
    const config = createConfig();
    it("should detect missing CSRF token on POST endpoint", async () => {
        const code = `
router.post('/api/profile/update', async (req, res) => {
  await db.users.update({ id: req.user.id, email: req.body.email });
  res.json({ success: true });
});`;
        const file = createFileContext(code, "routes/profile.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const csrfFindings = findings.filter((f) => f.patternId === "csrf-missing-token");
        expect(csrfFindings.length).toBeGreaterThan(0);
        expect(csrfFindings[0].severity).toBe("high");
        expect(csrfFindings[0].cweIds).toContain("CWE-352");
    });
    it("should not flag endpoint with CSRF protection", async () => {
        const code = `
router.post('/api/profile/update', csrfProtection, async (req, res) => {
  if (!req.csrfToken()) return res.status(403).end();
  await db.users.update({ id: req.user.id, email: req.body.email });
  res.json({ success: true });
});`;
        const file = createFileContext(code, "routes/profile.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const csrfFindings = findings.filter((f) => f.patternId === "csrf-missing-token");
        expect(csrfFindings.length).toBe(0);
    });
    it("should detect disabled CSRF protection", async () => {
        const code = `
app.use(csrf({ csrf: false }));`;
        const file = createFileContext(code, "middleware/security.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const csrfFindings = findings.filter((f) => f.patternId === "csrf-disabled");
        expect(csrfFindings.length).toBeGreaterThan(0);
    });
    it("should detect SameSite=None cookies", async () => {
        const code = `
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'none'
});`;
        const file = createFileContext(code, "auth/session.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const csrfFindings = findings.filter((f) => f.patternId === "csrf-samesite-none");
        expect(csrfFindings.length).toBeGreaterThan(0);
        expect(csrfFindings[0].severity).toBe("medium");
    });
});
// ============================================================================
// Clickjacking Tests
// ============================================================================
describe("Web App Tactics - Clickjacking Detection", () => {
    const config = createConfig();
    it("should detect missing X-Frame-Options", async () => {
        const code = `
app.use(helmet({
  contentSecurityPolicy: true,
  hsts: true
}));`;
        const file = createFileContext(code, "middleware/security.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const clickjackingFindings = findings.filter((f) => f.patternId === "clickjacking-no-frame-options");
        expect(clickjackingFindings.length).toBeGreaterThan(0);
        expect(clickjackingFindings[0].severity).toBe("medium");
        expect(clickjackingFindings[0].cweIds).toContain("CWE-1021");
    });
    it("should detect disabled frameguard", async () => {
        const code = `
app.use(helmet({
  frameguard: false
}));`;
        const file = createFileContext(code, "middleware/security.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const clickjackingFindings = findings.filter((f) => f.patternId === "clickjacking-frame-options-disabled");
        expect(clickjackingFindings.length).toBeGreaterThan(0);
    });
});
// ============================================================================
// Open Redirect Tests
// ============================================================================
describe("Web App Tactics - Open Redirect Detection", () => {
    const config = createConfig();
    it("should detect open redirect via query parameter", async () => {
        const code = `
app.get('/redirect', (req, res) => {
  res.redirect(req.query.url);
});`;
        const file = createFileContext(code, "routes/redirect.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const redirectFindings = findings.filter((f) => f.patternId === "open-redirect-query-param");
        expect(redirectFindings.length).toBeGreaterThan(0);
        expect(redirectFindings[0].severity).toBe("medium");
        expect(redirectFindings[0].cweIds).toContain("CWE-601");
    });
    it("should not flag whitelisted redirects", async () => {
        const code = `
app.get('/redirect', (req, res) => {
  const url = req.query.url;
  if (allowedUrls.includes(url)) {
    res.redirect(url);
  }
});`;
        const file = createFileContext(code, "routes/redirect.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const redirectFindings = findings.filter((f) => f.patternId === "open-redirect-query-param");
        expect(redirectFindings.length).toBe(0);
    });
    it("should detect post-login open redirect", async () => {
        const code = `
async function login(req, res) {
  await authenticateUser(req.body);
  res.redirect(req.query.returnUrl);
}`;
        const file = createFileContext(code, "auth/login.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const redirectFindings = findings.filter((f) => f.patternId === "open-redirect-post-login");
        expect(redirectFindings.length).toBeGreaterThan(0);
        expect(redirectFindings[0].severity).toBe("high");
    });
});
// ============================================================================
// CORS Tests
// ============================================================================
describe("Web App Tactics - CORS Misconfiguration Detection", () => {
    const config = createConfig();
    it("should detect reflected origin in CORS", async () => {
        const code = `
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
  next();
});`;
        const file = createFileContext(code, "middleware/cors.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const corsFindings = findings.filter((f) => f.patternId === "cors-reflect-origin");
        expect(corsFindings.length).toBeGreaterThan(0);
        expect(corsFindings[0].severity).toBe("high");
        expect(corsFindings[0].cweIds).toContain("CWE-942");
    });
    it("should detect null origin allowed", async () => {
        const code = `
res.setHeader('Access-Control-Allow-Origin', 'null');`;
        const file = createFileContext(code, "api/cors.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const corsFindings = findings.filter((f) => f.patternId === "cors-null-origin");
        expect(corsFindings.length).toBeGreaterThan(0);
    });
    it("should detect wildcard with credentials", async () => {
        const code = `
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');`;
        const file = createFileContext(code, "api/public.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const corsFindings = findings.filter((f) => f.patternId === "cors-wildcard-credentials");
        expect(corsFindings.length).toBeGreaterThan(0);
        expect(corsFindings[0].severity).toBe("high");
    });
});
// ============================================================================
// PoC Generation Tests
// ============================================================================
describe("Web App Tactics - PoC Generation", () => {
    const config = createConfig();
    it("should generate PoC for stored XSS", async () => {
        const code = `<div dangerouslySetInnerHTML={{ __html: props.comment }} />`;
        const file = createFileContext(code, "components/Comment.tsx");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFinding = findings.find((f) => f.patternId === "xss-stored-dangerouslysetinnerhtml");
        expect(xssFinding).toBeDefined();
        if (xssFinding) {
            const poc = await webAppTactic.generatePoC(xssFinding);
            expect(poc).toBeDefined();
            expect(poc?.steps.length).toBeGreaterThan(0);
            expect(poc?.payload).toBeDefined();
            expect(poc?.safeTestInstructions).toBeDefined();
        }
    });
    it("should generate PoC for CSRF", async () => {
        const code = `router.post('/api/transfer', async (req, res) => {
  await transfer(req.body);
});`;
        const file = createFileContext(code, "routes/banking.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const csrfFinding = findings.find((f) => f.patternId === "csrf-missing-token");
        expect(csrfFinding).toBeDefined();
        if (csrfFinding) {
            const poc = await webAppTactic.generatePoC(csrfFinding);
            expect(poc).toBeDefined();
            expect(poc?.steps.length).toBeGreaterThan(0);
            expect(poc?.payload).toContain("form");
        }
    });
    it("should generate PoC for open redirect", async () => {
        const code = `res.redirect(req.query.next);`;
        const file = createFileContext(code, "auth/callback.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const redirectFinding = findings.find((f) => f.patternId === "open-redirect-query-param");
        expect(redirectFinding).toBeDefined();
        if (redirectFinding) {
            const poc = await webAppTactic.generatePoC(redirectFinding);
            expect(poc).toBeDefined();
            expect(poc?.steps.length).toBeGreaterThan(0);
            expect(poc?.expectedResult.toLowerCase()).toContain("redirect");
        }
    });
});
// ============================================================================
// False Positive Tests
// ============================================================================
describe("Web App Tactics - False Positive Filtering", () => {
    const config = createConfig();
    it("should skip test files", async () => {
        const code = `
test('should render comment', () => {
  const html = '<script>alert(1)</script>';
  element.innerHTML = html;
});`;
        const file = createFileContext(code, "components/Comment.test.tsx");
        const findings = await webAppTactic.analyzeFile(file, config);
        expect(findings.length).toBe(0);
    });
    it("should skip comments", async () => {
        const code = `
// Example: dangerouslySetInnerHTML={{ __html: userContent }}
// This would be vulnerable if used in production`;
        const file = createFileContext(code, "docs/security.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        expect(findings.length).toBe(0);
    });
    it("should skip sanitized content", async () => {
        const code = `
const sanitized = sanitizeHtml(userInput);
element.innerHTML = sanitized;`;
        const file = createFileContext(code, "utils/dom.ts");
        const findings = await webAppTactic.analyzeFile(file, config);
        const xssFindings = findings.filter((f) => f.patternId.startsWith("xss-"));
        expect(xssFindings.length).toBe(0);
    });
});
// ============================================================================
// Prompt Enhancement Tests
// ============================================================================
describe("Web App Tactics - Prompt Enhancement", () => {
    it("should provide comprehensive prompt enhancement", () => {
        const enhancement = webAppTactic.getPromptEnhancement();
        expect(enhancement).toContain("XSS");
        expect(enhancement).toContain("CSRF");
        expect(enhancement).toContain("Clickjacking");
        expect(enhancement).toContain("Open Redirect");
        expect(enhancement).toContain("CORS");
        expect(enhancement).toContain("sanitization");
        expect(enhancement).toContain("encoding");
    });
    it("should specify relevant file patterns", () => {
        const patterns = webAppTactic.getRelevantFilePatterns();
        expect(patterns).toContain("**/components/**");
        expect(patterns).toContain("**/pages/**");
        expect(patterns).toContain("**/*.tsx");
        expect(patterns).toContain("**/*.jsx");
        expect(patterns).toContain("**/*.vue");
    });
});
//# sourceMappingURL=web-app.test.js.map