@vulcn/engine 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../index.ts","../session.ts","../browser.ts","../recorder.ts","../payloads.ts","../runner.ts"],"sourcesContent":["// Core exports\nexport { Recorder, type RecordingSession } from \"./recorder\";\nexport { Runner } from \"./runner\";\nexport {\n createSession,\n parseSession,\n serializeSession,\n SessionSchema,\n StepSchema,\n type Session,\n type Step,\n} from \"./session\";\n\n// Browser utilities\nexport {\n launchBrowser,\n installBrowsers,\n checkBrowsers,\n BrowserNotFoundError,\n type LaunchOptions,\n type BrowserLaunchResult,\n} from \"./browser\";\n\n// Payloads\nexport {\n BUILTIN_PAYLOADS,\n getPayload,\n getPayloadNames,\n getPayloadsByCategory,\n type Payload,\n type PayloadCategory,\n type PayloadName,\n} from \"./payloads\";\n\n// Types\nexport type {\n BrowserType,\n RecorderOptions,\n RunnerOptions,\n Finding,\n RunResult,\n} from \"./types\";\n","import { z } from \"zod\";\nimport { parse, stringify } from \"yaml\";\n\n// Step types\nexport const StepSchema = z.discriminatedUnion(\"type\", [\n z.object({\n id: z.string(),\n type: z.literal(\"navigate\"),\n url: z.string(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"click\"),\n selector: z.string(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"input\"),\n selector: z.string(),\n value: z.string(),\n injectable: z.boolean().optional().default(true),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"keypress\"),\n key: z.string(),\n modifiers: z.array(z.string()).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"scroll\"),\n selector: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"wait\"),\n duration: z.number(),\n timestamp: z.number(),\n }),\n]);\n\nexport type Step = z.infer<typeof StepSchema>;\n\n// Session schema\nexport const SessionSchema = z.object({\n version: z.string().default(\"1\"),\n name: z.string(),\n recordedAt: z.string(),\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).default(\"chromium\"),\n viewport: z.object({\n width: z.number(),\n height: z.number(),\n }),\n startUrl: z.string(),\n steps: z.array(StepSchema),\n});\n\nexport type Session = z.infer<typeof SessionSchema>;\n\n/**\n * Create a new session object\n */\nexport function createSession(options: {\n name: string;\n startUrl: string;\n browser?: \"chromium\" | \"firefox\" | \"webkit\";\n viewport?: { width: number; height: number };\n}): Session {\n return {\n version: \"1\",\n name: options.name,\n recordedAt: new Date().toISOString(),\n browser: options.browser ?? \"chromium\",\n viewport: options.viewport ?? { width: 1280, height: 720 },\n startUrl: options.startUrl,\n steps: [],\n };\n}\n\n/**\n * Parse a session from YAML string\n */\nexport function parseSession(yaml: string): Session {\n const data = parse(yaml);\n return SessionSchema.parse(data);\n}\n\n/**\n * Serialize a session to YAML string\n */\nexport function serializeSession(session: Session): string {\n return stringify(session, { lineWidth: 0 });\n}\n","import { chromium, firefox, webkit, type Browser } from \"playwright\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { BrowserType } from \"./types\";\n\nconst execAsync = promisify(exec);\n\nexport interface LaunchOptions {\n browser?: BrowserType;\n headless?: boolean;\n}\n\nexport interface BrowserLaunchResult {\n browser: Browser;\n channel?: string;\n}\n\nexport class BrowserNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BrowserNotFoundError\";\n }\n}\n\n/**\n * Launch a browser with smart fallback:\n * 1. Try system Chrome/Edge first (zero-install experience)\n * 2. Fall back to Playwright's bundled browsers\n */\nexport async function launchBrowser(\n options: LaunchOptions = {},\n): Promise<BrowserLaunchResult> {\n const browserType = options.browser ?? \"chromium\";\n const headless = options.headless ?? false;\n\n // For Chromium, try system browsers first\n if (browserType === \"chromium\") {\n // Try system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless,\n });\n return { browser, channel: \"chrome\" };\n } catch {\n // Chrome not available\n }\n\n // Try system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless,\n });\n return { browser, channel: \"msedge\" };\n } catch {\n // Edge not available\n }\n\n // Fall back to Playwright's bundled Chromium\n try {\n const browser = await chromium.launch({ headless });\n return { browser, channel: \"chromium\" };\n } catch {\n throw new BrowserNotFoundError(\n \"No Chromium browser found. Install Chrome or run: vulcn install chromium\",\n );\n }\n }\n\n // Firefox\n if (browserType === \"firefox\") {\n try {\n const browser = await firefox.launch({ headless });\n return { browser, channel: \"firefox\" };\n } catch {\n throw new BrowserNotFoundError(\n \"Firefox not found. Run: vulcn install firefox\",\n );\n }\n }\n\n // WebKit\n if (browserType === \"webkit\") {\n try {\n const browser = await webkit.launch({ headless });\n return { browser, channel: \"webkit\" };\n } catch {\n throw new BrowserNotFoundError(\n \"WebKit not found. Run: vulcn install webkit\",\n );\n }\n }\n\n throw new BrowserNotFoundError(`Unknown browser type: ${browserType}`);\n}\n\n/**\n * Install Playwright browsers\n */\nexport async function installBrowsers(\n browsers: BrowserType[] = [\"chromium\"],\n): Promise<void> {\n const browserArg = browsers.join(\" \");\n await execAsync(`npx playwright install ${browserArg}`);\n}\n\n/**\n * Check which browsers are available\n */\nexport async function checkBrowsers(): Promise<{\n systemChrome: boolean;\n systemEdge: boolean;\n playwrightChromium: boolean;\n playwrightFirefox: boolean;\n playwrightWebkit: boolean;\n}> {\n const results = {\n systemChrome: false,\n systemEdge: false,\n playwrightChromium: false,\n playwrightFirefox: false,\n playwrightWebkit: false,\n };\n\n // Check system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless: true,\n });\n await browser.close();\n results.systemChrome = true;\n } catch {\n // Not available\n }\n\n // Check system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless: true,\n });\n await browser.close();\n results.systemEdge = true;\n } catch {\n // Not available\n }\n\n // Check Playwright Chromium\n try {\n const browser = await chromium.launch({ headless: true });\n await browser.close();\n results.playwrightChromium = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright Firefox\n try {\n const browser = await firefox.launch({ headless: true });\n await browser.close();\n results.playwrightFirefox = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright WebKit\n try {\n const browser = await webkit.launch({ headless: true });\n await browser.close();\n results.playwrightWebkit = true;\n } catch {\n // Not installed\n }\n\n return results;\n}\n","import { Page } from \"playwright\";\nimport { createSession, type Session, type Step } from \"./session\";\nimport { launchBrowser } from \"./browser\";\nimport type { RecorderOptions } from \"./types\";\n\n/**\n * Active recording session handle\n */\nexport interface RecordingSession {\n /** Stop recording and return the session */\n stop(): Promise<Session>;\n /** Get current recorded steps */\n getSteps(): Step[];\n /** Get the Playwright page (for advanced use) */\n getPage(): Page;\n}\n\n/**\n * Recorder - captures browser interactions as a replayable session\n */\nexport class Recorder {\n /**\n * Start a new recording session\n * Opens a browser window for the user to interact with\n */\n static async start(\n startUrl: string,\n options: RecorderOptions = {},\n ): Promise<RecordingSession> {\n const browserType = options.browser ?? \"chromium\";\n const viewport = options.viewport ?? { width: 1280, height: 720 };\n const headless = options.headless ?? false;\n\n // Launch browser with smart fallback (system Chrome first)\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport });\n const page = await context.newPage();\n\n // Navigate to start URL\n await page.goto(startUrl);\n\n // Create session\n const session = createSession({\n name: `Recording ${new Date().toISOString()}`,\n startUrl,\n browser: browserType,\n viewport,\n });\n\n // Track recording start time\n const startTime = Date.now();\n const steps: Step[] = [];\n let stepCounter = 0;\n\n const generateStepId = () => {\n stepCounter++;\n return `step_${String(stepCounter).padStart(3, \"0\")}`;\n };\n\n // Add initial navigation step\n steps.push({\n id: generateStepId(),\n type: \"navigate\",\n url: startUrl,\n timestamp: 0,\n });\n\n // Attach event listeners\n Recorder.attachListeners(page, steps, startTime, generateStepId);\n\n return {\n async stop() {\n session.steps = steps;\n await browser.close();\n return session;\n },\n getSteps() {\n return [...steps];\n },\n getPage() {\n return page;\n },\n };\n }\n\n private static attachListeners(\n page: Page,\n steps: Step[],\n startTime: number,\n generateStepId: () => string,\n ) {\n const getTimestamp = () => Date.now() - startTime;\n\n // Track navigation\n page.on(\"framenavigated\", (frame) => {\n if (frame === page.mainFrame()) {\n const url = frame.url();\n // Avoid duplicate nav steps for initial load\n const lastStep = steps[steps.length - 1];\n if (\n steps.length > 0 &&\n lastStep.type === \"navigate\" &&\n lastStep.url === url\n ) {\n return;\n }\n steps.push({\n id: generateStepId(),\n type: \"navigate\",\n url,\n timestamp: getTimestamp(),\n });\n }\n });\n\n // Expose recording function to browser\n page.exposeFunction(\n \"__vulcn_record\",\n (event: { type: string; data: Record<string, unknown> }) => {\n const timestamp = getTimestamp();\n\n switch (event.type) {\n case \"click\": {\n const data = event.data as {\n selector: string;\n x: number;\n y: number;\n };\n steps.push({\n id: generateStepId(),\n type: \"click\",\n selector: data.selector,\n position: { x: data.x, y: data.y },\n timestamp,\n });\n break;\n }\n case \"input\": {\n const data = event.data as {\n selector: string;\n value: string;\n inputType: string | null;\n injectable: boolean;\n };\n steps.push({\n id: generateStepId(),\n type: \"input\",\n selector: data.selector,\n value: data.value,\n injectable: data.injectable,\n timestamp,\n });\n break;\n }\n case \"keypress\": {\n const data = event.data as { key: string; modifiers?: string[] };\n steps.push({\n id: generateStepId(),\n type: \"keypress\",\n key: data.key,\n modifiers: data.modifiers,\n timestamp,\n });\n break;\n }\n }\n },\n );\n\n // Inject recording script into every frame\n page.on(\"load\", async () => {\n await Recorder.injectRecordingScript(page);\n });\n\n // Inject into initial page\n Recorder.injectRecordingScript(page);\n }\n\n private static async injectRecordingScript(page: Page) {\n await page.evaluate(`\n (function() {\n if (window.__vulcn_injected) return;\n window.__vulcn_injected = true;\n\n var textInputTypes = ['text', 'password', 'email', 'search', 'url', 'tel', 'number'];\n\n function getSelector(el) {\n if (el.id) {\n return '#' + CSS.escape(el.id);\n }\n if (el.name) {\n var tag = el.tagName.toLowerCase();\n var nameSelector = tag + '[name=\"' + el.name + '\"]';\n if (document.querySelectorAll(nameSelector).length === 1) {\n return nameSelector;\n }\n }\n if (el.dataset && el.dataset.testid) {\n return '[data-testid=\"' + el.dataset.testid + '\"]';\n }\n if (el.tagName === 'INPUT' && el.type && el.name) {\n var inputSelector = 'input[type=\"' + el.type + '\"][name=\"' + el.name + '\"]';\n if (document.querySelectorAll(inputSelector).length === 1) {\n return inputSelector;\n }\n }\n if (el.className && typeof el.className === 'string') {\n var classes = el.className.trim().split(/\\\\s+/).filter(function(c) { return c.length > 0; });\n if (classes.length > 0) {\n var classSelector = el.tagName.toLowerCase() + '.' + classes.map(function(c) { return CSS.escape(c); }).join('.');\n if (document.querySelectorAll(classSelector).length === 1) {\n return classSelector;\n }\n }\n }\n var path = [];\n var current = el;\n while (current && current !== document.body) {\n var tag = current.tagName.toLowerCase();\n var parent = current.parentElement;\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName; });\n if (siblings.length > 1) {\n var index = siblings.indexOf(current) + 1;\n tag = tag + ':nth-of-type(' + index + ')';\n }\n }\n path.unshift(tag);\n current = parent;\n }\n return path.join(' > ');\n }\n\n function getInputType(el) {\n if (el.tagName === 'INPUT') return el.type || 'text';\n if (el.tagName === 'TEXTAREA') return 'textarea';\n if (el.tagName === 'SELECT') return 'select';\n return null;\n }\n\n function isTextInjectable(el) {\n var inputType = getInputType(el);\n if (!inputType) return false;\n if (inputType === 'textarea') return true;\n if (inputType === 'select') return false;\n return textInputTypes.indexOf(inputType) !== -1;\n }\n\n document.addEventListener('click', function(e) {\n var target = e.target;\n window.__vulcn_record({\n type: 'click',\n data: {\n selector: getSelector(target),\n x: e.clientX,\n y: e.clientY\n }\n });\n }, true);\n\n document.addEventListener('change', function(e) {\n var target = e.target;\n if ('value' in target) {\n var inputType = getInputType(target);\n window.__vulcn_record({\n type: 'input',\n data: {\n selector: getSelector(target),\n value: target.value,\n inputType: inputType,\n injectable: isTextInjectable(target)\n }\n });\n }\n }, true);\n\n document.addEventListener('keydown', function(e) {\n if (e.ctrlKey || e.metaKey || e.altKey) {\n var modifiers = [];\n if (e.ctrlKey) modifiers.push('ctrl');\n if (e.metaKey) modifiers.push('meta');\n if (e.altKey) modifiers.push('alt');\n if (e.shiftKey) modifiers.push('shift');\n\n window.__vulcn_record({\n type: 'keypress',\n data: {\n key: e.key,\n modifiers: modifiers\n }\n });\n }\n }, true);\n })();\n `);\n }\n}\n","/**\n * Built-in security payloads\n */\n\nexport type PayloadCategory = \"xss\" | \"sqli\" | \"ssrf\" | \"path-traversal\";\nexport type PayloadName =\n | \"xss-basic\"\n | \"xss-event\"\n | \"xss-svg\"\n | \"sqli-basic\"\n | \"sqli-error\"\n | \"sqli-blind\";\n\nexport interface Payload {\n name: PayloadName;\n category: PayloadCategory;\n payloads: string[];\n detectPatterns: RegExp[];\n description: string;\n}\n\nexport const BUILTIN_PAYLOADS: Record<PayloadName, Payload> = {\n \"xss-basic\": {\n name: \"xss-basic\",\n category: \"xss\",\n description: \"Basic XSS payloads with script tags and event handlers\",\n payloads: [\n '<script>alert(\"XSS\")</script>',\n '<img src=x onerror=alert(\"XSS\")>',\n '\"><script>alert(\"XSS\")</script>',\n \"javascript:alert('XSS')\",\n '<svg onload=alert(\"XSS\")>',\n ],\n detectPatterns: [\n /<script[^>]*>alert\\(/i,\n /onerror\\s*=\\s*alert\\(/i,\n /onload\\s*=\\s*alert\\(/i,\n /javascript:alert\\(/i,\n ],\n },\n \"xss-event\": {\n name: \"xss-event\",\n category: \"xss\",\n description: \"XSS via event handlers\",\n payloads: [\n '\" onfocus=\"alert(1)\" autofocus=\"',\n \"' onmouseover='alert(1)'\",\n '<body onload=alert(\"XSS\")>',\n \"<input onfocus=alert(1) autofocus>\",\n \"<marquee onstart=alert(1)>\",\n ],\n detectPatterns: [\n /onfocus\\s*=\\s*[\"']?alert/i,\n /onmouseover\\s*=\\s*[\"']?alert/i,\n /onload\\s*=\\s*[\"']?alert/i,\n /onstart\\s*=\\s*[\"']?alert/i,\n ],\n },\n \"xss-svg\": {\n name: \"xss-svg\",\n category: \"xss\",\n description: \"XSS via SVG elements\",\n payloads: [\n '<svg/onload=alert(\"XSS\")>',\n \"<svg><script>alert(1)</script></svg>\",\n \"<svg><animate onbegin=alert(1)>\",\n \"<svg><set onbegin=alert(1)>\",\n ],\n detectPatterns: [\n /<svg[^>]*onload\\s*=/i,\n /<svg[^>]*>.*<script>/i,\n /onbegin\\s*=\\s*alert/i,\n ],\n },\n \"sqli-basic\": {\n name: \"sqli-basic\",\n category: \"sqli\",\n description: \"Basic SQL injection payloads\",\n payloads: [\n \"' OR '1'='1\",\n \"' OR '1'='1' --\",\n \"1' OR '1'='1\",\n \"admin'--\",\n \"' UNION SELECT NULL--\",\n ],\n detectPatterns: [\n /sql.*syntax/i,\n /mysql.*error/i,\n /ORA-\\d{5}/i,\n /pg_query/i,\n /sqlite.*error/i,\n /unclosed.*quotation/i,\n ],\n },\n \"sqli-error\": {\n name: \"sqli-error\",\n category: \"sqli\",\n description: \"SQL injection payloads to trigger errors\",\n payloads: [\"'\", \"''\", \"`\", '\"', \"')\", \"'\\\"\", \"1' AND '1'='2\", \"1 AND 1=2\"],\n detectPatterns: [\n /sql.*syntax/i,\n /mysql.*error/i,\n /ORA-\\d{5}/i,\n /postgresql.*error/i,\n /sqlite.*error/i,\n /quoted.*string.*properly.*terminated/i,\n ],\n },\n \"sqli-blind\": {\n name: \"sqli-blind\",\n category: \"sqli\",\n description: \"Blind SQL injection payloads\",\n payloads: [\n \"1' AND SLEEP(5)--\",\n \"1; WAITFOR DELAY '0:0:5'--\",\n \"1' AND (SELECT COUNT(*) FROM information_schema.tables)>0--\",\n ],\n detectPatterns: [\n // Blind SQLi is detected by timing, not content\n ],\n },\n};\n\n/**\n * Get payloads by name\n */\nexport function getPayload(name: PayloadName): Payload | undefined {\n return BUILTIN_PAYLOADS[name];\n}\n\n/**\n * Get all payload names\n */\nexport function getPayloadNames(): PayloadName[] {\n return Object.keys(BUILTIN_PAYLOADS) as PayloadName[];\n}\n\n/**\n * Get payloads by category\n */\nexport function getPayloadsByCategory(category: PayloadCategory): Payload[] {\n return Object.values(BUILTIN_PAYLOADS).filter((p) => p.category === category);\n}\n","import type { Page } from \"playwright\";\nimport { launchBrowser } from \"./browser\";\nimport { BUILTIN_PAYLOADS, type PayloadName } from \"./payloads\";\nimport type { Session, Step } from \"./session\";\nimport type { Finding, RunResult, RunnerOptions } from \"./types\";\n\n/**\n * Runner - replays sessions with security payloads\n */\nexport class Runner {\n /**\n * Execute a session with security payloads\n */\n static async execute(\n session: Session,\n payloadNames: PayloadName[],\n options: RunnerOptions = {},\n ): Promise<RunResult> {\n const browserType = options.browser ?? session.browser ?? \"chromium\";\n const headless = options.headless ?? true;\n const startTime = Date.now();\n\n const findings: Finding[] = [];\n const errors: string[] = [];\n let payloadsTested = 0;\n\n // Launch browser\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport: session.viewport });\n const page = await context.newPage();\n\n try {\n // Find injectable steps\n const injectableSteps = session.steps.filter(\n (step): step is Step & { type: \"input\" } =>\n step.type === \"input\" && step.injectable !== false,\n );\n\n // Get all payloads\n const allPayloads: { name: PayloadName; value: string }[] = [];\n for (const name of payloadNames) {\n const payload = BUILTIN_PAYLOADS[name];\n if (payload) {\n for (const value of payload.payloads) {\n allPayloads.push({ name, value });\n }\n }\n }\n\n // For each injectable step, test with each payload\n for (const injectableStep of injectableSteps) {\n for (const payload of allPayloads) {\n try {\n // Replay session up to this step with payload injected\n const finding = await Runner.replayWithPayload(\n page,\n session,\n injectableStep,\n payload.name,\n payload.value,\n );\n\n if (finding) {\n findings.push(finding);\n options.onFinding?.(finding);\n }\n\n payloadsTested++;\n } catch (err) {\n errors.push(`${injectableStep.id}: ${String(err)}`);\n }\n }\n }\n } finally {\n await browser.close();\n }\n\n return {\n findings,\n stepsExecuted: session.steps.length,\n payloadsTested,\n duration: Date.now() - startTime,\n errors,\n };\n }\n\n private static async replayWithPayload(\n page: Page,\n session: Session,\n targetStep: Step & { type: \"input\" },\n payloadName: PayloadName,\n payloadValue: string,\n ): Promise<Finding | undefined> {\n // Navigate to start\n await page.goto(session.startUrl);\n\n // Replay steps\n for (const step of session.steps) {\n if (step.type === \"navigate\") {\n await page.goto(step.url);\n } else if (step.type === \"click\") {\n await page.click(step.selector, { timeout: 5000 });\n } else if (step.type === \"input\") {\n // Inject payload for target step\n const value = step.id === targetStep.id ? payloadValue : step.value;\n await page.fill(step.selector, value, { timeout: 5000 });\n } else if (step.type === \"keypress\") {\n const modifiers = step.modifiers ?? [];\n for (const mod of modifiers) {\n await page.keyboard.down(mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\");\n }\n await page.keyboard.press(step.key);\n for (const mod of modifiers.reverse()) {\n await page.keyboard.up(mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\");\n }\n }\n\n // If we just filled the target step, check for reflection\n if (step.id === targetStep.id) {\n const finding = await Runner.checkForVulnerability(\n page,\n targetStep,\n payloadName,\n payloadValue,\n );\n if (finding) {\n return finding;\n }\n }\n }\n\n return undefined;\n }\n\n private static async checkForVulnerability(\n page: Page,\n step: Step & { type: \"input\" },\n payloadName: PayloadName,\n payloadValue: string,\n ): Promise<Finding | undefined> {\n const payload = BUILTIN_PAYLOADS[payloadName];\n if (!payload) return undefined;\n\n // Get page content\n const content = await page.content();\n\n // Check for reflection patterns\n for (const pattern of payload.detectPatterns) {\n if (pattern.test(content)) {\n return {\n type: payload.category,\n severity: payload.category === \"xss\" ? \"high\" : \"critical\",\n title: `${payload.category.toUpperCase()} vulnerability detected`,\n description: `Payload was reflected in page content`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n evidence: content.match(pattern)?.[0],\n };\n }\n }\n\n // Check if payload appears verbatim (potential XSS)\n if (content.includes(payloadValue)) {\n return {\n type: payload.category,\n severity: \"medium\",\n title: `Potential ${payload.category.toUpperCase()} - payload reflection`,\n description: `Payload was reflected in page without encoding`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n };\n }\n\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,iBAAkB;AAClB,kBAAiC;AAG1B,IAAM,aAAa,aAAE,mBAAmB,QAAQ;AAAA,EACrD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,UAAU;AAAA,IAC1B,KAAK,aAAE,OAAO;AAAA,IACd,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AAAA,EACD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,OAAO;AAAA,IACvB,UAAU,aAAE,OAAO;AAAA,IACnB,UAAU,aAAE,OAAO,EAAE,GAAG,aAAE,OAAO,GAAG,GAAG,aAAE,OAAO,EAAE,CAAC,EAAE,SAAS;AAAA,IAC9D,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AAAA,EACD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,OAAO;AAAA,IACvB,UAAU,aAAE,OAAO;AAAA,IACnB,OAAO,aAAE,OAAO;AAAA,IAChB,YAAY,aAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,IAAI;AAAA,IAC/C,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AAAA,EACD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,UAAU;AAAA,IAC1B,KAAK,aAAE,OAAO;AAAA,IACd,WAAW,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,IACxC,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AAAA,EACD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,QAAQ;AAAA,IACxB,UAAU,aAAE,OAAO,EAAE,SAAS;AAAA,IAC9B,UAAU,aAAE,OAAO,EAAE,GAAG,aAAE,OAAO,GAAG,GAAG,aAAE,OAAO,EAAE,CAAC;AAAA,IACnD,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AAAA,EACD,aAAE,OAAO;AAAA,IACP,IAAI,aAAE,OAAO;AAAA,IACb,MAAM,aAAE,QAAQ,MAAM;AAAA,IACtB,UAAU,aAAE,OAAO;AAAA,IACnB,WAAW,aAAE,OAAO;AAAA,EACtB,CAAC;AACH,CAAC;AAKM,IAAM,gBAAgB,aAAE,OAAO;AAAA,EACpC,SAAS,aAAE,OAAO,EAAE,QAAQ,GAAG;AAAA,EAC/B,MAAM,aAAE,OAAO;AAAA,EACf,YAAY,aAAE,OAAO;AAAA,EACrB,SAAS,aAAE,KAAK,CAAC,YAAY,WAAW,QAAQ,CAAC,EAAE,QAAQ,UAAU;AAAA,EACrE,UAAU,aAAE,OAAO;AAAA,IACjB,OAAO,aAAE,OAAO;AAAA,IAChB,QAAQ,aAAE,OAAO;AAAA,EACnB,CAAC;AAAA,EACD,UAAU,aAAE,OAAO;AAAA,EACnB,OAAO,aAAE,MAAM,UAAU;AAC3B,CAAC;AAOM,SAAS,cAAc,SAKlB;AACV,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,QAAQ;AAAA,IACd,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,SAAS,QAAQ,WAAW;AAAA,IAC5B,UAAU,QAAQ,YAAY,EAAE,OAAO,MAAM,QAAQ,IAAI;AAAA,IACzD,UAAU,QAAQ;AAAA,IAClB,OAAO,CAAC;AAAA,EACV;AACF;AAKO,SAAS,aAAa,MAAuB;AAClD,QAAM,WAAO,mBAAM,IAAI;AACvB,SAAO,cAAc,MAAM,IAAI;AACjC;AAKO,SAAS,iBAAiB,SAA0B;AACzD,aAAO,uBAAU,SAAS,EAAE,WAAW,EAAE,CAAC;AAC5C;;;ACnGA,wBAAwD;AACxD,gCAAqB;AACrB,uBAA0B;AAG1B,IAAM,gBAAY,4BAAU,8BAAI;AAYzB,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAOA,eAAsB,cACpB,UAAyB,CAAC,GACI;AAC9B,QAAM,cAAc,QAAQ,WAAW;AACvC,QAAM,WAAW,QAAQ,YAAY;AAGrC,MAAI,gBAAgB,YAAY;AAE9B,QAAI;AACF,YAAM,UAAU,MAAM,2BAAS,OAAO;AAAA,QACpC,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AACD,aAAO,EAAE,SAAS,SAAS,SAAS;AAAA,IACtC,QAAQ;AAAA,IAER;AAGA,QAAI;AACF,YAAM,UAAU,MAAM,2BAAS,OAAO;AAAA,QACpC,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AACD,aAAO,EAAE,SAAS,SAAS,SAAS;AAAA,IACtC,QAAQ;AAAA,IAER;AAGA,QAAI;AACF,YAAM,UAAU,MAAM,2BAAS,OAAO,EAAE,SAAS,CAAC;AAClD,aAAO,EAAE,SAAS,SAAS,WAAW;AAAA,IACxC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,gBAAgB,WAAW;AAC7B,QAAI;AACF,YAAM,UAAU,MAAM,0BAAQ,OAAO,EAAE,SAAS,CAAC;AACjD,aAAO,EAAE,SAAS,SAAS,UAAU;AAAA,IACvC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,gBAAgB,UAAU;AAC5B,QAAI;AACF,YAAM,UAAU,MAAM,yBAAO,OAAO,EAAE,SAAS,CAAC;AAChD,aAAO,EAAE,SAAS,SAAS,SAAS;AAAA,IACtC,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,qBAAqB,yBAAyB,WAAW,EAAE;AACvE;AAKA,eAAsB,gBACpB,WAA0B,CAAC,UAAU,GACtB;AACf,QAAM,aAAa,SAAS,KAAK,GAAG;AACpC,QAAM,UAAU,0BAA0B,UAAU,EAAE;AACxD;AAKA,eAAsB,gBAMnB;AACD,QAAM,UAAU;AAAA,IACd,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,oBAAoB;AAAA,IACpB,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,EACpB;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,2BAAS,OAAO;AAAA,MACpC,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC;AACD,UAAM,QAAQ,MAAM;AACpB,YAAQ,eAAe;AAAA,EACzB,QAAQ;AAAA,EAER;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,2BAAS,OAAO;AAAA,MACpC,SAAS;AAAA,MACT,UAAU;AAAA,IACZ,CAAC;AACD,UAAM,QAAQ,MAAM;AACpB,YAAQ,aAAa;AAAA,EACvB,QAAQ;AAAA,EAER;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,2BAAS,OAAO,EAAE,UAAU,KAAK,CAAC;AACxD,UAAM,QAAQ,MAAM;AACpB,YAAQ,qBAAqB;AAAA,EAC/B,QAAQ;AAAA,EAER;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,0BAAQ,OAAO,EAAE,UAAU,KAAK,CAAC;AACvD,UAAM,QAAQ,MAAM;AACpB,YAAQ,oBAAoB;AAAA,EAC9B,QAAQ;AAAA,EAER;AAGA,MAAI;AACF,UAAM,UAAU,MAAM,yBAAO,OAAO,EAAE,UAAU,KAAK,CAAC;AACtD,UAAM,QAAQ,MAAM;AACpB,YAAQ,mBAAmB;AAAA,EAC7B,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;;;AC7JO,IAAM,WAAN,MAAM,UAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,aAAa,MACX,UACA,UAA2B,CAAC,GACD;AAC3B,UAAM,cAAc,QAAQ,WAAW;AACvC,UAAM,WAAW,QAAQ,YAAY,EAAE,OAAO,MAAM,QAAQ,IAAI;AAChE,UAAM,WAAW,QAAQ,YAAY;AAGrC,UAAM,EAAE,QAAQ,IAAI,MAAM,cAAc;AAAA,MACtC,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AACD,UAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,SAAS,CAAC;AACrD,UAAM,OAAO,MAAM,QAAQ,QAAQ;AAGnC,UAAM,KAAK,KAAK,QAAQ;AAGxB,UAAM,UAAU,cAAc;AAAA,MAC5B,MAAM,cAAa,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,MAC3C;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAGD,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,QAAgB,CAAC;AACvB,QAAI,cAAc;AAElB,UAAM,iBAAiB,MAAM;AAC3B;AACA,aAAO,QAAQ,OAAO,WAAW,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IACrD;AAGA,UAAM,KAAK;AAAA,MACT,IAAI,eAAe;AAAA,MACnB,MAAM;AAAA,MACN,KAAK;AAAA,MACL,WAAW;AAAA,IACb,CAAC;AAGD,cAAS,gBAAgB,MAAM,OAAO,WAAW,cAAc;AAE/D,WAAO;AAAA,MACL,MAAM,OAAO;AACX,gBAAQ,QAAQ;AAChB,cAAM,QAAQ,MAAM;AACpB,eAAO;AAAA,MACT;AAAA,MACA,WAAW;AACT,eAAO,CAAC,GAAG,KAAK;AAAA,MAClB;AAAA,MACA,UAAU;AACR,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAe,gBACb,MACA,OACA,WACA,gBACA;AACA,UAAM,eAAe,MAAM,KAAK,IAAI,IAAI;AAGxC,SAAK,GAAG,kBAAkB,CAAC,UAAU;AACnC,UAAI,UAAU,KAAK,UAAU,GAAG;AAC9B,cAAM,MAAM,MAAM,IAAI;AAEtB,cAAM,WAAW,MAAM,MAAM,SAAS,CAAC;AACvC,YACE,MAAM,SAAS,KACf,SAAS,SAAS,cAClB,SAAS,QAAQ,KACjB;AACA;AAAA,QACF;AACA,cAAM,KAAK;AAAA,UACT,IAAI,eAAe;AAAA,UACnB,MAAM;AAAA,UACN;AAAA,UACA,WAAW,aAAa;AAAA,QAC1B,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,SAAK;AAAA,MACH;AAAA,MACA,CAAC,UAA2D;AAC1D,cAAM,YAAY,aAAa;AAE/B,gBAAQ,MAAM,MAAM;AAAA,UAClB,KAAK,SAAS;AACZ,kBAAM,OAAO,MAAM;AAKnB,kBAAM,KAAK;AAAA,cACT,IAAI,eAAe;AAAA,cACnB,MAAM;AAAA,cACN,UAAU,KAAK;AAAA,cACf,UAAU,EAAE,GAAG,KAAK,GAAG,GAAG,KAAK,EAAE;AAAA,cACjC;AAAA,YACF,CAAC;AACD;AAAA,UACF;AAAA,UACA,KAAK,SAAS;AACZ,kBAAM,OAAO,MAAM;AAMnB,kBAAM,KAAK;AAAA,cACT,IAAI,eAAe;AAAA,cACnB,MAAM;AAAA,cACN,UAAU,KAAK;AAAA,cACf,OAAO,KAAK;AAAA,cACZ,YAAY,KAAK;AAAA,cACjB;AAAA,YACF,CAAC;AACD;AAAA,UACF;AAAA,UACA,KAAK,YAAY;AACf,kBAAM,OAAO,MAAM;AACnB,kBAAM,KAAK;AAAA,cACT,IAAI,eAAe;AAAA,cACnB,MAAM;AAAA,cACN,KAAK,KAAK;AAAA,cACV,WAAW,KAAK;AAAA,cAChB;AAAA,YACF,CAAC;AACD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,GAAG,QAAQ,YAAY;AAC1B,YAAM,UAAS,sBAAsB,IAAI;AAAA,IAC3C,CAAC;AAGD,cAAS,sBAAsB,IAAI;AAAA,EACrC;AAAA,EAEA,aAAqB,sBAAsB,MAAY;AACrD,UAAM,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAmHnB;AAAA,EACH;AACF;;;ACtRO,IAAM,mBAAiD;AAAA,EAC5D,aAAa;AAAA,IACX,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU,CAAC,KAAK,MAAM,KAAK,KAAK,MAAM,MAAO,iBAAiB,WAAW;AAAA,IACzE,gBAAgB;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,UAAU;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA;AAAA,IAEhB;AAAA,EACF;AACF;AAKO,SAAS,WAAW,MAAwC;AACjE,SAAO,iBAAiB,IAAI;AAC9B;AAKO,SAAS,kBAAiC;AAC/C,SAAO,OAAO,KAAK,gBAAgB;AACrC;AAKO,SAAS,sBAAsB,UAAsC;AAC1E,SAAO,OAAO,OAAO,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC9E;;;ACrIO,IAAM,SAAN,MAAM,QAAO;AAAA;AAAA;AAAA;AAAA,EAIlB,aAAa,QACX,SACA,cACA,UAAyB,CAAC,GACN;AACpB,UAAM,cAAc,QAAQ,WAAW,QAAQ,WAAW;AAC1D,UAAM,WAAW,QAAQ,YAAY;AACrC,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,WAAsB,CAAC;AAC7B,UAAM,SAAmB,CAAC;AAC1B,QAAI,iBAAiB;AAGrB,UAAM,EAAE,QAAQ,IAAI,MAAM,cAAc;AAAA,MACtC,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AACD,UAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,CAAC;AACvE,UAAM,OAAO,MAAM,QAAQ,QAAQ;AAEnC,QAAI;AAEF,YAAM,kBAAkB,QAAQ,MAAM;AAAA,QACpC,CAAC,SACC,KAAK,SAAS,WAAW,KAAK,eAAe;AAAA,MACjD;AAGA,YAAM,cAAsD,CAAC;AAC7D,iBAAW,QAAQ,cAAc;AAC/B,cAAM,UAAU,iBAAiB,IAAI;AACrC,YAAI,SAAS;AACX,qBAAW,SAAS,QAAQ,UAAU;AACpC,wBAAY,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,kBAAkB,iBAAiB;AAC5C,mBAAW,WAAW,aAAa;AACjC,cAAI;AAEF,kBAAM,UAAU,MAAM,QAAO;AAAA,cAC3B;AAAA,cACA;AAAA,cACA;AAAA,cACA,QAAQ;AAAA,cACR,QAAQ;AAAA,YACV;AAEA,gBAAI,SAAS;AACX,uBAAS,KAAK,OAAO;AACrB,sBAAQ,YAAY,OAAO;AAAA,YAC7B;AAEA;AAAA,UACF,SAAS,KAAK;AACZ,mBAAO,KAAK,GAAG,eAAe,EAAE,KAAK,OAAO,GAAG,CAAC,EAAE;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAAA,IACF,UAAE;AACA,YAAM,QAAQ,MAAM;AAAA,IACtB;AAEA,WAAO;AAAA,MACL;AAAA,MACA,eAAe,QAAQ,MAAM;AAAA,MAC7B;AAAA,MACA,UAAU,KAAK,IAAI,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAqB,kBACnB,MACA,SACA,YACA,aACA,cAC8B;AAE9B,UAAM,KAAK,KAAK,QAAQ,QAAQ;AAGhC,eAAW,QAAQ,QAAQ,OAAO;AAChC,UAAI,KAAK,SAAS,YAAY;AAC5B,cAAM,KAAK,KAAK,KAAK,GAAG;AAAA,MAC1B,WAAW,KAAK,SAAS,SAAS;AAChC,cAAM,KAAK,MAAM,KAAK,UAAU,EAAE,SAAS,IAAK,CAAC;AAAA,MACnD,WAAW,KAAK,SAAS,SAAS;AAEhC,cAAM,QAAQ,KAAK,OAAO,WAAW,KAAK,eAAe,KAAK;AAC9D,cAAM,KAAK,KAAK,KAAK,UAAU,OAAO,EAAE,SAAS,IAAK,CAAC;AAAA,MACzD,WAAW,KAAK,SAAS,YAAY;AACnC,cAAM,YAAY,KAAK,aAAa,CAAC;AACrC,mBAAW,OAAO,WAAW;AAC3B,gBAAM,KAAK,SAAS,KAAK,GAA2C;AAAA,QACtE;AACA,cAAM,KAAK,SAAS,MAAM,KAAK,GAAG;AAClC,mBAAW,OAAO,UAAU,QAAQ,GAAG;AACrC,gBAAM,KAAK,SAAS,GAAG,GAA2C;AAAA,QACpE;AAAA,MACF;AAGA,UAAI,KAAK,OAAO,WAAW,IAAI;AAC7B,cAAM,UAAU,MAAM,QAAO;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,YAAI,SAAS;AACX,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAqB,sBACnB,MACA,MACA,aACA,cAC8B;AAC9B,UAAM,UAAU,iBAAiB,WAAW;AAC5C,QAAI,CAAC,QAAS,QAAO;AAGrB,UAAM,UAAU,MAAM,KAAK,QAAQ;AAGnC,eAAW,WAAW,QAAQ,gBAAgB;AAC5C,UAAI,QAAQ,KAAK,OAAO,GAAG;AACzB,eAAO;AAAA,UACL,MAAM,QAAQ;AAAA,UACd,UAAU,QAAQ,aAAa,QAAQ,SAAS;AAAA,UAChD,OAAO,GAAG,QAAQ,SAAS,YAAY,CAAC;AAAA,UACxC,aAAa;AAAA,UACb,QAAQ,KAAK;AAAA,UACb,SAAS;AAAA,UACT,KAAK,KAAK,IAAI;AAAA,UACd,UAAU,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAGA,QAAI,QAAQ,SAAS,YAAY,GAAG;AAClC,aAAO;AAAA,QACL,MAAM,QAAQ;AAAA,QACd,UAAU;AAAA,QACV,OAAO,aAAa,QAAQ,SAAS,YAAY,CAAC;AAAA,QAClD,aAAa;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,SAAS;AAAA,QACT,KAAK,KAAK,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../index.ts","../session.ts","../browser.ts","../plugin-manager.ts","../plugin-types.ts","../recorder.ts","../runner.ts"],"sourcesContent":["/**\n * @vulcn/engine - Core security testing engine\n *\n * This is the minimal, low-level core that provides:\n * - Recording browser interactions\n * - Replaying sessions with payload injection\n * - Plugin system for extensibility\n *\n * Plugins handle:\n * - Payload loading (builtin, payloadbox, custom files)\n * - Vulnerability detection (reflection, execution, etc.)\n * - Reporting (JSON, SARIF, HTML)\n */\n\n// Core classes\nexport { Recorder, type RecordingSession } from \"./recorder\";\nexport { Runner } from \"./runner\";\n\n// Session handling\nexport {\n createSession,\n parseSession,\n serializeSession,\n SessionSchema,\n StepSchema,\n type Session,\n type Step,\n} from \"./session\";\n\n// Browser utilities\nexport {\n launchBrowser,\n installBrowsers,\n checkBrowsers,\n BrowserNotFoundError,\n type LaunchOptions,\n type BrowserLaunchResult,\n} from \"./browser\";\n\n// Core types\nexport type {\n BrowserType,\n RecorderOptions,\n RunnerOptions,\n Finding,\n RunResult,\n} from \"./types\";\n\n// Payload types (for plugins to use)\nexport type {\n PayloadCategory,\n PayloadSource,\n RuntimePayload,\n CustomPayload,\n CustomPayloadFile,\n} from \"./payload-types\";\n\n// Plugin System\nexport { PluginManager, pluginManager } from \"./plugin-manager\";\nexport { PLUGIN_API_VERSION } from \"./plugin-types\";\nexport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n PluginHooks,\n PluginContext,\n RecordContext,\n RunContext,\n DetectContext,\n LoadedPlugin,\n PluginLogger,\n EngineInfo,\n PluginSource,\n} from \"./plugin-types\";\n","import { z } from \"zod\";\nimport { parse, stringify } from \"yaml\";\n\n// Step types\nexport const StepSchema = z.discriminatedUnion(\"type\", [\n z.object({\n id: z.string(),\n type: z.literal(\"navigate\"),\n url: z.string(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"click\"),\n selector: z.string(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"input\"),\n selector: z.string(),\n value: z.string(),\n injectable: z.boolean().optional().default(true),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"keypress\"),\n key: z.string(),\n modifiers: z.array(z.string()).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"scroll\"),\n selector: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"wait\"),\n duration: z.number(),\n timestamp: z.number(),\n }),\n]);\n\nexport type Step = z.infer<typeof StepSchema>;\n\n// Session schema\nexport const SessionSchema = z.object({\n version: z.string().default(\"1\"),\n name: z.string(),\n recordedAt: z.string(),\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).default(\"chromium\"),\n viewport: z.object({\n width: z.number(),\n height: z.number(),\n }),\n startUrl: z.string(),\n steps: z.array(StepSchema),\n});\n\nexport type Session = z.infer<typeof SessionSchema>;\n\n/**\n * Create a new session object\n */\nexport function createSession(options: {\n name: string;\n startUrl: string;\n browser?: \"chromium\" | \"firefox\" | \"webkit\";\n viewport?: { width: number; height: number };\n}): Session {\n return {\n version: \"1\",\n name: options.name,\n recordedAt: new Date().toISOString(),\n browser: options.browser ?? \"chromium\",\n viewport: options.viewport ?? { width: 1280, height: 720 },\n startUrl: options.startUrl,\n steps: [],\n };\n}\n\n/**\n * Parse a session from YAML string\n */\nexport function parseSession(yaml: string): Session {\n const data = parse(yaml);\n return SessionSchema.parse(data);\n}\n\n/**\n * Serialize a session to YAML string\n */\nexport function serializeSession(session: Session): string {\n return stringify(session, { lineWidth: 0 });\n}\n","import { chromium, firefox, webkit, type Browser } from \"playwright\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { BrowserType } from \"./types\";\n\nconst execAsync = promisify(exec);\n\nexport interface LaunchOptions {\n browser?: BrowserType;\n headless?: boolean;\n}\n\nexport interface BrowserLaunchResult {\n browser: Browser;\n channel?: string;\n}\n\nexport class BrowserNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BrowserNotFoundError\";\n }\n}\n\n/**\n * Launch a browser with smart fallback:\n * 1. Try system Chrome/Edge first (zero-install experience)\n * 2. Fall back to Playwright's bundled browsers\n */\nexport async function launchBrowser(\n options: LaunchOptions = {},\n): Promise<BrowserLaunchResult> {\n const browserType = options.browser ?? \"chromium\";\n const headless = options.headless ?? false;\n\n // For Chromium, try system browsers first\n if (browserType === \"chromium\") {\n // Try system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless,\n });\n return { browser, channel: \"chrome\" };\n } catch {\n // Chrome not available\n }\n\n // Try system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless,\n });\n return { browser, channel: \"msedge\" };\n } catch {\n // Edge not available\n }\n\n // Fall back to Playwright's bundled Chromium\n try {\n const browser = await chromium.launch({ headless });\n return { browser, channel: \"chromium\" };\n } catch {\n throw new BrowserNotFoundError(\n \"No Chromium browser found. Install Chrome or run: vulcn install chromium\",\n );\n }\n }\n\n // Firefox\n if (browserType === \"firefox\") {\n try {\n const browser = await firefox.launch({ headless });\n return { browser, channel: \"firefox\" };\n } catch {\n throw new BrowserNotFoundError(\n \"Firefox not found. Run: vulcn install firefox\",\n );\n }\n }\n\n // WebKit\n if (browserType === \"webkit\") {\n try {\n const browser = await webkit.launch({ headless });\n return { browser, channel: \"webkit\" };\n } catch {\n throw new BrowserNotFoundError(\n \"WebKit not found. Run: vulcn install webkit\",\n );\n }\n }\n\n throw new BrowserNotFoundError(`Unknown browser type: ${browserType}`);\n}\n\n/**\n * Install Playwright browsers\n */\nexport async function installBrowsers(\n browsers: BrowserType[] = [\"chromium\"],\n): Promise<void> {\n const browserArg = browsers.join(\" \");\n await execAsync(`npx playwright install ${browserArg}`);\n}\n\n/**\n * Check which browsers are available\n */\nexport async function checkBrowsers(): Promise<{\n systemChrome: boolean;\n systemEdge: boolean;\n playwrightChromium: boolean;\n playwrightFirefox: boolean;\n playwrightWebkit: boolean;\n}> {\n const results = {\n systemChrome: false,\n systemEdge: false,\n playwrightChromium: false,\n playwrightFirefox: false,\n playwrightWebkit: false,\n };\n\n // Check system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless: true,\n });\n await browser.close();\n results.systemChrome = true;\n } catch {\n // Not available\n }\n\n // Check system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless: true,\n });\n await browser.close();\n results.systemEdge = true;\n } catch {\n // Not available\n }\n\n // Check Playwright Chromium\n try {\n const browser = await chromium.launch({ headless: true });\n await browser.close();\n results.playwrightChromium = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright Firefox\n try {\n const browser = await firefox.launch({ headless: true });\n await browser.close();\n results.playwrightFirefox = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright WebKit\n try {\n const browser = await webkit.launch({ headless: true });\n await browser.close();\n results.playwrightWebkit = true;\n } catch {\n // Not installed\n }\n\n return results;\n}\n","/**\n * Vulcn Plugin Manager\n * Handles plugin loading, lifecycle, and hook execution\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { resolve, isAbsolute } from \"node:path\";\nimport YAML from \"yaml\";\nimport { z } from \"zod\";\nimport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n LoadedPlugin,\n PluginContext,\n PluginSource,\n PluginLogger,\n EngineInfo,\n PluginHooks,\n} from \"./plugin-types\";\nimport { PLUGIN_API_VERSION } from \"./plugin-types\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\n\n// Package version (injected at build time or read from package.json)\nconst ENGINE_VERSION = \"0.2.0\";\n\n/**\n * Config file schema\n */\nconst VulcnConfigSchema = z.object({\n version: z.string().default(\"1\"),\n plugins: z\n .array(\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n enabled: z.boolean().default(true),\n }),\n )\n .optional(),\n settings: z\n .object({\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).optional(),\n headless: z.boolean().optional(),\n timeout: z.number().optional(),\n })\n .optional(),\n});\n\n/**\n * Plugin Manager - loads, configures, and orchestrates plugins\n */\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private config: VulcnConfig | null = null;\n private initialized = false;\n\n /**\n * Shared context passed to all plugins\n */\n private sharedPayloads: RuntimePayload[] = [];\n private sharedFindings: Finding[] = [];\n\n /**\n * Load configuration from vulcn.config.yml\n */\n async loadConfig(configPath?: string): Promise<VulcnConfig> {\n const paths = configPath\n ? [configPath]\n : [\n \"vulcn.config.yml\",\n \"vulcn.config.yaml\",\n \"vulcn.config.json\",\n \".vulcnrc.yml\",\n \".vulcnrc.yaml\",\n \".vulcnrc.json\",\n ];\n\n for (const path of paths) {\n const resolved = isAbsolute(path) ? path : resolve(process.cwd(), path);\n if (existsSync(resolved)) {\n const content = await readFile(resolved, \"utf-8\");\n const parsed = path.endsWith(\".json\")\n ? JSON.parse(content)\n : YAML.parse(content);\n this.config = VulcnConfigSchema.parse(parsed);\n return this.config;\n }\n }\n\n // No config file - use defaults\n this.config = { version: \"1\", plugins: [], settings: {} };\n return this.config;\n }\n\n /**\n * Load all plugins from config\n */\n async loadPlugins(): Promise<void> {\n if (!this.config) {\n await this.loadConfig();\n }\n\n const pluginConfigs = this.config?.plugins || [];\n\n for (const pluginConfig of pluginConfigs) {\n if (pluginConfig.enabled === false) continue;\n\n try {\n const loaded = await this.loadPlugin(pluginConfig);\n this.plugins.push(loaded);\n } catch (err) {\n console.error(\n `Failed to load plugin ${pluginConfig.name}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n /**\n * Load a single plugin\n */\n private async loadPlugin(config: PluginConfig): Promise<LoadedPlugin> {\n const { name, config: pluginConfig = {} } = config;\n let plugin: VulcnPlugin;\n let source: PluginSource;\n\n // Determine plugin source and load\n if (name.startsWith(\"./\") || name.startsWith(\"../\") || isAbsolute(name)) {\n // Local file plugin\n const resolved = isAbsolute(name) ? name : resolve(process.cwd(), name);\n const module = await import(resolved);\n plugin = module.default || module;\n source = \"local\";\n } else if (name.startsWith(\"@vulcn/\")) {\n // Official plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n } else {\n // Community plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n }\n\n // Validate plugin structure\n this.validatePlugin(plugin);\n\n // Validate plugin config if schema provided\n let resolvedConfig = pluginConfig;\n if (plugin.configSchema) {\n try {\n resolvedConfig = plugin.configSchema.parse(pluginConfig);\n } catch (err) {\n throw new Error(\n `Invalid config for plugin ${name}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n return {\n plugin,\n config: resolvedConfig,\n source,\n enabled: true,\n };\n }\n\n /**\n * Validate plugin structure\n */\n private validatePlugin(plugin: unknown): asserts plugin is VulcnPlugin {\n if (!plugin || typeof plugin !== \"object\") {\n throw new Error(\"Plugin must be an object\");\n }\n\n const p = plugin as Record<string, unknown>;\n if (typeof p.name !== \"string\" || !p.name) {\n throw new Error(\"Plugin must have a name\");\n }\n if (typeof p.version !== \"string\" || !p.version) {\n throw new Error(\"Plugin must have a version\");\n }\n\n // Check API version compatibility\n const apiVersion = (p.apiVersion as number) || 1;\n if (apiVersion > PLUGIN_API_VERSION) {\n throw new Error(\n `Plugin requires API version ${apiVersion}, but engine supports ${PLUGIN_API_VERSION}`,\n );\n }\n }\n\n /**\n * Add a plugin programmatically (for testing or dynamic loading)\n */\n addPlugin(plugin: VulcnPlugin, config: Record<string, unknown> = {}): void {\n this.validatePlugin(plugin);\n this.plugins.push({\n plugin,\n config,\n source: \"custom\",\n enabled: true,\n });\n }\n\n /**\n * Initialize all plugins (call onInit hooks)\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Load payloads from plugins that provide them\n for (const loaded of this.plugins) {\n if (loaded.plugin.payloads) {\n const payloads =\n typeof loaded.plugin.payloads === \"function\"\n ? await loaded.plugin.payloads()\n : loaded.plugin.payloads;\n this.sharedPayloads.push(...payloads);\n }\n }\n\n // Call onInit hooks\n await this.callHook(\"onInit\", (hook, ctx) => hook(ctx));\n\n this.initialized = true;\n }\n\n /**\n * Destroy all plugins (call onDestroy hooks)\n */\n async destroy(): Promise<void> {\n await this.callHook(\"onDestroy\", (hook, ctx) => hook(ctx));\n this.plugins = [];\n this.sharedPayloads = [];\n this.sharedFindings = [];\n this.initialized = false;\n }\n\n /**\n * Get all loaded payloads\n */\n getPayloads(): RuntimePayload[] {\n return this.sharedPayloads;\n }\n\n /**\n * Get all collected findings\n */\n getFindings(): Finding[] {\n return this.sharedFindings;\n }\n\n /**\n * Add a finding (used by detectors)\n */\n addFinding(finding: Finding): void {\n this.sharedFindings.push(finding);\n }\n\n /**\n * Add payloads (used by loaders)\n */\n addPayloads(payloads: RuntimePayload[]): void {\n this.sharedPayloads.push(...payloads);\n }\n\n /**\n * Clear findings (for new run)\n */\n clearFindings(): void {\n this.sharedFindings = [];\n }\n\n /**\n * Get loaded plugins\n */\n getPlugins(): LoadedPlugin[] {\n return this.plugins;\n }\n\n /**\n * Check if a plugin is loaded by name\n */\n hasPlugin(name: string): boolean {\n return this.plugins.some((p) => p.plugin.name === name);\n }\n\n /**\n * Create base context for plugins\n */\n createContext(pluginConfig: Record<string, unknown>): PluginContext {\n const engineInfo: EngineInfo = {\n version: ENGINE_VERSION,\n pluginApiVersion: PLUGIN_API_VERSION,\n };\n\n return {\n config: pluginConfig,\n engine: engineInfo,\n payloads: this.sharedPayloads,\n findings: this.sharedFindings,\n logger: this.createLogger(\"plugin\"),\n fetch: globalThis.fetch,\n };\n }\n\n /**\n * Create scoped logger for a plugin\n */\n private createLogger(name: string): PluginLogger {\n const prefix = `[${name}]`;\n return {\n debug: (msg, ...args) => console.debug(prefix, msg, ...args),\n info: (msg, ...args) => console.info(prefix, msg, ...args),\n warn: (msg, ...args) => console.warn(prefix, msg, ...args),\n error: (msg, ...args) => console.error(prefix, msg, ...args),\n };\n }\n\n /**\n * Call a hook on all plugins sequentially\n */\n async callHook<K extends keyof PluginHooks>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<unknown>,\n ): Promise<void> {\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n await executor(hook as NonNullable<PluginHooks[K]>, ctx);\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n }\n\n /**\n * Call a hook and collect results\n */\n async callHookCollect<K extends keyof PluginHooks, R>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<R | R[] | null>,\n ): Promise<R[]> {\n const results: R[] = [];\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n const result = await executor(\n hook as NonNullable<PluginHooks[K]>,\n ctx,\n );\n if (result !== null && result !== undefined) {\n if (Array.isArray(result)) {\n results.push(...result);\n } else {\n results.push(result);\n }\n }\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return results;\n }\n\n /**\n * Call a hook that transforms a value through the pipeline\n */\n async callHookPipe<T>(\n hookName: keyof PluginHooks,\n initial: T,\n executor: (\n hook: NonNullable<PluginHooks[typeof hookName]>,\n value: T,\n ctx: PluginContext,\n ) => Promise<T>,\n ): Promise<T> {\n let value = initial;\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n value = await executor(\n hook as NonNullable<PluginHooks[typeof hookName]>,\n value,\n ctx,\n );\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return value;\n }\n}\n\n/**\n * Default shared plugin manager instance\n */\nexport const pluginManager = new PluginManager();\n","/**\n * Vulcn Plugin System Types\n * @module @vulcn/engine/plugin\n */\n\nimport type {\n Page,\n Dialog,\n ConsoleMessage,\n Request,\n Response,\n} from \"playwright\";\nimport type { z } from \"zod\";\nimport type { Session, Step } from \"./session\";\nimport type { Finding, RunResult, BrowserType } from \"./types\";\nimport type { RuntimePayload, PayloadCategory } from \"./payload-types\";\n\n// Re-export for plugin authors\nexport type {\n Session,\n Step,\n Finding,\n RunResult,\n RuntimePayload,\n PayloadCategory,\n};\n\n/**\n * Plugin API version - plugins declare compatibility\n */\nexport const PLUGIN_API_VERSION = 1;\n\n/**\n * Plugin source types for identification\n */\nexport type PluginSource = \"builtin\" | \"npm\" | \"local\" | \"custom\";\n\n/**\n * Main plugin interface\n */\nexport interface VulcnPlugin {\n /** Unique plugin name (e.g., \"@vulcn/plugin-payloads\") */\n name: string;\n\n /** Plugin version (semver) */\n version: string;\n\n /** Plugin API version this plugin targets */\n apiVersion?: number;\n\n /** Human-readable description */\n description?: string;\n\n /** Lifecycle hooks */\n hooks?: PluginHooks;\n\n /**\n * Payloads provided by this plugin (Loaders)\n * Can be static array or async function for lazy loading\n */\n payloads?: RuntimePayload[] | (() => Promise<RuntimePayload[]>);\n\n /**\n * Zod schema for plugin configuration validation\n */\n configSchema?: z.ZodSchema;\n}\n\n/**\n * Plugin lifecycle hooks\n */\nexport interface PluginHooks {\n // ─────────────────────────────────────────────────────────────────\n // Initialization\n // ─────────────────────────────────────────────────────────────────\n\n /**\n * Called when plugin is loaded, before any operation\n * Use for setup, loading payloads, etc.\n */\n onInit?: (ctx: PluginContext) => Promise<void>;\n\n /**\n * Called when plugin is unloaded/cleanup\n */\n onDestroy?: (ctx: PluginContext) => Promise<void>;\n\n // ─────────────────────────────────────────────────────────────────\n // Recording Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when recording starts */\n onRecordStart?: (ctx: RecordContext) => Promise<void>;\n\n /** Called for each recorded step, can transform */\n onRecordStep?: (step: Step, ctx: RecordContext) => Promise<Step>;\n\n /** Called when recording ends, can transform session */\n onRecordEnd?: (session: Session, ctx: RecordContext) => Promise<Session>;\n\n // ─────────────────────────────────────────────────────────────────\n // Running Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when run starts */\n onRunStart?: (ctx: RunContext) => Promise<void>;\n\n /** Called before each payload is injected, can transform payload */\n onBeforePayload?: (\n payload: string,\n step: Step,\n ctx: RunContext,\n ) => Promise<string>;\n\n /** Called after payload injection, for detection */\n onAfterPayload?: (ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called when run ends, can transform results */\n onRunEnd?: (result: RunResult, ctx: RunContext) => Promise<RunResult>;\n\n // ─────────────────────────────────────────────────────────────────\n // Browser Event Hooks (Detection)\n // ─────────────────────────────────────────────────────────────────\n\n /** Called on page load/navigation */\n onPageLoad?: (page: Page, ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called when JavaScript alert/confirm/prompt appears */\n onDialog?: (dialog: Dialog, ctx: DetectContext) => Promise<Finding | null>;\n\n /** Called on console.log/warn/error */\n onConsoleMessage?: (\n msg: ConsoleMessage,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on network request */\n onNetworkRequest?: (\n request: Request,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on network response */\n onNetworkResponse?: (\n response: Response,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n}\n\n/**\n * Logger interface for plugins\n */\nexport interface PluginLogger {\n debug: (msg: string, ...args: unknown[]) => void;\n info: (msg: string, ...args: unknown[]) => void;\n warn: (msg: string, ...args: unknown[]) => void;\n error: (msg: string, ...args: unknown[]) => void;\n}\n\n/**\n * Engine information exposed to plugins\n */\nexport interface EngineInfo {\n version: string;\n pluginApiVersion: number;\n}\n\n/**\n * Base context available to all plugin hooks\n */\nexport interface PluginContext {\n /** Plugin-specific config from vulcn.config.yml */\n config: Record<string, unknown>;\n\n /** Engine information */\n engine: EngineInfo;\n\n /** Shared payload registry - loaders add payloads here */\n payloads: RuntimePayload[];\n\n /** Shared findings collection - detectors add findings here */\n findings: Finding[];\n\n /** Scoped logger */\n logger: PluginLogger;\n\n /** Fetch API for network requests */\n fetch: typeof fetch;\n}\n\n/**\n * Context for recording phase hooks\n */\nexport interface RecordContext extends PluginContext {\n /** Starting URL */\n startUrl: string;\n\n /** Browser type being used */\n browser: BrowserType;\n\n /** Playwright page instance */\n page: Page;\n}\n\n/**\n * Context for running phase hooks\n */\nexport interface RunContext extends PluginContext {\n /** Session being executed */\n session: Session;\n\n /** Playwright page instance */\n page: Page;\n\n /** Browser type being used */\n browser: BrowserType;\n\n /** Whether running headless */\n headless: boolean;\n}\n\n/**\n * Context for detection hooks\n */\nexport interface DetectContext extends RunContext {\n /** Current step being tested */\n step: Step;\n\n /** Current payload set being tested */\n payloadSet: RuntimePayload;\n\n /** Actual payload value injected */\n payloadValue: string;\n\n /** Step ID for reporting */\n stepId: string;\n}\n\n/**\n * Plugin configuration in vulcn.config.yml\n */\nexport interface PluginConfig {\n /** Plugin name/path */\n name: string;\n\n /** Plugin-specific configuration */\n config?: Record<string, unknown>;\n\n /** Whether plugin is enabled (default: true) */\n enabled?: boolean;\n}\n\n/**\n * Vulcn configuration file schema\n */\nexport interface VulcnConfig {\n /** Config version */\n version: string;\n\n /** Plugins to load */\n plugins?: PluginConfig[];\n\n /** Global settings */\n settings?: {\n browser?: BrowserType;\n headless?: boolean;\n timeout?: number;\n };\n}\n\n/**\n * Loaded plugin instance with resolved config\n */\nexport interface LoadedPlugin {\n /** Plugin definition */\n plugin: VulcnPlugin;\n\n /** Resolved configuration */\n config: Record<string, unknown>;\n\n /** Source of the plugin */\n source: PluginSource;\n\n /** Whether plugin is enabled */\n enabled: boolean;\n}\n","/**\n * Recorder - captures browser interactions as a replayable session\n * v0.2.0: Plugin hooks for recording customization\n */\n\nimport { Page } from \"playwright\";\nimport { createSession, type Session, type Step } from \"./session\";\nimport { launchBrowser } from \"./browser\";\nimport type { RecorderOptions } from \"./types\";\nimport { PluginManager, pluginManager } from \"./plugin-manager\";\nimport type { RecordContext } from \"./plugin-types\";\n\n/**\n * Configuration for the recorder\n */\nexport interface RecorderConfig {\n /** Plugin manager to use (defaults to shared instance) */\n pluginManager?: PluginManager;\n}\n\n/**\n * Active recording session handle\n */\nexport interface RecordingSession {\n /** Stop recording and return the session */\n stop(): Promise<Session>;\n /** Get current recorded steps */\n getSteps(): Step[];\n /** Get the Playwright page (for advanced use) */\n getPage(): Page;\n}\n\n/**\n * Recorder - captures browser interactions as a replayable session\n *\n * Uses plugin hooks for:\n * - onRecordStart: Called when recording starts\n * - onRecordStep: Called for each step, can transform\n * - onRecordEnd: Called when recording ends, can transform session\n */\nexport class Recorder {\n /**\n * Start a new recording session\n * Opens a browser window for the user to interact with\n */\n static async start(\n startUrl: string,\n options: RecorderOptions = {},\n config: RecorderConfig = {},\n ): Promise<RecordingSession> {\n const manager = config.pluginManager ?? pluginManager;\n const browserType = options.browser ?? \"chromium\";\n const viewport = options.viewport ?? { width: 1280, height: 720 };\n const headless = options.headless ?? false;\n\n // Initialize plugins if not already done\n await manager.initialize();\n\n // Launch browser with smart fallback (system Chrome first)\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport });\n const page = await context.newPage();\n\n // Navigate to start URL\n await page.goto(startUrl);\n\n // Create session\n const session = createSession({\n name: `Recording ${new Date().toISOString()}`,\n startUrl,\n browser: browserType,\n viewport,\n });\n\n // Track recording start time\n const startTime = Date.now();\n const steps: Step[] = [];\n let stepCounter = 0;\n\n const generateStepId = () => {\n stepCounter++;\n return `step_${String(stepCounter).padStart(3, \"0\")}`;\n };\n\n // Create base record context\n const baseRecordContext: Omit<RecordContext, \"config\"> = {\n startUrl,\n browser: browserType,\n page,\n engine: { version: \"0.2.0\", pluginApiVersion: 1 },\n payloads: manager.getPayloads(),\n findings: manager.getFindings(),\n logger: {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n },\n fetch: globalThis.fetch,\n };\n\n // Call onRecordStart hooks\n await manager.callHook(\"onRecordStart\", async (hook, ctx) => {\n const recordCtx: RecordContext = { ...baseRecordContext, ...ctx };\n await hook(recordCtx);\n });\n\n // Add initial navigation step\n const initialStep: Step = {\n id: generateStepId(),\n type: \"navigate\",\n url: startUrl,\n timestamp: 0,\n };\n\n // Transform through plugins\n const transformedInitialStep = await Recorder.transformStep(\n initialStep,\n manager,\n baseRecordContext,\n );\n if (transformedInitialStep) {\n steps.push(transformedInitialStep);\n }\n\n // Attach event listeners with step transformation\n Recorder.attachListeners(\n page,\n steps,\n startTime,\n generateStepId,\n manager,\n baseRecordContext,\n );\n\n return {\n async stop() {\n // Call onRecordEnd hooks to transform session\n session.steps = steps;\n let finalSession = session;\n\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRecordEnd;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const recordCtx: RecordContext = { ...baseRecordContext, ...ctx };\n finalSession = await hook(finalSession, recordCtx);\n }\n }\n\n await browser.close();\n return finalSession;\n },\n getSteps() {\n return [...steps];\n },\n getPage() {\n return page;\n },\n };\n }\n\n /**\n * Transform a step through plugin hooks\n * Returns null if the step should be filtered out\n */\n private static async transformStep(\n step: Step,\n manager: PluginManager,\n baseContext: Omit<RecordContext, \"config\">,\n ): Promise<Step | null> {\n let transformedStep = step;\n\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRecordStep;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const recordCtx: RecordContext = { ...baseContext, ...ctx };\n transformedStep = await hook(transformedStep, recordCtx);\n }\n }\n\n return transformedStep;\n }\n\n private static attachListeners(\n page: Page,\n steps: Step[],\n startTime: number,\n generateStepId: () => string,\n manager: PluginManager,\n baseContext: Omit<RecordContext, \"config\">,\n ) {\n const getTimestamp = () => Date.now() - startTime;\n\n // Helper to add step with plugin transformation\n const addStep = async (step: Step) => {\n const transformed = await Recorder.transformStep(\n step,\n manager,\n baseContext,\n );\n if (transformed) {\n steps.push(transformed);\n }\n };\n\n // Track navigation\n page.on(\"framenavigated\", (frame) => {\n if (frame === page.mainFrame()) {\n const url = frame.url();\n // Avoid duplicate nav steps for initial load\n const lastStep = steps[steps.length - 1];\n if (\n steps.length > 0 &&\n lastStep.type === \"navigate\" &&\n lastStep.url === url\n ) {\n return;\n }\n addStep({\n id: generateStepId(),\n type: \"navigate\",\n url,\n timestamp: getTimestamp(),\n });\n }\n });\n\n // Expose recording function to browser\n page.exposeFunction(\n \"__vulcn_record\",\n async (event: { type: string; data: Record<string, unknown> }) => {\n const timestamp = getTimestamp();\n\n switch (event.type) {\n case \"click\": {\n const data = event.data as {\n selector: string;\n x: number;\n y: number;\n };\n await addStep({\n id: generateStepId(),\n type: \"click\",\n selector: data.selector,\n position: { x: data.x, y: data.y },\n timestamp,\n });\n break;\n }\n case \"input\": {\n const data = event.data as {\n selector: string;\n value: string;\n inputType: string | null;\n injectable: boolean;\n };\n await addStep({\n id: generateStepId(),\n type: \"input\",\n selector: data.selector,\n value: data.value,\n injectable: data.injectable,\n timestamp,\n });\n break;\n }\n case \"keypress\": {\n const data = event.data as { key: string; modifiers?: string[] };\n await addStep({\n id: generateStepId(),\n type: \"keypress\",\n key: data.key,\n modifiers: data.modifiers,\n timestamp,\n });\n break;\n }\n }\n },\n );\n\n // Inject recording script into every frame\n page.on(\"load\", async () => {\n await Recorder.injectRecordingScript(page);\n });\n\n // Inject into initial page\n Recorder.injectRecordingScript(page);\n }\n\n private static async injectRecordingScript(page: Page) {\n await page.evaluate(`\n (function() {\n if (window.__vulcn_injected) return;\n window.__vulcn_injected = true;\n\n var textInputTypes = ['text', 'password', 'email', 'search', 'url', 'tel', 'number'];\n\n function getSelector(el) {\n if (el.id) {\n return '#' + CSS.escape(el.id);\n }\n if (el.name) {\n var tag = el.tagName.toLowerCase();\n var nameSelector = tag + '[name=\"' + el.name + '\"]';\n if (document.querySelectorAll(nameSelector).length === 1) {\n return nameSelector;\n }\n }\n if (el.dataset && el.dataset.testid) {\n return '[data-testid=\"' + el.dataset.testid + '\"]';\n }\n if (el.tagName === 'INPUT' && el.type && el.name) {\n var inputSelector = 'input[type=\"' + el.type + '\"][name=\"' + el.name + '\"]';\n if (document.querySelectorAll(inputSelector).length === 1) {\n return inputSelector;\n }\n }\n if (el.className && typeof el.className === 'string') {\n var classes = el.className.trim().split(/\\\\s+/).filter(function(c) { return c.length > 0; });\n if (classes.length > 0) {\n var classSelector = el.tagName.toLowerCase() + '.' + classes.map(function(c) { return CSS.escape(c); }).join('.');\n if (document.querySelectorAll(classSelector).length === 1) {\n return classSelector;\n }\n }\n }\n var path = [];\n var current = el;\n while (current && current !== document.body) {\n var tag = current.tagName.toLowerCase();\n var parent = current.parentElement;\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName; });\n if (siblings.length > 1) {\n var index = siblings.indexOf(current) + 1;\n tag = tag + ':nth-of-type(' + index + ')';\n }\n }\n path.unshift(tag);\n current = parent;\n }\n return path.join(' > ');\n }\n\n function getInputType(el) {\n if (el.tagName === 'INPUT') return el.type || 'text';\n if (el.tagName === 'TEXTAREA') return 'textarea';\n if (el.tagName === 'SELECT') return 'select';\n return null;\n }\n\n function isTextInjectable(el) {\n var inputType = getInputType(el);\n if (!inputType) return false;\n if (inputType === 'textarea') return true;\n if (inputType === 'select') return false;\n return textInputTypes.indexOf(inputType) !== -1;\n }\n\n document.addEventListener('click', function(e) {\n var target = e.target;\n window.__vulcn_record({\n type: 'click',\n data: {\n selector: getSelector(target),\n x: e.clientX,\n y: e.clientY\n }\n });\n }, true);\n\n document.addEventListener('change', function(e) {\n var target = e.target;\n if ('value' in target) {\n var inputType = getInputType(target);\n window.__vulcn_record({\n type: 'input',\n data: {\n selector: getSelector(target),\n value: target.value,\n inputType: inputType,\n injectable: isTextInjectable(target)\n }\n });\n }\n }, true);\n\n document.addEventListener('keydown', function(e) {\n if (e.ctrlKey || e.metaKey || e.altKey) {\n var modifiers = [];\n if (e.ctrlKey) modifiers.push('ctrl');\n if (e.metaKey) modifiers.push('meta');\n if (e.altKey) modifiers.push('alt');\n if (e.shiftKey) modifiers.push('shift');\n\n window.__vulcn_record({\n type: 'keypress',\n data: {\n key: e.key,\n modifiers: modifiers\n }\n });\n }\n }, true);\n })();\n `);\n }\n}\n","/**\n * Runner - replays sessions with security payloads\n * v0.2.0: Plugin-based architecture for extensibility\n */\n\nimport type { Page, Dialog, ConsoleMessage } from \"playwright\";\nimport { launchBrowser } from \"./browser\";\nimport type { RuntimePayload, PayloadCategory } from \"./payload-types\";\nimport type { Session, Step } from \"./session\";\nimport type { Finding, RunResult, RunnerOptions } from \"./types\";\nimport { PluginManager, pluginManager } from \"./plugin-manager\";\nimport type { DetectContext, RunContext } from \"./plugin-types\";\n\nexport interface RunnerConfig {\n /** Plugin manager to use (defaults to shared instance) */\n pluginManager?: PluginManager;\n}\n\n/**\n * Runner - replays sessions with security payloads\n *\n * Uses plugin hooks for:\n * - Payload loading (onInit)\n * - Payload transformation (onBeforePayload)\n * - Vulnerability detection (onAfterPayload, onDialog, onConsoleMessage, etc.)\n * - Results processing (onRunEnd)\n */\nexport class Runner {\n /**\n * Execute a session with security payloads from plugins\n *\n * @param session - The recorded session to replay\n * @param options - Runner configuration\n * @param config - Plugin manager configuration\n */\n static async execute(\n session: Session,\n options: RunnerOptions = {},\n config: RunnerConfig = {},\n ): Promise<RunResult> {\n const manager = config.pluginManager ?? pluginManager;\n const browserType = options.browser ?? session.browser ?? \"chromium\";\n const headless = options.headless ?? true;\n const startTime = Date.now();\n\n const errors: string[] = [];\n let payloadsTested = 0;\n\n // Initialize plugins and load payloads\n await manager.initialize();\n manager.clearFindings();\n\n const payloads = manager.getPayloads();\n if (payloads.length === 0) {\n return {\n findings: [],\n stepsExecuted: session.steps.length,\n payloadsTested: 0,\n duration: Date.now() - startTime,\n errors: [\n \"No payloads loaded. Add a payload plugin or configure payloads.\",\n ],\n };\n }\n\n // Launch browser\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport: session.viewport });\n const page = await context.newPage();\n\n // Create base run context\n const baseRunContext: Omit<RunContext, \"config\"> = {\n session,\n page,\n browser: browserType,\n headless,\n engine: { version: \"0.2.0\", pluginApiVersion: 1 },\n payloads: manager.getPayloads(),\n findings: manager.getFindings(),\n logger: {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n },\n fetch: globalThis.fetch,\n };\n\n // Call onRunStart hooks\n await manager.callHook(\"onRunStart\", async (hook, ctx) => {\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n await hook(runCtx);\n });\n\n // Set up browser event listeners for detection\n const eventFindings: Finding[] = [];\n let currentDetectContext: DetectContext | null = null;\n\n // Dialog handler (for alert-based XSS detection)\n const dialogHandler = async (dialog: Dialog) => {\n if (currentDetectContext) {\n const findings = await manager.callHookCollect<\"onDialog\", Finding>(\n \"onDialog\",\n async (hook, ctx) => {\n const detectCtx: DetectContext = {\n ...currentDetectContext!,\n ...ctx,\n };\n return hook(dialog, detectCtx);\n },\n );\n eventFindings.push(...findings);\n }\n // Always dismiss dialogs to prevent blocking\n try {\n await dialog.dismiss();\n } catch {\n // Dialog may have already been handled\n }\n };\n\n // Console message handler (for console-based XSS detection)\n const consoleHandler = async (msg: ConsoleMessage) => {\n if (currentDetectContext) {\n const findings = await manager.callHookCollect<\n \"onConsoleMessage\",\n Finding\n >(\"onConsoleMessage\", async (hook, ctx) => {\n const detectCtx: DetectContext = { ...currentDetectContext!, ...ctx };\n return hook(msg, detectCtx);\n });\n eventFindings.push(...findings);\n }\n };\n\n page.on(\"dialog\", dialogHandler);\n page.on(\"console\", consoleHandler);\n\n try {\n // Find injectable steps\n const injectableSteps = session.steps.filter(\n (step): step is Step & { type: \"input\" } =>\n step.type === \"input\" && step.injectable !== false,\n );\n\n // Build flat list of all individual payloads to test\n const allPayloads: { payloadSet: RuntimePayload; value: string }[] = [];\n for (const payloadSet of payloads) {\n for (const value of payloadSet.payloads) {\n allPayloads.push({ payloadSet, value });\n }\n }\n\n // For each injectable step, test with each payload\n for (const injectableStep of injectableSteps) {\n for (const { payloadSet, value: originalValue } of allPayloads) {\n try {\n // Transform payload through plugins\n let transformedPayload = originalValue;\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onBeforePayload;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n transformedPayload = await hook(\n transformedPayload,\n injectableStep,\n runCtx,\n );\n }\n }\n\n // Create detect context for this payload\n currentDetectContext = {\n ...baseRunContext,\n config: {},\n step: injectableStep,\n payloadSet,\n payloadValue: transformedPayload,\n stepId: injectableStep.id,\n };\n\n // Replay session with payload\n await Runner.replayWithPayload(\n page,\n session,\n injectableStep,\n transformedPayload,\n );\n\n // Call onAfterPayload hooks for detection\n const afterFindings = await manager.callHookCollect<\n \"onAfterPayload\",\n Finding\n >(\"onAfterPayload\", async (hook, ctx) => {\n const detectCtx: DetectContext = {\n ...currentDetectContext!,\n ...ctx,\n };\n return hook(detectCtx);\n });\n\n // Also do basic reflection check (built-in fallback)\n const reflectionFinding = await Runner.checkReflection(\n page,\n injectableStep,\n payloadSet,\n transformedPayload,\n );\n\n // Collect all findings\n const allFindings = [...afterFindings, ...eventFindings];\n if (reflectionFinding) {\n allFindings.push(reflectionFinding);\n }\n\n // Add unique findings\n for (const finding of allFindings) {\n manager.addFinding(finding);\n options.onFinding?.(finding);\n }\n\n // Clear event findings for next iteration\n eventFindings.length = 0;\n payloadsTested++;\n } catch (err) {\n errors.push(`${injectableStep.id}: ${String(err)}`);\n }\n }\n }\n } finally {\n // Remove listeners\n page.off(\"dialog\", dialogHandler);\n page.off(\"console\", consoleHandler);\n\n currentDetectContext = null;\n await browser.close();\n }\n\n // Create result\n let result: RunResult = {\n findings: manager.getFindings(),\n stepsExecuted: session.steps.length,\n payloadsTested,\n duration: Date.now() - startTime,\n errors,\n };\n\n // Transform result through plugins\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRunEnd;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n result = await hook(result, runCtx);\n }\n }\n\n return result;\n }\n\n /**\n * Execute with explicit payloads (legacy API, for backwards compatibility)\n */\n static async executeWithPayloads(\n session: Session,\n payloads: RuntimePayload[],\n options: RunnerOptions = {},\n ): Promise<RunResult> {\n // Create a temporary plugin manager with the provided payloads\n const manager = new PluginManager();\n manager.addPayloads(payloads);\n\n return Runner.execute(session, options, { pluginManager: manager });\n }\n\n /**\n * Replay session steps with payload injected at target step\n */\n private static async replayWithPayload(\n page: Page,\n session: Session,\n targetStep: Step & { type: \"input\" },\n payloadValue: string,\n ): Promise<void> {\n // Navigate to start\n await page.goto(session.startUrl, { waitUntil: \"domcontentloaded\" });\n\n // Replay steps\n for (const step of session.steps) {\n try {\n if (step.type === \"navigate\") {\n await page.goto(step.url, { waitUntil: \"domcontentloaded\" });\n } else if (step.type === \"click\") {\n await page.click(step.selector, { timeout: 5000 });\n } else if (step.type === \"input\") {\n // Inject payload for target step\n const value = step.id === targetStep.id ? payloadValue : step.value;\n await page.fill(step.selector, value, { timeout: 5000 });\n } else if (step.type === \"keypress\") {\n const modifiers = step.modifiers ?? [];\n for (const mod of modifiers) {\n await page.keyboard.down(\n mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\",\n );\n }\n await page.keyboard.press(step.key);\n for (const mod of modifiers.reverse()) {\n await page.keyboard.up(mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\");\n }\n }\n } catch {\n // Step failed, continue to next\n }\n\n // Stop after target step is injected (we can check sooner)\n if (step.id === targetStep.id) {\n // Wait a bit for any scripts to execute\n await page.waitForTimeout(100);\n break;\n }\n }\n }\n\n /**\n * Basic reflection check - fallback when no detection plugin is loaded\n */\n private static async checkReflection(\n page: Page,\n step: Step & { type: \"input\" },\n payloadSet: RuntimePayload,\n payloadValue: string,\n ): Promise<Finding | undefined> {\n // Get page content\n const content = await page.content();\n\n // Check for reflection patterns\n for (const pattern of payloadSet.detectPatterns) {\n if (pattern.test(content)) {\n return {\n type: payloadSet.category,\n severity: Runner.getSeverity(payloadSet.category),\n title: `${payloadSet.category.toUpperCase()} vulnerability detected`,\n description: `Payload pattern was reflected in page content`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n evidence: content.match(pattern)?.[0]?.slice(0, 200),\n };\n }\n }\n\n // Check if payload appears verbatim (potential XSS)\n if (content.includes(payloadValue)) {\n return {\n type: payloadSet.category,\n severity: \"medium\",\n title: `Potential ${payloadSet.category.toUpperCase()} - payload reflection`,\n description: `Payload was reflected in page without encoding`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n };\n }\n\n return undefined;\n }\n\n /**\n * Determine severity based on vulnerability category\n */\n private static getSeverity(\n category: PayloadCategory,\n ): \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\" {\n switch (category) {\n case \"sqli\":\n case \"command-injection\":\n case \"xxe\":\n return \"critical\";\n case \"xss\":\n case \"ssrf\":\n case \"path-traversal\":\n return \"high\";\n case \"open-redirect\":\n return \"medium\";\n default:\n return \"medium\";\n }\n }\n}\n"],"mappings":"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","names":["import_yaml","import_zod","YAML","module"]}