xploitscan 1.1.7 → 1.1.8
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.
- package/dist/index.js +20 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2056,15 +2056,26 @@ ${isMonthlyCap ? "Monthly" : "Daily"} scan limit reached.`));
|
|
|
2056
2056
|
}
|
|
2057
2057
|
if (options.diff) {
|
|
2058
2058
|
const base = typeof options.diff === "string" ? options.diff : "main";
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2059
|
+
if (base.startsWith("-") || !/^[A-Za-z0-9._/\-~^]+$/.test(base)) {
|
|
2060
|
+
spinner.warn(`Invalid --diff base "${base}" \u2014 scanning all files`);
|
|
2061
|
+
} else {
|
|
2062
|
+
try {
|
|
2063
|
+
const { execFile: execFile4 } = await import("child_process");
|
|
2064
|
+
const { promisify } = await import("util");
|
|
2065
|
+
const execFileAsync = promisify(execFile4);
|
|
2066
|
+
const { stdout } = await execFileAsync(
|
|
2067
|
+
"git",
|
|
2068
|
+
["diff", "--name-only", base],
|
|
2069
|
+
{ cwd: dir, encoding: "utf-8" }
|
|
2070
|
+
);
|
|
2071
|
+
const changedFiles = stdout.trim().split("\n").filter(Boolean);
|
|
2072
|
+
files = files.filter((f) => changedFiles.some((cf) => f.endsWith(cf) || cf.endsWith(f)));
|
|
2073
|
+
if (!isSilent) {
|
|
2074
|
+
spinner.info(chalk2.gray(` Diff mode: scanning ${files.length} changed files vs ${base}`));
|
|
2075
|
+
}
|
|
2076
|
+
} catch {
|
|
2077
|
+
spinner.warn("Could not run git diff \u2014 scanning all files");
|
|
2065
2078
|
}
|
|
2066
|
-
} catch {
|
|
2067
|
-
spinner.warn("Could not run git diff \u2014 scanning all files");
|
|
2068
2079
|
}
|
|
2069
2080
|
}
|
|
2070
2081
|
if (files.length === 0) {
|
|
@@ -2685,7 +2696,7 @@ async function cursorInstallCommand(opts = {}) {
|
|
|
2685
2696
|
var program = new Command();
|
|
2686
2697
|
program.name("xploitscan").description(
|
|
2687
2698
|
"AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do."
|
|
2688
|
-
).version("1.1.
|
|
2699
|
+
).version("1.1.8");
|
|
2689
2700
|
program.command("scan").description("Scan a directory for security vulnerabilities").argument("[directory]", "Directory to scan", ".").option("--no-ai", "Skip AI-powered analysis").option(
|
|
2690
2701
|
"-f, --format <format>",
|
|
2691
2702
|
"Output format: terminal, json, sarif, splunk-hec, elastic-ecs, datadog-logs",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/scan.ts","../src/utils/files.ts","../src/utils/config.ts","../src/scanners/semgrep.ts","../src/scanners/gitleaks.ts","../src/scanners/ai-analyzer.ts","../src/scanners/ast-analyzer.ts","../src/scanners/osv.ts","../src/scanners/dependency-scanner.ts","../src/scanners/config-analyzer.ts","../src/scanners/multi-file-analyzer.ts","../src/reporters/terminal.ts","../src/reporters/json.ts","../src/reporters/sarif.ts","../src/reporters/siem.ts","../src/commands/auth.ts","../src/commands/hook.ts","../src/commands/cursor.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { loginCommand, logoutCommand, whoamiCommand } from \"./commands/auth.js\";\nimport { installHookCommand, uninstallHookCommand } from \"./commands/hook.js\";\nimport { cursorInstallCommand } from \"./commands/cursor.js\";\n\n// Injected at build time by tsup's `define` (see tsup.config.ts). Reads the\n// current version from packages/cli/package.json so `xploitscan --version`\n// always matches the published tarball, without a runtime fs read.\ndeclare const __XPLOITSCAN_VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name(\"xploitscan\")\n .description(\n \"AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do.\",\n )\n .version(__XPLOITSCAN_VERSION__);\n\nprogram\n .command(\"scan\")\n .description(\"Scan a directory for security vulnerabilities\")\n .argument(\"[directory]\", \"Directory to scan\", \".\")\n .option(\"--no-ai\", \"Skip AI-powered analysis\")\n .option(\n \"-f, --format <format>\",\n \"Output format: terminal, json, sarif, splunk-hec, elastic-ecs, datadog-logs\",\n \"terminal\",\n )\n .option(\"-v, --verbose\", \"Show detailed output\", false)\n .option(\"--diff [base]\", \"Scan only files changed vs base branch (default: main)\")\n .option(\"-w, --watch\", \"Watch for file changes and re-scan automatically\", false)\n .action(async (directory: string, opts: { ai: boolean; format: string; verbose: boolean; diff?: string | boolean; watch: boolean }) => {\n await scanCommand(directory, {\n directory,\n aiAnalysis: opts.ai,\n format: opts.format as \"terminal\" | \"json\" | \"sarif\",\n verbose: opts.verbose,\n diff: opts.diff,\n watch: opts.watch,\n });\n });\n\n// Auth commands\nconst auth = program\n .command(\"auth\")\n .description(\"Manage authentication\");\n\nauth\n .command(\"login\")\n .description(\"Log in to your XploitScan account\")\n .action(loginCommand);\n\nauth\n .command(\"logout\")\n .description(\"Log out of your XploitScan account\")\n .action(logoutCommand);\n\nauth\n .command(\"whoami\")\n .description(\"Show current logged-in user\")\n .action(whoamiCommand);\n\n// Hook commands\nconst hook = program\n .command(\"hook\")\n .description(\"Manage git pre-commit hooks for automatic scanning\");\n\nhook\n .command(\"install\")\n .description(\"Install a git pre-commit hook that runs XploitScan on every commit\")\n .option(\"-f, --force\", \"Overwrite existing XploitScan hook without prompting\", false)\n .action(async (opts: { force?: boolean }) => {\n await installHookCommand({ force: opts.force });\n });\n\nhook\n .command(\"uninstall\")\n .description(\"Remove the XploitScan pre-commit hook\")\n .action(uninstallHookCommand);\n\n// Cursor integration\nconst cursor = program\n .command(\"cursor\")\n .description(\"Manage XploitScan integration with Cursor IDE\");\n\ncursor\n .command(\"install\")\n .description(\"Drop XploitScan security rules into .cursor/rules and .cursorrules so Cursor enforces them at write-time\")\n .option(\"-f, --force\", \"Overwrite existing rule files\", false)\n .option(\"--legacy-only\", \"Only install the legacy .cursorrules file (skip .cursor/rules/*.mdc)\", false)\n .action(async (opts: { force?: boolean; legacyOnly?: boolean }) => {\n await cursorInstallCommand({ force: opts.force, legacyOnly: opts.legacyOnly });\n });\n\n// Upgrade command (shortcut)\nprogram\n .command(\"upgrade\")\n .description(\"Upgrade to XploitScan Pro for unlimited scans\")\n .action(async () => {\n const { getStoredToken, getCheckoutUrl } = await import(\"./utils/api.js\");\n const chalk = (await import(\"chalk\")).default;\n\n const token = getStoredToken();\n if (!token) {\n console.log(chalk.yellow(\"Please log in first: xploitscan auth login\"));\n return;\n }\n\n console.log(chalk.cyan(\"Creating checkout session...\"));\n const url = await getCheckoutUrl();\n if (url) {\n console.log(chalk.green(`\\nOpen this URL to upgrade:`));\n console.log(chalk.bold.underline(url));\n const { execFile } = await import(\"node:child_process\");\n const openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start\" : \"xdg-open\";\n execFile(openCmd, [url], () => {});\n } else {\n console.log(chalk.red(\"Failed to create checkout session. Please try again.\"));\n }\n });\n\nprogram.parse();\n","import { resolve, join, relative } from \"node:path\";\nimport { watch as fsWatch } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport type { Finding, ScanOptions, ScanResult } from \"../types.js\";\nimport { collectFiles, readFileContents } from \"../utils/files.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { runCustomRules } from \"../scanners/custom-rules.js\";\nimport { runSemgrep } from \"../scanners/semgrep.js\";\nimport { runGitleaks } from \"../scanners/gitleaks.js\";\nimport { analyzeWithAI } from \"../scanners/ai-analyzer.js\";\nimport { filterFalsePositives } from \"xploitscan-shared-rules\";\nimport { buildASTContext } from \"../scanners/ast-analyzer.js\";\nimport { scanDependencies, scanDependenciesOsv } from \"../scanners/dependency-scanner.js\";\nimport { scanEntropy } from \"xploitscan-shared-rules\";\nimport { scanConfigs } from \"../scanners/config-analyzer.js\";\nimport { scanMultiFile } from \"../scanners/multi-file-analyzer.js\";\nimport { renderTerminalReport } from \"../reporters/terminal.js\";\nimport { renderJsonReport } from \"../reporters/json.js\";\nimport { renderSarifReport } from \"../reporters/sarif.js\";\nimport {\n renderSplunkReport,\n renderElasticReport,\n renderDatadogReport,\n} from \"../reporters/siem.js\";\nimport { checkUsage, incrementUsage, uploadScanResults, isAuthenticated, loadCachedProRules, downloadProRulesBundle } from \"../utils/api.js\";\nimport type { CustomRule } from \"../types.js\";\n\nexport async function scanCommand(\n directory: string,\n options: Partial<ScanOptions>,\n): Promise<void> {\n const dir = resolve(directory || \".\");\n const format = options.format ?? \"terminal\";\n const verbose = options.verbose ?? false;\n const startTime = Date.now();\n\n // Load config\n const config = await loadConfig(dir);\n const useAI = (options.aiAnalysis ?? config.ai ?? true) && !!process.env.ANTHROPIC_API_KEY;\n\n const isSilent = format !== \"terminal\";\n\n // Step 0: Check usage limits and determine rule tier.\n //\n // The server collapses Indie, Pro, Team, and Trial all into plan: \"pro\"\n // for this endpoint — the CLI only cares whether to download the full\n // 206-rule bundle (yes for all paid tiers). The underlying scan cap\n // surfaces via `limit` and `remaining`:\n // -1 / -1 → unlimited (Pro/Team/Trial)\n // 500 / N → Indie (monthly cap), server also sets tier: \"indie\"\n // 5 / N → Free (daily cap)\n let tier: \"free\" | \"pro\" = \"free\";\n let userPlan = \"anonymous\";\n\n if (isAuthenticated()) {\n const usage = await checkUsage();\n userPlan = usage.plan;\n if (usage.plan === \"pro\") {\n tier = \"pro\";\n }\n if (!usage.allowed) {\n // Distinguish the Indie (monthly) cap from the Free (daily) cap so\n // the message points users at the right mental model.\n const isMonthlyCap = (usage as { tier?: string }).tier === \"indie\";\n console.log(chalk.red(`\\n${isMonthlyCap ? \"Monthly\" : \"Daily\"} scan limit reached.`));\n console.log(chalk.yellow(\"Upgrade to Pro for unlimited scans: \") + chalk.bold(\"xploitscan upgrade\"));\n console.log(chalk.gray(`Resets ${isMonthlyCap ? \"next month\" : \"tomorrow\"}. Plan: ${(usage as { tier?: string }).tier || usage.plan}\\n`));\n process.exitCode = 1;\n return;\n }\n if (usage.plan === \"free\" && usage.remaining > 0 && usage.remaining <= 2 && !isSilent) {\n console.log(chalk.gray(` ${usage.remaining} free scan${usage.remaining === 1 ? \"\" : \"s\"} remaining today\\n`));\n }\n // Indie — warn when the monthly budget gets thin.\n if ((usage as { tier?: string }).tier === \"indie\" && usage.remaining > 0 && usage.remaining <= 25 && !isSilent) {\n console.log(chalk.gray(` ${usage.remaining} Indie scan${usage.remaining === 1 ? \"\" : \"s\"} remaining this month\\n`));\n }\n }\n\n // Step 0b: Load Pro rules from cache or server\n let proRulesExtra: CustomRule[] = [];\n if (tier === \"pro\") {\n const cached = loadCachedProRules() as CustomRule[] | null;\n if (cached) {\n proRulesExtra = cached;\n } else {\n if (!isSilent) console.log(chalk.gray(\" Downloading Pro rules...\"));\n const downloaded = await downloadProRulesBundle();\n if (downloaded) {\n proRulesExtra = (loadCachedProRules() as CustomRule[] | null) || [];\n }\n if (proRulesExtra.length === 0 && !isSilent) {\n console.log(chalk.yellow(\" Could not load Pro rules — scanning with free rules only\"));\n }\n }\n }\n\n // Step 1: Collect files\n const spinner = ora({\n text: \"Scanning files...\",\n color: \"cyan\",\n isSilent,\n }).start();\n\n let files: string[];\n try {\n files = await collectFiles(dir);\n } catch (error) {\n spinner.fail(\"Failed to scan directory\");\n console.error(chalk.red(`Error: ${error instanceof Error ? error.message : error}`));\n process.exit(1);\n }\n\n // Diff mode: filter to only changed files\n if (options.diff) {\n const base = typeof options.diff === \"string\" ? options.diff : \"main\";\n try {\n const { execSync } = await import(\"node:child_process\");\n const changedFiles = execSync(`git diff --name-only ${base}`, { cwd: dir, encoding: \"utf-8\" })\n .trim().split(\"\\n\").filter(Boolean);\n files = files.filter(f => changedFiles.some(cf => f.endsWith(cf) || cf.endsWith(f)));\n if (!isSilent) {\n spinner.info(chalk.gray(` Diff mode: scanning ${files.length} changed files vs ${base}`));\n }\n } catch {\n spinner.warn(\"Could not run git diff — scanning all files\");\n }\n }\n\n if (files.length === 0) {\n spinner.warn(\"No source files found in this directory\");\n return;\n }\n\n spinner.text = `Found ${files.length} files. Running security rules...`;\n\n // Step 2: Run all static scanners in parallel\n const allFindings: Finding[] = [];\n\n // 2a: Custom rules with AST context (always runs, instant)\n const fileContentsForAnalysis: { path: string; content: string }[] = [];\n for (const filePath of files) {\n const content = readFileContents(dir, filePath);\n if (!content) continue;\n fileContentsForAnalysis.push({ path: filePath, content });\n\n // Build AST context for false positive reduction\n const astCtx = buildASTContext(content, filePath);\n\n // Skip scanner files scanning themselves\n if (astCtx.isScannerFile) continue;\n\n // Run custom rules with AST-stripped content for comment-heavy files\n const findings = runCustomRules(content, filePath, config.disableRules, tier, proRulesExtra);\n\n // Add confidence scores based on AST context\n for (const f of findings) {\n if (astCtx.isTestFile) {\n f.confidence = \"low\";\n } else if (astCtx.isConfigFile) {\n f.confidence = \"medium\";\n } else {\n f.confidence = \"high\";\n }\n }\n\n allFindings.push(...findings);\n }\n\n const customCount = allFindings.length;\n if (verbose && customCount > 0) {\n spinner.info(`Custom rules found ${customCount} issues`);\n }\n\n // 2b: Dependency vulnerability scanning (static allowlist + live OSV.dev)\n spinner.text = \"Scanning dependencies...\";\n const depFindings = scanDependencies(fileContentsForAnalysis);\n for (const f of depFindings) { f.confidence = \"high\"; }\n allFindings.push(...depFindings);\n\n // Track which (package, CVE) pairs the static scanner already caught so the\n // OSV pass doesn't double-report\n const dedupeKeys = new Set<string>();\n for (const f of depFindings) {\n // Static findings use rule = CVE id. Extract the package name from the title.\n const match = f.title.match(/^(\\S+)/);\n if (match) dedupeKeys.add(`${match[1].toLowerCase()}:${f.rule}`);\n }\n\n spinner.text = \"Checking dependencies against OSV.dev...\";\n try {\n const osvFindings = await scanDependenciesOsv(fileContentsForAnalysis, dedupeKeys);\n for (const f of osvFindings) { f.confidence = \"high\"; }\n allFindings.push(...osvFindings);\n if (verbose && osvFindings.length > 0) {\n spinner.info(`OSV.dev found ${osvFindings.length} additional vulnerable dependencies`);\n }\n } catch (err) {\n if (verbose) spinner.info(`OSV.dev lookup skipped: ${err instanceof Error ? err.message : \"unknown error\"}`);\n }\n\n if (verbose && depFindings.length > 0) {\n spinner.info(`Dependency scanner found ${depFindings.length} issues`);\n }\n\n // 2c: Entropy-based secret detection\n spinner.text = \"Scanning for high-entropy secrets...\";\n const entropyFindings = scanEntropy(fileContentsForAnalysis);\n for (const f of entropyFindings) { f.confidence = \"medium\"; }\n allFindings.push(...entropyFindings);\n if (verbose && entropyFindings.length > 0) {\n spinner.info(`Entropy scanner found ${entropyFindings.length} potential secrets`);\n }\n\n // 2d: Configuration file deep analysis\n spinner.text = \"Analyzing configuration files...\";\n const configFindings = scanConfigs(fileContentsForAnalysis);\n for (const f of configFindings) { f.confidence = \"high\"; }\n allFindings.push(...configFindings);\n if (verbose && configFindings.length > 0) {\n spinner.info(`Config analyzer found ${configFindings.length} issues`);\n }\n\n // 2e: Multi-file cross-reference analysis\n spinner.text = \"Running cross-file analysis...\";\n const multiFileFindings = scanMultiFile(fileContentsForAnalysis);\n for (const f of multiFileFindings) { f.confidence = \"medium\"; }\n allFindings.push(...multiFileFindings);\n if (verbose && multiFileFindings.length > 0) {\n spinner.info(`Multi-file analyzer found ${multiFileFindings.length} issues`);\n }\n\n // 2f: Semgrep + Gitleaks (run in parallel, gracefully skip if not installed)\n spinner.text = \"Running external scanners...\";\n spinner.color = \"yellow\";\n\n // Resolve custom rules directory (shipped with xploitscan)\n const rulesDir = resolve(join(import.meta.dirname, \"../../rules\"));\n const fallbackRulesDir = resolve(join(dir, \"../rules\"));\n\n const [semgrepResult, gitleaksResult] = await Promise.allSettled([\n runSemgrep(dir, rulesDir).catch(() => runSemgrep(dir, fallbackRulesDir)).catch(() => ({ findings: [] as Finding[], available: false })),\n runGitleaks(dir).catch(() => ({ findings: [] as Finding[], available: false })),\n ]);\n\n const semgrep = semgrepResult.status === \"fulfilled\" ? semgrepResult.value : { findings: [], available: false };\n const gitleaks = gitleaksResult.status === \"fulfilled\" ? gitleaksResult.value : { findings: [], available: false };\n\n allFindings.push(...semgrep.findings);\n allFindings.push(...gitleaks.findings);\n\n if (verbose) {\n if (semgrep.available) {\n spinner.info(`Semgrep found ${semgrep.findings.length} issues`);\n } else {\n spinner.info(chalk.gray(\"Semgrep not installed — install with: pip install semgrep\"));\n }\n if (gitleaks.available) {\n spinner.info(`Gitleaks found ${gitleaks.findings.length} issues`);\n } else {\n spinner.info(chalk.gray(\"Gitleaks not installed — install with: brew install gitleaks\"));\n }\n }\n\n // Show install hints for missing tools (non-verbose, terminal only)\n if (!isSilent && !verbose) {\n const missing: string[] = [];\n if (!semgrep.available) missing.push(\"semgrep (pip install semgrep)\");\n if (!gitleaks.available) missing.push(\"gitleaks (brew install gitleaks)\");\n if (missing.length > 0) {\n spinner.info(chalk.gray(`Optional: install ${missing.join(\" and \")} for deeper scanning`));\n }\n }\n\n const staticCount = allFindings.length;\n spinner.text = `Static analysis found ${staticCount} issue${staticCount !== 1 ? \"s\" : \"\"}`;\n\n // Step 3: AI analysis (if enabled)\n if (useAI) {\n spinner.text = \"Running AI analysis...\";\n spinner.color = \"magenta\";\n\n try {\n const priorityFiles = files\n .map((path) => ({\n path,\n content: readFileContents(dir, path) ?? \"\",\n }))\n .filter((f) => f.content.length > 0 && f.content.length < 30_000)\n .filter((f) => {\n const isHighPriority =\n /(?:api|server|route|auth|middleware|webhook|payment|stripe|supabase)/i.test(f.path) ||\n allFindings.some((finding) => finding.file === f.path) ||\n /(?:query|execute|fetch|prisma|drizzle|mongoose)/i.test(f.content);\n return isHighPriority;\n })\n .slice(0, 20);\n\n if (priorityFiles.length > 0) {\n const aiFindings = await analyzeWithAI(priorityFiles, allFindings);\n allFindings.push(...aiFindings);\n\n if (verbose && aiFindings.length > 0) {\n spinner.info(`AI analysis found ${aiFindings.length} additional issues`);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n spinner.warn(`AI analysis skipped: ${error.message}`);\n }\n }\n } else if (!process.env.ANTHROPIC_API_KEY && !isSilent) {\n spinner.info(\n chalk.gray(\"Tip: Set ANTHROPIC_API_KEY for AI-powered contextual analysis\"),\n );\n }\n\n // Step 3b: AI false positive filter (if API key present)\n // Reviews regex findings with Claude Haiku to remove false positives.\n // Separate from --no-ai (which disables finding generation). This filter\n // only reviews existing findings, it doesn't generate new ones.\n let aiReviewed = false;\n let aiRemovedCount = 0;\n let aiTotalBefore = 0;\n let aiFilteredFindings: Array<{ finding: Finding; reason: string }> = [];\n if (process.env.ANTHROPIC_API_KEY && allFindings.length > 0) {\n spinner.text = \"AI reviewing findings for false positives...\";\n spinner.color = \"cyan\";\n try {\n const fileContentsMap = new Map<string, string>();\n for (const f of allFindings) {\n if (!fileContentsMap.has(f.file)) {\n const content = readFileContents(dir, f.file);\n if (content) fileContentsMap.set(f.file, content);\n }\n }\n const result = await filterFalsePositives(allFindings, fileContentsMap);\n aiReviewed = result.aiReviewed;\n aiRemovedCount = result.removedCount;\n aiTotalBefore = result.totalBefore;\n aiFilteredFindings = result.filteredFindings;\n if (result.removedCount > 0) {\n allFindings.length = 0;\n allFindings.push(...result.findings);\n }\n } catch {\n // AI filter error — keep all findings (graceful degradation)\n }\n }\n\n spinner.stop();\n\n // Step 4: Deduplicate findings (same file + line + similar rule)\n const seen = new Set<string>();\n const dedupedFindings = allFindings.filter((f) => {\n // Normalize: group by file:line and a simplified rule key\n const ruleKey = f.source === \"gitleaks\" ? `secret:${f.file}:${f.line}` : `${f.rule}:${f.file}:${f.line}`;\n if (seen.has(ruleKey)) return false;\n seen.add(ruleKey);\n return true;\n });\n\n // Step 5: Render results\n const result: ScanResult = {\n findings: dedupedFindings,\n filesScanned: files.length,\n duration: Date.now() - startTime,\n timestamp: new Date().toISOString(),\n directory: dir,\n };\n\n switch (format) {\n case \"json\":\n renderJsonReport(result);\n break;\n case \"sarif\":\n renderSarifReport(result);\n break;\n case \"splunk-hec\":\n renderSplunkReport(result);\n break;\n case \"elastic-ecs\":\n renderElasticReport(result);\n break;\n case \"datadog-logs\":\n renderDatadogReport(result);\n break;\n default:\n renderTerminalReport(result, fileContentsForAnalysis);\n break;\n }\n\n // Step 5b: Show AI review status\n if (aiReviewed && !isSilent) {\n console.log(\"\");\n if (aiRemovedCount > 0) {\n if (dedupedFindings.length > 0) {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.white(`${aiRemovedCount} false positive${aiRemovedCount !== 1 ? \"s\" : \"\"} removed · `) + chalk.green(`${dedupedFindings.length} real issue${dedupedFindings.length !== 1 ? \"s\" : \"\"} confirmed`));\n } else {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.green(`clean scan`) + chalk.gray(` (${aiRemovedCount} false positive${aiRemovedCount !== 1 ? \"s\" : \"\"} removed)`));\n }\n } else {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.green(`all ${dedupedFindings.length} finding${dedupedFindings.length !== 1 ? \"s\" : \"\"} confirmed real`));\n }\n // Show filtered findings if --verbose or if user wants detail\n if (verbose && aiFilteredFindings.length > 0) {\n console.log(chalk.gray(`\\n Filtered findings (AI determined these are false positives):`));\n for (const { finding: f, reason } of aiFilteredFindings) {\n console.log(chalk.gray(` ${f.severity.toUpperCase().padEnd(8)} ${f.rule} ${f.file}:${f.line}`));\n console.log(chalk.gray(` Reason: ${reason}`));\n }\n }\n }\n\n // Step 5c: Show upsell for free tier\n if (tier === \"free\" && !isSilent) {\n console.log(\"\");\n if (userPlan === \"anonymous\") {\n console.log(chalk.gray(\" Scanned with 30 free rules.\") + chalk.cyan(\" Log in to unlock all 206 rules →\") + chalk.bold(\" xploitscan auth login\"));\n } else {\n console.log(chalk.gray(\" Scanned with 30 rules.\") + chalk.cyan(\" Upgrade to Pro for all 206 rules →\") + chalk.bold(\" xploitscan upgrade\"));\n }\n console.log(\"\");\n }\n\n // Step 6: Upload results and increment usage (if authenticated)\n if (isAuthenticated()) {\n await Promise.allSettled([\n incrementUsage(),\n uploadScanResults({\n directory: dir,\n filesScanned: files.length,\n findings: dedupedFindings,\n duration: Date.now() - startTime,\n }),\n ]);\n }\n\n // Exit with error code if critical/high findings exist\n const hasCritical = dedupedFindings.some(\n (f) => f.severity === \"critical\" || f.severity === \"high\",\n );\n if (hasCritical) {\n process.exitCode = 1;\n }\n\n // Step 7: Watch mode\n if (options.watch) {\n const watchExclude = new Set([\"node_modules\", \".git\", \"dist\", \"build\", \".next\"]);\n const debounceMs = 500;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let isScanning = false;\n\n console.log(chalk.cyan(\"\\nWatching for changes... (press Ctrl+C to stop)\"));\n\n fsWatch(dir, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n\n // Skip excluded directories\n const parts = filename.split(\"/\");\n if (parts.some((p) => watchExclude.has(p))) return;\n\n console.log(chalk.gray(`File changed: ${filename}`));\n\n if (debounceTimer) clearTimeout(debounceTimer);\n\n debounceTimer = setTimeout(async () => {\n if (isScanning) return;\n isScanning = true;\n\n console.log(chalk.cyan(\"\\nRe-scanning...\\n\"));\n try {\n await scanCommand(directory, {\n ...options,\n watch: false, // prevent recursive watch\n });\n } catch (error) {\n console.error(chalk.red(`Re-scan error: ${error instanceof Error ? error.message : error}`));\n } finally {\n isScanning = false;\n console.log(chalk.cyan(\"\\nWatching for changes... (press Ctrl+C to stop)\"));\n }\n }, debounceMs);\n });\n\n // Keep the process alive\n await new Promise(() => {});\n }\n}\n","import fg from \"fast-glob\";\nimport ignore from \"ignore\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\nconst SOURCE_EXTENSIONS = [\n \"js\", \"jsx\", \"ts\", \"tsx\", \"mjs\", \"cjs\",\n \"vue\", \"svelte\", \"astro\",\n \"py\", \"rb\", \"go\", \"rs\", \"java\", \"php\",\n \"swift\", \"kt\", \"kts\", \"dart\", \"cs\",\n \"c\", \"cpp\", \"h\",\n \"sh\", \"bash\", \"zsh\",\n \"env\", \"yaml\", \"yml\", \"toml\", \"json\", \"xml\",\n \"html\", \"htm\", \"sql\",\n \"properties\", \"ini\", \"cfg\", \"conf\",\n \"tf\", \"hcl\", \"dockerfile\",\n \"erb\", \"jinja\", \"j2\",\n \"gradle\",\n \"r\", \"lua\", \"pl\", \"pm\", \"ex\", \"exs\",\n \"ipynb\", \"md\",\n \"prisma\", \"plist\", \"pbxproj\", \"entitlements\", \"rules\", \"csv\",\n];\n\nconst SOURCE_FILENAMES = [\n \"Dockerfile\", \"Makefile\", \"Gemfile\", \"Rakefile\",\n \"package.json\", \"Cargo.toml\", \"go.mod\", \"requirements.txt\", \"Pipfile\",\n \"next.config.js\", \"next.config.mjs\", \"next.config.ts\", \"vercel.json\",\n \"firebase.json\", \".firebaserc\", \"firestore.rules\",\n \"app.json\", \"app.config.js\", \"eas.json\",\n \"wrangler.toml\", \"netlify.toml\",\n \"drizzle.config.ts\", \"drizzle.config.js\",\n \"Procfile\", \"Caddyfile\", \"nginx.conf\",\n \"AndroidManifest.xml\",\n];\n\nconst ALWAYS_IGNORE = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".nuxt\",\n \".svelte-kit\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n \"coverage\",\n \".turbo\",\n \"*.min.js\",\n \"*.min.css\",\n \"*.map\",\n \"package-lock.json\",\n \"pnpm-lock.yaml\",\n \"yarn.lock\",\n // Intentionally-vulnerable test corpora. Security tools (XploitScan,\n // Semgrep, Bearer, Gitleaks, Snyk Code) all maintain directories of\n // known-bad code samples used to verify that rules fire. Scanning\n // them produces a flood of \"critical\" findings on the scanner's own\n // dogfood, drowning the signal from real product code. Skip by\n // default. Users who genuinely want to scan a fixture directory can\n // override via .xploitscanignore (the negation syntax `!test-\n // fixtures/` un-ignores it).\n //\n // We only match these as directory names anywhere in the tree, so a\n // project whose actual source happens to live under a path called\n // `fixtures-prod/` is unaffected.\n \"test-fixtures\",\n \"test-fixtures/**\",\n \"__fixtures__\",\n \"__fixtures__/**\",\n \"vulnerable-samples\",\n \"vulnerable-samples/**\",\n];\n\nexport async function collectFiles(directory: string): Promise<string[]> {\n const ig = ignore.default();\n\n // Load .gitignore if present\n const gitignorePath = join(directory, \".gitignore\");\n if (existsSync(gitignorePath)) {\n const gitignoreContent = readFileSync(gitignorePath, \"utf-8\");\n ig.add(gitignoreContent);\n }\n\n // Load .xploitscanignore if present\n const xploitscanIgnorePath = join(directory, \".xploitscanignore\");\n if (existsSync(xploitscanIgnorePath)) {\n const xploitscanIgnoreContent = readFileSync(xploitscanIgnorePath, \"utf-8\");\n ig.add(xploitscanIgnoreContent);\n }\n\n // Always ignore these\n ig.add(ALWAYS_IGNORE);\n\n const patterns = SOURCE_EXTENSIONS.map((ext) => `**/*.${ext}`);\n // Also grab dotfiles like .env, .env.local, etc.\n patterns.push(\"**/.env*\");\n // Also grab files matched by name (Dockerfile, Makefile, etc.)\n for (const name of SOURCE_FILENAMES) {\n patterns.push(`**/${name}`);\n }\n\n const files = await fg(patterns, {\n cwd: directory,\n absolute: false,\n dot: true,\n onlyFiles: true,\n ignore: ALWAYS_IGNORE.map((p) => `**/${p}`),\n });\n\n // Apply .gitignore filtering\n return files.filter((file) => !ig.ignores(file));\n}\n\nexport function readFileContents(\n directory: string,\n filePath: string,\n): string | null {\n try {\n const fullPath = resolve(join(directory, filePath));\n if (!fullPath.startsWith(resolve(directory))) {\n throw new Error(\"Path traversal detected\");\n }\n return readFileSync(fullPath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport function getSnippet(\n content: string,\n line: number,\n contextLines = 2,\n): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n\n return lines\n .slice(start, end)\n .map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">\" : \" \";\n return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n","import { cosmiconfig } from \"cosmiconfig\";\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface XploitScanRcConfig {\n rules?: {\n disable?: string[];\n severityOverride?: Record<string, string>;\n };\n scan?: {\n exclude?: string[];\n maxFileSize?: number;\n extensions?: string[];\n };\n output?: {\n format?: \"terminal\" | \"json\" | \"sarif\";\n verbose?: boolean;\n };\n watch?: {\n debounce?: number;\n exclude?: string[];\n };\n}\n\nexport interface XploitScanConfig {\n /** Glob patterns to exclude from scanning */\n exclude?: string[];\n /** Enable/disable AI analysis (requires ANTHROPIC_API_KEY) */\n ai?: boolean;\n /** Severity threshold to report: only show findings at or above this level */\n severity?: \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\";\n /** Custom rules to disable by ID */\n disableRules?: string[];\n /** Severity overrides by rule ID */\n severityOverride?: Record<string, string>;\n /** Max file size in bytes */\n maxFileSize?: number;\n /** Additional file extensions to scan */\n extensions?: string[];\n /** Output format */\n format?: \"terminal\" | \"json\" | \"sarif\";\n /** Verbose output */\n verbose?: boolean;\n /** Watch mode config */\n watch?: {\n debounce?: number;\n exclude?: string[];\n };\n}\n\nconst defaults: XploitScanConfig = {\n exclude: [],\n ai: true,\n severity: \"low\",\n disableRules: [],\n};\n\nfunction loadXploitscanRc(directory: string): XploitScanRcConfig | null {\n try {\n const rcPath = join(directory, \".xploitscanrc\");\n const content = readFileSync(rcPath, \"utf-8\");\n return JSON.parse(content) as XploitScanRcConfig;\n } catch {\n return null;\n }\n}\n\nfunction mergeRcIntoConfig(\n base: XploitScanConfig,\n rc: XploitScanRcConfig,\n): XploitScanConfig {\n const merged = { ...base };\n\n // Merge rules\n if (rc.rules) {\n if (rc.rules.disable) {\n merged.disableRules = [\n ...new Set([...(merged.disableRules ?? []), ...rc.rules.disable]),\n ];\n }\n if (rc.rules.severityOverride) {\n merged.severityOverride = {\n ...(merged.severityOverride ?? {}),\n ...rc.rules.severityOverride,\n };\n }\n }\n\n // Merge scan settings\n if (rc.scan) {\n if (rc.scan.exclude) {\n merged.exclude = [\n ...new Set([...(merged.exclude ?? []), ...rc.scan.exclude]),\n ];\n }\n if (rc.scan.maxFileSize !== undefined) {\n merged.maxFileSize = rc.scan.maxFileSize;\n }\n if (rc.scan.extensions) {\n merged.extensions = [\n ...new Set([...(merged.extensions ?? []), ...rc.scan.extensions]),\n ];\n }\n }\n\n // Merge output settings (rc takes priority)\n if (rc.output) {\n if (rc.output.format !== undefined) {\n merged.format = rc.output.format;\n }\n if (rc.output.verbose !== undefined) {\n merged.verbose = rc.output.verbose;\n }\n }\n\n // Merge watch settings\n if (rc.watch) {\n merged.watch = {\n ...(merged.watch ?? {}),\n ...rc.watch,\n };\n }\n\n return merged;\n}\n\nexport async function loadConfig(\n directory: string,\n): Promise<XploitScanConfig> {\n const explorer = cosmiconfig(\"xploitscan\");\n\n let config = { ...defaults };\n\n try {\n const result = await explorer.search(directory);\n if (result && result.config) {\n config = { ...defaults, ...result.config };\n }\n } catch {\n // Config loading failed, use defaults\n }\n\n // Load .xploitscanrc and merge (rc takes priority)\n const rc = loadXploitscanRc(directory);\n if (rc) {\n config = mergeRcIntoConfig(config, rc);\n }\n\n return config;\n}\n","import { execFile } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport type { Finding, Severity } from \"../types.js\";\n\ninterface SemgrepSarifResult {\n runs?: Array<{\n results?: Array<{\n ruleId?: string;\n level?: string;\n message?: { text?: string };\n locations?: Array<{\n physicalLocation?: {\n artifactLocation?: { uri?: string };\n region?: {\n startLine?: number;\n startColumn?: number;\n snippet?: { text?: string };\n };\n };\n }>;\n }>;\n }>;\n}\n\nconst SEVERITY_MAP: Record<string, Severity> = {\n error: \"high\",\n warning: \"medium\",\n note: \"low\",\n none: \"info\",\n};\n\nfunction semgrepSeverityToXploitscan(level: string): Severity {\n return SEVERITY_MAP[level] ?? \"medium\";\n}\n\nasync function isSemgrepInstalled(): Promise<boolean> {\n return new Promise((resolve) => {\n execFile(\"semgrep\", [\"--version\"], (error) => {\n resolve(!error);\n });\n });\n}\n\nexport async function runSemgrep(\n directory: string,\n customRulesDir?: string,\n): Promise<{ findings: Finding[]; available: boolean }> {\n const installed = await isSemgrepInstalled();\n if (!installed) {\n return { findings: [], available: false };\n }\n\n const findings: Finding[] = [];\n\n // Create a temp directory for SARIF output\n const tmpDir = await mkdtemp(join(tmpdir(), \"xploitscan-semgrep-\"));\n const sarifPath = join(tmpDir, \"results.sarif\");\n\n try {\n // Build semgrep args\n const args = [\n \"scan\",\n \"--sarif\",\n \"--output\", sarifPath,\n \"--quiet\",\n \"--no-git-ignore\", // We handle .gitignore ourselves\n \"--timeout\", \"30\",\n \"--max-target-bytes\", \"1000000\",\n ];\n\n // Use auto config (community rules) + custom rules if available\n args.push(\"--config\", \"auto\");\n\n if (customRulesDir && existsSync(customRulesDir)) {\n args.push(\"--config\", customRulesDir);\n }\n\n args.push(directory);\n\n // Run semgrep\n await new Promise<void>((resolve, reject) => {\n const proc = execFile(\n \"semgrep\",\n args,\n { timeout: 120_000, maxBuffer: 10 * 1024 * 1024 },\n (error, _stdout, stderr) => {\n // Semgrep returns exit code 1 when findings exist — that's fine\n if (error && error.code !== 1) {\n reject(new Error(`Semgrep failed: ${stderr || error.message}`));\n } else {\n resolve();\n }\n },\n );\n });\n\n // Parse SARIF output\n if (!existsSync(sarifPath)) return { findings, available: true };\n\n const sarifContent = await readFile(sarifPath, \"utf-8\");\n const sarif: SemgrepSarifResult = JSON.parse(sarifContent);\n\n for (const run of sarif.runs ?? []) {\n for (const result of run.results ?? []) {\n const location = result.locations?.[0]?.physicalLocation;\n const filePath = location?.artifactLocation?.uri ?? \"unknown\";\n const line = location?.region?.startLine ?? 1;\n const snippet = location?.region?.snippet?.text ?? \"\";\n\n // Determine category from rule ID\n const ruleId = result.ruleId ?? \"semgrep\";\n const category = categorizeSemgrepRule(ruleId);\n\n findings.push({\n id: `SG-${filePath}:${line}:${ruleId}`,\n rule: ruleId,\n severity: semgrepSeverityToXploitscan(result.level ?? \"warning\"),\n title: truncate(result.message?.text ?? ruleId, 100),\n description: result.message?.text ?? \"\",\n file: filePath.replace(/^file:\\/\\//, \"\"),\n line,\n column: location?.region?.startColumn,\n snippet: formatSnippet(snippet, line),\n category,\n source: \"semgrep\",\n });\n }\n }\n } finally {\n // Clean up temp directory\n await rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n }\n\n return { findings, available: true };\n}\n\nfunction categorizeSemgrepRule(ruleId: string): string {\n const id = ruleId.toLowerCase();\n if (id.includes(\"sql\") || id.includes(\"injection\") || id.includes(\"xss\") || id.includes(\"command\")) return \"Injection\";\n if (id.includes(\"auth\") || id.includes(\"session\")) return \"Authentication\";\n if (id.includes(\"crypto\") || id.includes(\"hash\") || id.includes(\"random\")) return \"Cryptography\";\n if (id.includes(\"cors\") || id.includes(\"header\") || id.includes(\"config\")) return \"Configuration\";\n if (id.includes(\"secret\") || id.includes(\"key\") || id.includes(\"password\") || id.includes(\"credential\")) return \"Secrets\";\n if (id.includes(\"path\") || id.includes(\"traversal\") || id.includes(\"file\")) return \"Path Traversal\";\n if (id.includes(\"deserial\")) return \"Deserialization\";\n return \"Security\";\n}\n\nfunction formatSnippet(text: string, line: number): string {\n if (!text) return \"\";\n const lines = text.split(\"\\n\");\n return lines\n .map((l, i) => {\n const num = line + i;\n return ` ${num.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n\nfunction truncate(str: string, max: number): string {\n return str.length > max ? str.substring(0, max - 3) + \"...\" : str;\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport type { Finding, Severity } from \"../types.js\";\nimport { getSnippet, readFileContents } from \"../utils/files.js\";\n\ninterface GitleaksResult {\n Description: string;\n File: string;\n StartLine: number;\n EndLine: number;\n StartColumn: number;\n EndColumn: number;\n Match: string;\n Secret: string;\n RuleID: string;\n Entropy: number;\n Tags?: string[];\n}\n\nconst RULE_SEVERITY: Record<string, Severity> = {\n \"aws-access-token\": \"critical\",\n \"aws-secret-access-key\": \"critical\",\n \"stripe-access-token\": \"critical\",\n \"github-pat\": \"critical\",\n \"private-key\": \"critical\",\n \"generic-api-key\": \"high\",\n \"slack-webhook\": \"high\",\n \"twilio-api-key\": \"high\",\n \"sendgrid-api-key\": \"high\",\n \"shopify-access-token\": \"high\",\n \"gcp-api-key\": \"critical\",\n \"heroku-api-key\": \"high\",\n \"npm-access-token\": \"critical\",\n \"pypi-upload-token\": \"critical\",\n \"telegram-bot-api-token\": \"high\",\n \"discord-bot-token\": \"high\",\n \"firebase-api-key\": \"high\",\n};\n\nasync function isGitleaksInstalled(): Promise<boolean> {\n return new Promise((resolve) => {\n execFile(\"gitleaks\", [\"version\"], (error) => {\n resolve(!error);\n });\n });\n}\n\nexport async function runGitleaks(\n directory: string,\n): Promise<{ findings: Finding[]; available: boolean }> {\n const installed = await isGitleaksInstalled();\n if (!installed) {\n return { findings: [], available: false };\n }\n\n const findings: Finding[] = [];\n const tmpDir = await mkdtemp(join(tmpdir(), \"xploitscan-gitleaks-\"));\n const reportPath = join(tmpDir, \"results.json\");\n\n try {\n const args = [\n \"detect\",\n \"--source\", directory,\n \"--report-path\", reportPath,\n \"--report-format\", \"json\",\n \"--no-git\",\n \"--exit-code\", \"0\", // Don't fail on findings\n ];\n\n await new Promise<void>((resolve, reject) => {\n execFile(\n \"gitleaks\",\n args,\n { timeout: 60_000, maxBuffer: 10 * 1024 * 1024 },\n (error, _stdout, stderr) => {\n if (error) {\n const sanitizedError = (stderr || error.message).slice(0, 200);\n reject(new Error(`Gitleaks failed: ${sanitizedError}`));\n } else {\n resolve();\n }\n },\n );\n });\n\n // Parse results\n if (!existsSync(reportPath)) return { findings, available: true };\n\n const reportContent = await readFile(reportPath, \"utf-8\");\n if (!reportContent.trim()) return { findings, available: true };\n\n const results: GitleaksResult[] = JSON.parse(reportContent);\n\n for (const result of results) {\n const filePath = result.File;\n const line = result.StartLine + 1; // Gitleaks uses 0-based lines\n const severity = RULE_SEVERITY[result.RuleID] ?? \"high\";\n\n // Read file content for snippet\n const content = readFileContents(directory, filePath);\n const snippet = content ? getSnippet(content, line) : ` ${result.Match}`;\n\n // Redact the actual secret in the description\n const redactedSecret = result.Secret.length > 8\n ? result.Secret.substring(0, 4) + \"...\" + result.Secret.substring(result.Secret.length - 4)\n : \"****\";\n\n findings.push({\n id: `GL-${filePath}:${line}:${result.RuleID}`,\n rule: `GL:${result.RuleID}`,\n severity,\n title: `${result.Description} (detected by Gitleaks)`,\n description: `A secret matching \"${result.RuleID}\" pattern was found: ${redactedSecret}. If this is a real credential, it may already be compromised. Rotate it immediately and move it to environment variables.`,\n file: filePath,\n line,\n column: result.StartColumn,\n snippet,\n fix: `1. Rotate this credential immediately (it may be in git history)\\n2. Move it to a .env file: ${result.RuleID.toUpperCase().replace(/-/g, \"_\")}=<new-value>\\n3. Add .env to .gitignore\\n4. Remove from git history: git filter-branch or BFG Repo Cleaner`,\n category: \"Secrets\",\n source: \"gitleaks\",\n });\n }\n } finally {\n await rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n }\n\n return { findings, available: true };\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Finding, Severity } from \"../types.js\";\n\nconst SYSTEM_PROMPT = `You are a security auditor specializing in code generated by AI tools (Cursor, Lovable, Bolt, Replit, Claude). Your audience is non-expert developers who may not understand security jargon.\n\nAnalyze the provided code for security vulnerabilities. Focus on issues that AI code generators commonly introduce:\n- Missing authentication/authorization\n- Exposed secrets or credentials\n- SQL injection, XSS, and other injection flaws\n- Insecure direct object references (IDOR)\n- Missing input validation\n- Insecure defaults (permissive CORS, no rate limiting)\n- Client-side security checks without server-side enforcement\n- Supabase RLS misconfigurations\n- Unprotected payment/webhook endpoints\n\nFor each vulnerability found, respond with a JSON array of objects:\n{\n \"title\": \"Short, clear title\",\n \"severity\": \"critical|high|medium|low\",\n \"line\": <approximate line number>,\n \"description\": \"Plain-English explanation a non-developer can understand. What's the risk? What could an attacker do?\",\n \"fix\": \"Step-by-step fix instructions with code example if helpful\",\n \"category\": \"Secrets|Authentication|Authorization|Injection|Configuration|Payment Security|Data Exposure\"\n}\n\nIf no vulnerabilities are found, return an empty array: []\n\nRules:\n- Only report real, exploitable issues — no theoretical concerns\n- Be specific about what an attacker could do\n- Explain fixes in beginner-friendly language\n- Reference specific line numbers`;\n\nexport async function analyzeWithAI(\n files: { path: string; content: string }[],\n existingFindings: Finding[],\n): Promise<Finding[]> {\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) return [];\n\n const client = new Anthropic();\n\n // Build context: send files with existing findings for dedup\n const existingRules = new Set(\n existingFindings.map((f) => `${f.file}:${f.line}:${f.rule}`),\n );\n\n // Batch files into chunks to stay within token limits (~50KB per chunk)\n const chunks = chunkFiles(files, 50_000);\n const allFindings: Finding[] = [];\n\n for (const chunk of chunks) {\n const fileContext = chunk\n .map((f) => `--- ${f.path} ---\\n${f.content}`)\n .join(\"\\n\\n\");\n\n const existingNote = existingFindings.length > 0\n ? `\\n\\nThe following issues have already been found by static rules (do NOT duplicate these):\\n${existingFindings.map((f) => `- ${f.file}:${f.line} — ${f.title}`).join(\"\\n\")}`\n : \"\";\n\n try {\n const response = await client.messages.create({\n model: \"claude-sonnet-4-5-20250514\",\n max_tokens: 4096,\n messages: [\n {\n role: \"user\",\n content: `Analyze these source files for security vulnerabilities:${existingNote}\\n\\n${fileContext}`,\n },\n ],\n system: SYSTEM_PROMPT,\n });\n\n const text = response.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\n // Extract JSON from response (handles markdown code blocks)\n const jsonMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (!jsonMatch) continue;\n\n const parsed = JSON.parse(jsonMatch[0]) as Array<{\n title: string;\n severity: Severity;\n line: number;\n description: string;\n fix: string;\n category: string;\n }>;\n\n for (const item of parsed) {\n // Find which file this finding belongs to\n const matchFile = chunk.find((f) => {\n const lines = f.content.split(\"\\n\");\n return item.line <= lines.length;\n });\n const file = matchFile?.path ?? chunk[0].path;\n\n const key = `${file}:${item.line}:AI`;\n if (existingRules.has(key)) continue;\n\n const content = chunk.find((f) => f.path === file)?.content ?? \"\";\n const lines = content.split(\"\\n\");\n const snippetStart = Math.max(0, item.line - 3);\n const snippetEnd = Math.min(lines.length, item.line + 2);\n const snippet = lines\n .slice(snippetStart, snippetEnd)\n .map((l, i) => {\n const num = snippetStart + i + 1;\n const marker = num === item.line ? \">\" : \" \";\n return `${marker} ${num.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n\n allFindings.push({\n id: `AI-${file}:${item.line}`,\n rule: \"AI\",\n severity: item.severity,\n title: item.title,\n description: item.description,\n file,\n line: item.line,\n snippet,\n fix: item.fix,\n category: item.category,\n source: \"ai\",\n });\n }\n } catch (error) {\n // AI analysis failed for this chunk — continue with others\n if (error instanceof Error && error.message.includes(\"API key\")) {\n throw new Error(\n \"Invalid ANTHROPIC_API_KEY. Get one at https://console.anthropic.com/\",\n );\n }\n }\n }\n\n return allFindings;\n}\n\nfunction chunkFiles(\n files: { path: string; content: string }[],\n maxChars: number,\n): { path: string; content: string }[][] {\n const chunks: { path: string; content: string }[][] = [];\n let current: { path: string; content: string }[] = [];\n let currentSize = 0;\n\n for (const file of files) {\n // Truncate individual files that exceed the chunk limit\n const truncatedContent = file.content.length > maxChars\n ? file.content.slice(0, maxChars) + \"\\n// ... truncated for analysis\"\n : file.content;\n const entry = { path: file.path, content: truncatedContent };\n\n if (currentSize + entry.content.length > maxChars && current.length > 0) {\n chunks.push(current);\n current = [];\n currentSize = 0;\n }\n current.push(entry);\n currentSize += entry.content.length;\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n","/**\n * AST-based JavaScript/TypeScript analyzer\n * Uses regex-based pseudo-AST parsing (no external deps) to understand code structure\n * and reduce false positives from comments, strings, and non-executable code.\n */\n\nimport type { Finding, Severity } from \"../types.js\";\n\n// ────────────────────────────────────────────\n// Code Structure Detection\n// ────────────────────────────────────────────\n\ninterface CodeContext {\n isComment: boolean;\n isString: boolean;\n isImport: boolean;\n isTestFile: boolean;\n isTypeDefinition: boolean;\n functionScope: string | null;\n nearestAssignment: string | null;\n}\n\n/** Strip all comments from JS/TS/Python code to prevent false positives */\nexport function stripComments(content: string): string {\n let result = \"\";\n let i = 0;\n let inSingleQuote = false;\n let inDoubleQuote = false;\n let inTemplate = false;\n let inSingleLineComment = false;\n let inMultiLineComment = false;\n\n while (i < content.length) {\n const ch = content[i];\n const next = content[i + 1];\n\n if (inSingleLineComment) {\n if (ch === \"\\n\") {\n inSingleLineComment = false;\n result += ch;\n }\n i++;\n continue;\n }\n\n if (inMultiLineComment) {\n if (ch === \"*\" && next === \"/\") {\n inMultiLineComment = false;\n i += 2;\n } else {\n if (ch === \"\\n\") result += ch; // preserve line numbers\n i++;\n }\n continue;\n }\n\n if (inSingleQuote) {\n result += ch;\n if (ch === \"'\" && content[i - 1] !== \"\\\\\") inSingleQuote = false;\n i++;\n continue;\n }\n\n if (inDoubleQuote) {\n result += ch;\n if (ch === '\"' && content[i - 1] !== \"\\\\\") inDoubleQuote = false;\n i++;\n continue;\n }\n\n if (inTemplate) {\n result += ch;\n if (ch === \"`\" && content[i - 1] !== \"\\\\\") inTemplate = false;\n i++;\n continue;\n }\n\n // Check for comment starts\n if (ch === \"/\" && next === \"/\") {\n inSingleLineComment = true;\n i += 2;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n inMultiLineComment = true;\n i += 2;\n continue;\n }\n // Python/YAML comments\n if (ch === \"#\" && !inSingleQuote && !inDoubleQuote) {\n inSingleLineComment = true;\n i++;\n continue;\n }\n\n // Check for string starts\n if (ch === \"'\") inSingleQuote = true;\n if (ch === '\"') inDoubleQuote = true;\n if (ch === \"`\") inTemplate = true;\n\n result += ch;\n i++;\n }\n\n return result;\n}\n\n/** Detect if a line index falls within a function body */\nexport function getFunctionScope(content: string, lineIndex: number): string | null {\n const lines = content.split(\"\\n\");\n // Walk backwards from the match line to find enclosing function\n for (let i = lineIndex; i >= 0; i--) {\n const line = lines[i];\n const funcMatch = line.match(/(?:function\\s+(\\w+)|(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?(?:function|\\([^)]*\\)\\s*=>)|(\\w+)\\s*\\([^)]*\\)\\s*\\{)/);\n if (funcMatch) {\n return funcMatch[1] || funcMatch[2] || funcMatch[3] || \"anonymous\";\n }\n }\n return null;\n}\n\n/** Check if content between two positions has a validation/sanitization pattern */\nexport function hasValidationBetween(content: string, startLine: number, endLine: number): boolean {\n const lines = content.split(\"\\n\");\n const segment = lines.slice(startLine, endLine + 1).join(\"\\n\");\n return /(?:validate|sanitize|escape|check|verify|assert|ensure|guard|protect|filter|whitelist|allowlist|isValid|isAllowed|isSafe)/i.test(segment);\n}\n\n/** Detect data flow: does user input reach a dangerous sink? */\nexport function tracesUserInput(content: string, sinkLine: number, linesBack: number = 15): boolean {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, sinkLine - linesBack);\n const segment = lines.slice(start, sinkLine + 1).join(\"\\n\");\n\n const userInputSources = /(?:req\\.(?:body|query|params|headers)|body\\.|input\\.|params\\.|args\\.|request\\.|formData|searchParams|useSearchParams|URLSearchParams)/i;\n return userInputSources.test(segment);\n}\n\n/** Extract all import/require statements to understand dependencies */\nexport function extractImports(content: string): string[] {\n const imports: string[] = [];\n const patterns = [\n /import\\s+.*?from\\s+[\"'`]([^\"'`]+)[\"'`]/g,\n /require\\s*\\(\\s*[\"'`]([^\"'`]+)[\"'`]\\s*\\)/g,\n ];\n for (const p of patterns) {\n let m;\n while ((m = p.exec(content)) !== null) {\n imports.push(m[1]);\n }\n }\n return imports;\n}\n\n/** Check if a file imports/uses a specific security middleware */\nexport function hasSecurityMiddleware(content: string): { auth: boolean; rateLimit: boolean; helmet: boolean; cors: boolean; csrf: boolean } {\n return {\n auth: /(?:requireAuth|isAuthenticated|authenticate|passport|jwt\\.verify|clerk|auth0|nextauth|session\\.user|getServerSession|getAuth|withAuth|authMiddleware)/i.test(content),\n rateLimit: /(?:rateLimit|rate-limit|express-rate-limit|throttle|slowDown|limiter)/i.test(content),\n helmet: /(?:helmet|security-headers|securityHeaders)/i.test(content),\n cors: /(?:cors\\(|corsMiddleware|allowedOrigins)/i.test(content),\n csrf: /(?:csrf|csurf|csrfToken|_csrf)/i.test(content),\n };\n}\n\n// ────────────────────────────────────────────\n// AST-Enhanced Rule Runner\n// ────────────────────────────────────────────\n\nexport interface ASTContext {\n strippedContent: string; // Code with comments removed\n imports: string[]; // All imports/requires\n middleware: ReturnType<typeof hasSecurityMiddleware>;\n isTestFile: boolean;\n isConfigFile: boolean;\n isScannerFile: boolean; // Self-detection: is this a security scanner?\n}\n\nexport function buildASTContext(content: string, filePath: string): ASTContext {\n return {\n strippedContent: stripComments(content),\n imports: extractImports(content),\n middleware: hasSecurityMiddleware(content),\n isTestFile: /(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|\\.stories\\.|fixtures?\\/|mocks?\\/|\\.cy\\.|\\.e2e\\.)/i.test(filePath),\n isConfigFile: /(?:\\.config\\.|config\\/|\\.rc$|tsconfig|next\\.config|vite\\.config|webpack\\.config)/i.test(filePath),\n isScannerFile: /(?:custom-rules|scanner|security-check|eslint-plugin|lint)/i.test(filePath) &&\n /(?:findMatches|check\\s*\\(content|rule\\.check|severity|category)/i.test(content),\n };\n}\n","/**\n * OSV.dev client — queries the public Open Source Vulnerabilities database\n * for known CVEs affecting a list of dependencies. Augments the hand-curated\n * allowlist in dependency-scanner.ts with live data so we catch the long tail\n * of vulnerable packages we haven't manually added.\n *\n * API: https://google.github.io/osv.dev/post-v1-querybatch/\n * Batch endpoint accepts up to 1000 package queries per request so a typical\n * project's full dependency list resolves in one HTTP round trip.\n */\n\ninterface OsvQuery {\n package: {\n name: string;\n ecosystem: \"npm\" | \"PyPI\" | \"RubyGems\" | \"Go\" | \"crates.io\" | \"Maven\" | \"Packagist\" | \"NuGet\";\n };\n version: string;\n}\n\ninterface OsvVulnSummary {\n id: string; // e.g. GHSA-xxxx-xxxx-xxxx or CVE-2023-12345\n summary?: string;\n details?: string;\n aliases?: string[];\n severity?: Array<{ type: string; score: string }>;\n}\n\ninterface OsvBatchResponse {\n results: Array<{\n vulns?: Array<{ id: string }>;\n }>;\n}\n\nexport interface OsvMatch {\n ecosystem: string;\n name: string;\n version: string;\n vulns: OsvVulnSummary[];\n}\n\nconst OSV_BATCH_URL = \"https://api.osv.dev/v1/querybatch\";\nconst OSV_VULN_URL = \"https://api.osv.dev/v1/vulns\";\nconst NETWORK_TIMEOUT_MS = 5000;\n\nasync function fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<Response> {\n const controller = new AbortController();\n const id = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(id);\n }\n}\n\n/**\n * Query OSV.dev for every given (ecosystem, name, version) tuple and return\n * any vulnerabilities found. Fails soft on network error / timeout so offline\n * scans still work — the caller just gets an empty array and continues.\n *\n * Rate limit aware: the batch endpoint lets us send the whole dependency\n * list in one request, then we only fetch details for packages that\n * actually matched (avoiding the N+1 lookup pattern).\n */\nexport async function queryOsvBatch(queries: OsvQuery[]): Promise<OsvMatch[]> {\n if (queries.length === 0) return [];\n try {\n // Step 1: batch query returns only vuln IDs per package\n const batchRes = await fetchWithTimeout(\n OSV_BATCH_URL,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ queries }),\n },\n NETWORK_TIMEOUT_MS,\n );\n if (!batchRes.ok) return [];\n const batch = (await batchRes.json()) as OsvBatchResponse;\n if (!Array.isArray(batch.results)) return [];\n\n // Collect unique vuln IDs across all matched packages so we only fetch\n // each one once (many packages can share a CVE)\n const uniqueVulnIds = new Set<string>();\n for (const result of batch.results) {\n for (const v of result.vulns ?? []) uniqueVulnIds.add(v.id);\n }\n\n // Step 2: fetch full details for each unique vuln (sequential with\n // per-request timeout — parallelizing would hammer their rate limiter)\n const vulnDetails = new Map<string, OsvVulnSummary>();\n for (const vulnId of uniqueVulnIds) {\n try {\n const detailRes = await fetchWithTimeout(\n `${OSV_VULN_URL}/${encodeURIComponent(vulnId)}`,\n { method: \"GET\" },\n NETWORK_TIMEOUT_MS,\n );\n if (!detailRes.ok) continue;\n const detail = (await detailRes.json()) as OsvVulnSummary;\n vulnDetails.set(vulnId, detail);\n } catch {\n // Skip individual vuln detail failures\n }\n }\n\n // Step 3: zip results back into the caller's query order\n const matches: OsvMatch[] = [];\n batch.results.forEach((result, i) => {\n const ids = (result.vulns ?? []).map((v) => v.id);\n if (ids.length === 0) return;\n const q = queries[i];\n matches.push({\n ecosystem: q.package.ecosystem,\n name: q.package.name,\n version: q.version,\n vulns: ids.map((id) => vulnDetails.get(id) ?? { id }),\n });\n });\n return matches;\n } catch {\n // Network error, timeout, DNS failure — fail soft so scans still work\n // offline. The manual allowlist in dependency-scanner.ts is the fallback.\n return [];\n }\n}\n\n/**\n * Classify an OSV vuln summary into a Finding severity. OSV ships several\n * severity vectors (CVSS v3, CVSS v4, ecosystem-specific) and most entries\n * have at least one. Falls back to 'medium' when unclassified.\n */\nexport function osvSeverity(vuln: OsvVulnSummary): \"critical\" | \"high\" | \"medium\" | \"low\" {\n const vectors = vuln.severity ?? [];\n for (const v of vectors) {\n // CVSS scores are embedded in a vector string — easiest to yank the first\n // numeric \"base score\" value out of it\n const match = v.score.match(/\\b(\\d+(?:\\.\\d+)?)\\b/);\n if (!match) continue;\n const score = parseFloat(match[1]);\n if (Number.isFinite(score)) {\n if (score >= 9) return \"critical\";\n if (score >= 7) return \"high\";\n if (score >= 4) return \"medium\";\n return \"low\";\n }\n }\n return \"medium\";\n}\n","/**\n * Dependency Vulnerability Scanner\n * Parses package.json, requirements.txt, Gemfile, etc. and checks\n * for known vulnerable packages and risky dependency patterns.\n */\n\nimport type { Finding } from \"../types.js\";\nimport { queryOsvBatch, osvSeverity } from \"./osv.js\";\n\n// Known vulnerable packages and their details\n// Format: package -> { minSafeVersion, cve, severity, description }\ninterface VulnEntry {\n minSafe: string;\n cve: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n title: string;\n description: string;\n fix: string;\n}\n\n// Well-known vulnerable npm packages (manually curated, high-signal)\nconst KNOWN_VULN_NPM: Record<string, VulnEntry[]> = {\n \"lodash\": [{\n minSafe: \"4.17.21\", cve: \"CVE-2021-23337\", severity: \"critical\",\n title: \"Lodash Prototype Pollution\",\n description: \"lodash before 4.17.21 is vulnerable to prototype pollution via set, setWith, and zipObjectDeep.\",\n fix: \"Upgrade lodash to >= 4.17.21: npm install lodash@latest\"\n }],\n \"axios\": [{\n minSafe: \"1.6.0\", cve: \"CVE-2023-45857\", severity: \"high\",\n title: \"Axios CSRF Vulnerability\",\n description: \"axios before 1.6.0 inadvertently leaks XSRF-TOKEN cookie in cross-site requests.\",\n fix: \"Upgrade axios to >= 1.6.0: npm install axios@latest\"\n }],\n \"express\": [{\n minSafe: \"4.19.2\", cve: \"CVE-2024-29041\", severity: \"medium\",\n title: \"Express Open Redirect\",\n description: \"Express before 4.19.2 is vulnerable to open redirect via crafted URLs.\",\n fix: \"Upgrade express to >= 4.19.2: npm install express@latest\"\n }],\n \"jsonwebtoken\": [{\n minSafe: \"9.0.0\", cve: \"CVE-2022-23529\", severity: \"high\",\n title: \"jsonwebtoken Insecure Key Handling\",\n description: \"jsonwebtoken before 9.0.0 allows attackers to set secretOrPublicKey to a malicious object.\",\n fix: \"Upgrade jsonwebtoken to >= 9.0.0: npm install jsonwebtoken@latest\"\n }],\n \"node-fetch\": [{\n minSafe: \"2.6.7\", cve: \"CVE-2022-0235\", severity: \"high\",\n title: \"node-fetch Header Exposure\",\n description: \"node-fetch before 2.6.7 exposes authorization headers on redirect to different origin.\",\n fix: \"Upgrade node-fetch to >= 2.6.7 or switch to native fetch (Node 18+).\"\n }],\n \"minimatch\": [{\n minSafe: \"3.0.5\", cve: \"CVE-2022-3517\", severity: \"high\",\n title: \"Minimatch ReDoS\",\n description: \"minimatch before 3.0.5 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade minimatch to >= 3.0.5: npm install minimatch@latest\"\n }],\n \"moment\": [{\n minSafe: \"999.0.0\", cve: \"N/A\", severity: \"medium\",\n title: \"Moment.js is Deprecated\",\n description: \"moment.js is in maintenance mode with known path traversal issues. Consider alternatives.\",\n fix: \"Migrate to date-fns, dayjs, or Temporal API. See: https://momentjs.com/docs/#/-project-status/\"\n }],\n \"request\": [{\n minSafe: \"999.0.0\", cve: \"N/A\", severity: \"medium\",\n title: \"Request Package Deprecated\",\n description: \"The 'request' package is deprecated and no longer receives security updates.\",\n fix: \"Migrate to node-fetch, axios, got, or native fetch (Node 18+).\"\n }],\n \"underscore\": [{\n minSafe: \"1.13.6\", cve: \"CVE-2021-23358\", severity: \"high\",\n title: \"Underscore.js Arbitrary Code Execution\",\n description: \"underscore before 1.13.6 allows arbitrary code execution via the template function.\",\n fix: \"Upgrade underscore to >= 1.13.6 or migrate to lodash.\"\n }],\n \"tar\": [{\n minSafe: \"6.2.1\", cve: \"CVE-2024-28863\", severity: \"high\",\n title: \"Tar Path Traversal\",\n description: \"tar before 6.2.1 is vulnerable to denial of service via crafted archives.\",\n fix: \"Upgrade tar to >= 6.2.1: npm install tar@latest\"\n }],\n \"semver\": [{\n minSafe: \"7.5.2\", cve: \"CVE-2022-25883\", severity: \"medium\",\n title: \"Semver ReDoS\",\n description: \"semver before 7.5.2 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade semver to >= 7.5.2: npm install semver@latest\"\n }],\n \"xml2js\": [{\n minSafe: \"0.5.0\", cve: \"CVE-2023-0842\", severity: \"medium\",\n title: \"xml2js Prototype Pollution\",\n description: \"xml2js before 0.5.0 is vulnerable to prototype pollution when parsing XML.\",\n fix: \"Upgrade xml2js to >= 0.5.0: npm install xml2js@latest\"\n }],\n};\n\n// Known vulnerable Python packages\nconst KNOWN_VULN_PYTHON: Record<string, VulnEntry[]> = {\n \"django\": [{\n minSafe: \"4.2.11\", cve: \"CVE-2024-27351\", severity: \"high\",\n title: \"Django ReDoS in Truncator\",\n description: \"Django before 4.2.11 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade Django to >= 4.2.11: pip install Django --upgrade\"\n }],\n \"flask\": [{\n minSafe: \"2.3.2\", cve: \"CVE-2023-30861\", severity: \"high\",\n title: \"Flask Session Cookie Vulnerability\",\n description: \"Flask before 2.3.2 may set permanent session cookies on redirect responses.\",\n fix: \"Upgrade Flask to >= 2.3.2: pip install Flask --upgrade\"\n }],\n \"pillow\": [{\n minSafe: \"10.2.0\", cve: \"CVE-2023-50447\", severity: \"critical\",\n title: \"Pillow Arbitrary Code Execution\",\n description: \"Pillow before 10.2.0 is vulnerable to arbitrary code execution via crafted images.\",\n fix: \"Upgrade Pillow to >= 10.2.0: pip install Pillow --upgrade\"\n }],\n \"requests\": [{\n minSafe: \"2.31.0\", cve: \"CVE-2023-32681\", severity: \"medium\",\n title: \"Requests Proxy Header Leak\",\n description: \"requests before 2.31.0 leaks Proxy-Authorization headers to destination servers.\",\n fix: \"Upgrade requests to >= 2.31.0: pip install requests --upgrade\"\n }],\n \"pyyaml\": [{\n minSafe: \"6.0.1\", cve: \"CVE-2022-41316\", severity: \"high\",\n title: \"PyYAML Unsafe Loading\",\n description: \"PyYAML has known deserialization vulnerabilities. Always use yaml.safe_load().\",\n fix: \"Upgrade PyYAML to >= 6.0.1 and use yaml.safe_load() instead of yaml.load().\"\n }],\n \"jinja2\": [{\n minSafe: \"3.1.3\", cve: \"CVE-2024-22195\", severity: \"medium\",\n title: \"Jinja2 XSS Vulnerability\",\n description: \"Jinja2 before 3.1.3 is vulnerable to XSS via xmlattr filter.\",\n fix: \"Upgrade Jinja2 to >= 3.1.3: pip install Jinja2 --upgrade\"\n }],\n};\n\n// Risky patterns in dependency configs\ninterface DepPattern {\n id: string;\n title: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n description: string;\n fix: string;\n test: (content: string, filePath: string) => boolean;\n}\n\nconst DEP_PATTERNS: DepPattern[] = [\n {\n id: \"DEP001\", title: \"Wildcard Dependency Version\", severity: \"high\",\n description: \"Using '*' or 'latest' as a dependency version allows any version to be installed, including malicious ones.\",\n fix: \"Pin dependency versions: use exact (1.2.3) or caret (^1.2.3) versions, never '*' or 'latest'.\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /[\"']\\*[\"']|[\"']latest[\"']/.test(content),\n },\n {\n id: \"DEP002\", title: \"Git Dependency Without Commit Hash\", severity: \"medium\",\n description: \"Git dependencies without a pinned commit hash can be replaced with malicious code.\",\n fix: \"Pin git dependencies to a specific commit: git+https://github.com/user/repo#commit-hash\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /git\\+https?:\\/\\/[^#\"]+[\"']/.test(content),\n },\n {\n id: \"DEP003\", title: \"Postinstall Script in Package\", severity: \"medium\",\n description: \"postinstall scripts run automatically after npm install and can execute arbitrary code.\",\n fix: \"Audit postinstall scripts carefully. Use --ignore-scripts flag for untrusted packages.\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /\"postinstall\"\\s*:/.test(content),\n },\n {\n id: \"DEP004\", title: \"No Package Lock File\", severity: \"medium\",\n description: \"Without a lockfile, npm install may resolve different versions on different machines.\",\n fix: \"Commit your lockfile (package-lock.json, pnpm-lock.yaml, or yarn.lock).\",\n test: (content, filePath) => filePath.endsWith(\".gitignore\") && /package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock/.test(content),\n },\n {\n id: \"DEP005\", title: \"HTTP Registry URL\", severity: \"high\",\n description: \"Using HTTP (not HTTPS) for npm registry allows man-in-the-middle attacks on package downloads.\",\n fix: \"Use HTTPS for registry: registry=https://registry.npmjs.org/\",\n test: (content, filePath) => filePath.endsWith(\".npmrc\") && /registry\\s*=\\s*http:\\/\\//.test(content),\n },\n];\n\nfunction compareVersions(v1: string, v2: string): number {\n const parts1 = v1.replace(/^[^0-9]*/, \"\").split(\".\").map(Number);\n const parts2 = v2.replace(/^[^0-9]*/, \"\").split(\".\").map(Number);\n for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n const a = parts1[i] || 0;\n const b = parts2[i] || 0;\n if (a < b) return -1;\n if (a > b) return 1;\n }\n return 0;\n}\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\nexport function scanDependencies(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n // npm package.json\n if (filePath.endsWith(\"package.json\") && !filePath.includes(\"node_modules\")) {\n try {\n const pkg = JSON.parse(content);\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n for (const [name, version] of Object.entries(allDeps)) {\n const vulns = KNOWN_VULN_NPM[name];\n if (!vulns) continue;\n\n const versionStr = String(version).replace(/^[\\^~>=<]*/g, \"\");\n for (const vuln of vulns) {\n if (compareVersions(versionStr, vuln.minSafe) < 0) {\n const line = content.split(\"\\n\").findIndex(l => l.includes(`\"${name}\"`)) + 1;\n findings.push({\n id: `${vuln.cve}-${filePath}:${line}`,\n rule: vuln.cve,\n severity: vuln.severity,\n title: vuln.title,\n description: vuln.description,\n file: filePath,\n line,\n snippet: getSnippet(content, line),\n fix: vuln.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Python requirements.txt\n if (filePath.match(/requirements.*\\.txt$/i)) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n const match = line.match(/^([a-zA-Z0-9_-]+)(?:[=<>!~]+(.+))?/);\n if (!match) continue;\n\n const name = match[1].toLowerCase();\n const version = match[2] || \"0.0.0\";\n const vulns = KNOWN_VULN_PYTHON[name];\n if (!vulns) continue;\n\n for (const vuln of vulns) {\n if (compareVersions(version, vuln.minSafe) < 0) {\n findings.push({\n id: `${vuln.cve}-${filePath}:${i + 1}`,\n rule: vuln.cve,\n severity: vuln.severity,\n title: vuln.title,\n description: vuln.description,\n file: filePath,\n line: i + 1,\n snippet: getSnippet(content, i + 1),\n fix: vuln.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n }\n\n // Dependency pattern checks\n for (const pattern of DEP_PATTERNS) {\n if (pattern.test(content, filePath)) {\n findings.push({\n id: `${pattern.id}-${filePath}:1`,\n rule: pattern.id,\n severity: pattern.severity,\n title: pattern.title,\n description: pattern.description,\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: pattern.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Async companion to scanDependencies() that augments the hand-curated\n * allowlist with a live OSV.dev lookup of every package version. Fails\n * soft on network error — offline scans still run the static checks.\n *\n * Returns ONLY the extra findings from OSV so callers can merge with the\n * sync results without double-counting:\n * const findings = [...scanDependencies(files), ...await scanDependenciesOsv(files)];\n *\n * Deduplication is by (package name + CVE id) so a package already caught\n * by the static allowlist doesn't show up twice.\n */\nexport async function scanDependenciesOsv(\n files: { path: string; content: string }[],\n alreadyFoundByRule: Set<string>,\n): Promise<Finding[]> {\n interface PendingLookup {\n ecosystem: \"npm\" | \"PyPI\" | \"RubyGems\" | \"Go\";\n name: string;\n version: string;\n file: string;\n line: number;\n content: string;\n }\n\n const lookups: PendingLookup[] = [];\n\n for (const { path: filePath, content } of files) {\n // npm package.json\n if (filePath.endsWith(\"package.json\") && !filePath.includes(\"node_modules\")) {\n try {\n const pkg = JSON.parse(content);\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n for (const [name, rawVersion] of Object.entries(allDeps)) {\n const version = String(rawVersion).replace(/^[\\^~>=<]*/g, \"\").trim();\n if (!version || version === \"*\" || version === \"latest\") continue;\n const line = content.split(\"\\n\").findIndex((l) => l.includes(`\"${name}\"`)) + 1;\n lookups.push({ ecosystem: \"npm\", name, version, file: filePath, line: line || 1, content });\n }\n } catch {\n /* invalid JSON */\n }\n }\n\n // Python requirements.txt (exact version pins only — ranges are tricky\n // without a resolver and we'd rather report nothing than guess wrong)\n if (filePath.match(/requirements.*\\.txt$/i)) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].trim().match(/^([a-zA-Z0-9_-]+)==([\\d.]+)/);\n if (!m) continue;\n lookups.push({\n ecosystem: \"PyPI\",\n name: m[1].toLowerCase(),\n version: m[2],\n file: filePath,\n line: i + 1,\n content,\n });\n }\n }\n\n // Ruby Gemfile.lock (exact pins)\n if (filePath.endsWith(\"Gemfile.lock\")) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(/^\\s{4}([a-z0-9_-]+)\\s+\\(([\\d.]+)\\)/);\n if (!m) continue;\n lookups.push({\n ecosystem: \"RubyGems\",\n name: m[1],\n version: m[2],\n file: filePath,\n line: i + 1,\n content,\n });\n }\n }\n }\n\n if (lookups.length === 0) return [];\n\n // Chunk into batches of 500 to stay well under OSV's 1000-query limit\n const CHUNK = 500;\n const findings: Finding[] = [];\n for (let start = 0; start < lookups.length; start += CHUNK) {\n const chunk = lookups.slice(start, start + CHUNK);\n const matches = await queryOsvBatch(\n chunk.map((l) => ({ package: { name: l.name, ecosystem: l.ecosystem }, version: l.version })),\n );\n // Zip matches back to their lookup rows by index within the chunk\n const matchByKey = new Map<string, (typeof matches)[0]>();\n for (const m of matches) matchByKey.set(`${m.ecosystem}:${m.name}:${m.version}`, m);\n\n for (const lookup of chunk) {\n const key = `${lookup.ecosystem}:${lookup.name}:${lookup.version}`;\n const match = matchByKey.get(key);\n if (!match) continue;\n for (const vuln of match.vulns) {\n // Skip if the static allowlist already reported this CVE for this package\n const dedupeKey = `${lookup.name}:${vuln.id}`;\n if (alreadyFoundByRule.has(dedupeKey)) continue;\n alreadyFoundByRule.add(dedupeKey);\n\n const severity = osvSeverity(vuln);\n const title = vuln.summary || `${lookup.name} vulnerability ${vuln.id}`;\n const description =\n vuln.details ||\n vuln.summary ||\n `${lookup.name}@${lookup.version} is affected by ${vuln.id}. See https://osv.dev/vulnerability/${vuln.id} for details.`;\n findings.push({\n id: `${vuln.id}-${lookup.file}:${lookup.line}`,\n rule: vuln.id,\n severity,\n title,\n description: description.slice(0, 500),\n file: lookup.file,\n line: lookup.line,\n snippet: getSnippet(lookup.content, lookup.line),\n fix: `Upgrade ${lookup.name} to a version that resolves ${vuln.id}. See https://osv.dev/vulnerability/${vuln.id}`,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n\n return findings;\n}\n","/**\n * Configuration File Deep Analyzer\n * Analyzes tsconfig, next.config, Dockerfile, GitHub Actions,\n * and other config files for security misconfigurations.\n */\n\nimport type { Finding } from \"../types.js\";\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\ninterface ConfigCheck {\n id: string;\n title: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n category: string;\n description: string;\n fix: string;\n owasp: string;\n cwe: string;\n filePattern: RegExp;\n check: (content: string, filePath: string) => { line: number; snippet: string } | null;\n}\n\nconst CONFIG_CHECKS: ConfigCheck[] = [\n // ── TypeScript Config ──\n {\n id: \"CFG001\", title: \"TypeScript Strict Mode Disabled\", severity: \"medium\",\n category: \"Configuration\", description: \"TypeScript without strict mode misses type errors that can cause runtime bugs and security issues.\",\n fix: 'Enable strict mode in tsconfig.json: \"strict\": true',\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /tsconfig\\.json$/,\n check(content) {\n try {\n if (/\"strict\"\\s*:\\s*false/.test(content)) {\n const line = content.split(\"\\n\").findIndex(l => /strict/.test(l)) + 1;\n return { line, snippet: getSnippet(content, line) };\n }\n if (!/\"strict\"\\s*:\\s*true/.test(content) && /\"compilerOptions\"/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n } catch {}\n return null;\n },\n },\n {\n id: \"CFG002\", title: \"TypeScript allowJs Without checkJs\", severity: \"low\",\n category: \"Configuration\", description: \"allowJs without checkJs means JavaScript files bypass type checking entirely.\",\n fix: 'Add \"checkJs\": true alongside \"allowJs\": true in tsconfig.json.',\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /tsconfig\\.json$/,\n check(content) {\n if (/\"allowJs\"\\s*:\\s*true/.test(content) && !/\"checkJs\"\\s*:\\s*true/.test(content)) {\n const line = content.split(\"\\n\").findIndex(l => /allowJs/.test(l)) + 1;\n return { line, snippet: getSnippet(content, line) };\n }\n return null;\n },\n },\n\n // ── Next.js Config ──\n {\n id: \"CFG003\", title: \"Next.js Missing Security Headers\", severity: \"medium\",\n category: \"Configuration\", description: \"Next.js app without security headers in next.config is missing important protections.\",\n fix: \"Add a headers() function to next.config.js with X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Content-Security-Policy.\",\n owasp: \"A05:2021\", cwe: \"CWE-693\",\n filePattern: /next\\.config\\.(js|mjs|ts)$/,\n check(content) {\n if (!/headers\\s*\\(/.test(content) && !/securityHeaders|security-headers/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n {\n id: \"CFG004\", title: \"Next.js Powered-By Header Not Disabled\", severity: \"low\",\n category: \"Configuration\", description: \"Next.js exposes X-Powered-By header by default, revealing your tech stack.\",\n fix: 'Add poweredByHeader: false to next.config.js.',\n owasp: \"A05:2021\", cwe: \"CWE-200\",\n filePattern: /next\\.config\\.(js|mjs|ts)$/,\n check(content) {\n if (!/poweredByHeader\\s*:\\s*false/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n\n // ── Dockerfile ──\n {\n id: \"CFG005\", title: \"Dockerfile Using Latest Tag\", severity: \"medium\",\n category: \"Configuration\", description: \"Using :latest tag in FROM makes builds non-reproducible and may pull vulnerable images.\",\n fix: \"Pin to a specific version: FROM node:20-alpine instead of FROM node:latest.\",\n owasp: \"A06:2021\", cwe: \"CWE-1395\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/^FROM\\s+\\S+:latest/i.test(lines[i].trim()) || /^FROM\\s+\\w+\\s*$/i.test(lines[i].trim())) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n {\n id: \"CFG006\", title: \"Dockerfile Not Using Multi-Stage Build\", severity: \"low\",\n category: \"Configuration\", description: \"Single-stage Docker builds include build tools in the final image, increasing attack surface.\",\n fix: \"Use multi-stage builds: first stage for building, second stage (FROM alpine) for running.\",\n owasp: \"A05:2021\", cwe: \"CWE-1059\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const fromCount = (content.match(/^FROM\\s/gmi) || []).length;\n if (fromCount <= 1 && content.includes(\"npm\") && content.length > 200) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n {\n id: \"CFG007\", title: \"Dockerfile Copies .env File\", severity: \"critical\",\n category: \"Configuration\", description: \"COPY that includes .env files bakes secrets into the Docker image layer.\",\n fix: \"Add .env to .dockerignore. Use build args or runtime env vars instead.\",\n owasp: \"A02:2021\", cwe: \"CWE-312\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/^COPY\\s.*\\.env\\b/i.test(lines[i].trim())) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── GitHub Actions ──\n {\n id: \"CFG008\", title: \"GitHub Actions with Broad Permissions\", severity: \"high\",\n category: \"Configuration\", description: \"GitHub Actions with write-all or broad permissions can be exploited if a dependency is compromised.\",\n fix: \"Use minimal permissions: permissions: { contents: read }. Only grant what's needed.\",\n owasp: \"A01:2021\", cwe: \"CWE-250\",\n filePattern: /\\.github\\/workflows\\/.*\\.(yml|yaml)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/permissions\\s*:\\s*write-all/i.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n {\n id: \"CFG009\", title: \"GitHub Actions Using Unpinned Action\", severity: \"medium\",\n category: \"Configuration\", description: \"Using actions with @main or @master instead of a pinned SHA allows supply chain attacks.\",\n fix: \"Pin actions to a specific SHA: uses: actions/checkout@abc123... instead of @main.\",\n owasp: \"A06:2021\", cwe: \"CWE-1395\",\n filePattern: /\\.github\\/workflows\\/.*\\.(yml|yaml)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/uses:\\s*\\S+@(?:main|master|dev|latest)\\s*$/i.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── Vite Config ──\n {\n id: \"CFG010\", title: \"Vite Server Open to Network\", severity: \"medium\",\n category: \"Configuration\", description: \"Vite dev server with host: true exposes it to the entire network.\",\n fix: \"Remove host: true or use host: '127.0.0.1' for local-only access during development.\",\n owasp: \"A05:2021\", cwe: \"CWE-668\",\n filePattern: /vite\\.config\\.(js|ts|mjs)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/host\\s*:\\s*true/.test(lines[i]) || /host\\s*:\\s*['\"]0\\.0\\.0\\.0['\"]/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── .npmrc ──\n {\n id: \"CFG011\", title: \"NPM Auth Token in .npmrc\", severity: \"critical\",\n category: \"Secrets\", description: \"NPM auth tokens in committed .npmrc files expose registry credentials.\",\n fix: \"Remove the token and add .npmrc to .gitignore. Use NPM_TOKEN env var instead.\",\n owasp: \"A02:2021\", cwe: \"CWE-798\",\n filePattern: /\\.npmrc$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/_authToken|_auth=|\\/\\/registry.*:_password/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── ESLint Config ──\n {\n id: \"CFG012\", title: \"Security ESLint Rules Disabled\", severity: \"medium\",\n category: \"Configuration\", description: \"Disabling security-related ESLint rules removes an important safety net.\",\n fix: \"Re-enable security rules or use eslint-plugin-security for automated checks.\",\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /\\.eslint(rc)?(\\.(js|json|yml|yaml|cjs|mjs))?$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/no-eval|no-implied-eval|no-new-func|no-script-url|security\\/detect/i.test(lines[i]) && /[\"']off[\"']|:\\s*0/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n];\n\nexport function scanConfigs(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n for (const check of CONFIG_CHECKS) {\n if (!check.filePattern.test(filePath)) continue;\n\n const result = check.check(content, filePath);\n if (result) {\n findings.push({\n id: `${check.id}-${filePath}:${result.line}`,\n rule: check.id,\n severity: check.severity,\n title: check.title,\n description: check.description,\n file: filePath,\n line: result.line,\n snippet: result.snippet,\n fix: check.fix,\n category: check.category,\n source: \"custom\",\n owasp: check.owasp,\n cwe: check.cwe,\n });\n }\n }\n }\n\n return findings;\n}\n","/**\n * Multi-File Analysis Scanner\n * Analyzes relationships between files to detect cross-file vulnerabilities\n * that single-file scanners miss.\n */\n\nimport type { Finding } from \"../types.js\";\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\ninterface FileInfo {\n path: string;\n content: string;\n}\n\nexport function scanMultiFile(files: FileInfo[]): Finding[] {\n const findings: Finding[] = [];\n\n // Build indexes\n const fileMap = new Map<string, string>();\n for (const f of files) {\n fileMap.set(f.path, f.content);\n }\n\n // ── Check 1: API routes without middleware protection ──\n // If there's a middleware file that handles auth, routes are protected.\n // But if routes exist WITHOUT middleware, they're exposed.\n const hasGlobalMiddleware = files.some(f =>\n /middleware\\.(ts|js)$/.test(f.path) &&\n /(?:auth|session|clerk|nextauth|getToken)/i.test(f.content)\n );\n const hasAuthModule = files.some(f =>\n /(?:auth|middleware)\\.(ts|js)$/i.test(f.path) &&\n /(?:requireAuth|isAuthenticated|authenticate|verify)/i.test(f.content)\n );\n\n // Check API route files for auth\n for (const f of files) {\n if (!/(?:\\/api\\/|routes?\\/|controllers?\\/)/i.test(f.path)) continue;\n if (/middleware/i.test(f.path)) continue;\n\n // Skip health/status endpoints\n if (/(?:health|status|ping|ready|live)\\.(?:ts|js)/i.test(f.path)) continue;\n\n const hasLocalAuth = /(?:requireAuth|isAuthenticated|authenticate|verify|getAuth|getSession|getServerSession|withAuth|jwt\\.verify|clerk)/i.test(f.content);\n const hasAuthImport = /import.*(?:auth|session|middleware|verify|clerk)/i.test(f.content);\n\n if (!hasLocalAuth && !hasAuthImport && !hasGlobalMiddleware) {\n // Only flag if the file has actual route handlers\n if (/\\.(get|post|put|delete|patch)\\s*\\(|export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(f.content)) {\n const line = f.content.split(\"\\n\").findIndex(l =>\n /\\.(get|post|put|delete|patch)\\s*\\(|export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(l)\n ) + 1;\n findings.push({\n id: `MFA001-${f.path}:${line}`,\n rule: \"MFA001\",\n severity: \"high\",\n title: \"API Route Without Auth (Cross-File Check)\",\n description: \"This API route file has no authentication checks and no auth middleware was detected in the project.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Add authentication middleware or import your auth module. If using Next.js, create middleware.ts in your app root.\",\n category: \"Authentication\",\n source: \"custom\",\n owasp: \"A01:2021\",\n cwe: \"CWE-862\",\n });\n }\n }\n }\n\n // ── Check 2: .env file exists but .gitignore doesn't exclude it ──\n const hasEnvFile = files.some(f => /^\\.env$|\\/\\.env$/.test(f.path));\n const gitignore = files.find(f => f.path.endsWith(\".gitignore\"));\n if (hasEnvFile && gitignore) {\n if (!/^\\.env$/m.test(gitignore.content) && !/^\\*\\.env$/m.test(gitignore.content)) {\n findings.push({\n id: \"MFA002-.gitignore:1\",\n rule: \"MFA002\",\n severity: \"critical\",\n title: \".env File Not in .gitignore\",\n description: \"An .env file exists in the project but .gitignore doesn't exclude it. Secrets may be committed.\",\n file: \".gitignore\",\n line: 1,\n snippet: getSnippet(gitignore.content, 1),\n fix: \"Add .env to .gitignore: echo '.env' >> .gitignore\",\n category: \"Secrets\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-312\",\n });\n }\n }\n\n // ── Check 3: Database client without connection pooling ──\n const dbFiles = files.filter(f =>\n /(?:db|database|prisma|drizzle|sequelize|mongoose|knex)\\.(ts|js)$/i.test(f.path) ||\n /(?:createClient|createPool|createConnection|PrismaClient)/i.test(f.content)\n );\n for (const f of dbFiles) {\n // Check if DB client is created at module level (not in a function)\n if (/new PrismaClient/i.test(f.content)) {\n // Good pattern: globalThis.prisma = globalThis.prisma || new PrismaClient()\n if (!/globalThis|global\\./i.test(f.content)) {\n const line = f.content.split(\"\\n\").findIndex(l => /new PrismaClient/.test(l)) + 1;\n findings.push({\n id: `MFA003-${f.path}:${line}`,\n rule: \"MFA003\",\n severity: \"medium\",\n title: \"Database Client Not Cached (Connection Leak)\",\n description: \"Creating a new PrismaClient on every request leaks database connections. Use a singleton pattern.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use singleton: globalThis.prisma = globalThis.prisma || new PrismaClient(). This prevents connection exhaustion in serverless.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-400\",\n });\n }\n }\n }\n\n // ── Check 4: CORS origin mismatch ──\n // Check if CORS is configured but the origin doesn't match APP_URL or frontend URL\n for (const f of files) {\n if (!/(?:server|app|index|main)\\.(ts|js)$/i.test(f.path)) continue;\n const corsMatch = f.content.match(/origin\\s*:\\s*[\"'`](https?:\\/\\/[^\"'`]+)[\"'`]/);\n if (corsMatch) {\n const corsOrigin = corsMatch[1];\n if (corsOrigin.includes(\"localhost\") || corsOrigin.includes(\"127.0.0.1\")) {\n const line = f.content.split(\"\\n\").findIndex(l => l.includes(corsOrigin)) + 1;\n findings.push({\n id: `MFA004-${f.path}:${line}`,\n rule: \"MFA004\",\n severity: \"medium\",\n title: \"CORS Origin Set to Localhost\",\n description: \"CORS origin is hardcoded to localhost. This will block requests from your production frontend.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use environment variable: origin: process.env.FRONTEND_URL || 'http://localhost:3000'\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-942\",\n });\n }\n }\n }\n\n // ── Check 5: Secret referenced but not in .env.example ──\n const envExample = files.find(f => /\\.env\\.example$|\\.env\\.sample$|\\.env\\.template$/.test(f.path));\n if (envExample) {\n const envVarsUsed = new Set<string>();\n for (const f of files) {\n if (/\\.(ts|js|tsx|jsx)$/i.test(f.path)) {\n const matches = f.content.matchAll(/process\\.env\\.([A-Z_][A-Z0-9_]*)/g);\n for (const m of matches) {\n envVarsUsed.add(m[1]);\n }\n }\n }\n const envVarsDocumented = new Set<string>();\n const exampleMatches = envExample.content.matchAll(/^([A-Z_][A-Z0-9_]*)=/gm);\n for (const m of exampleMatches) {\n envVarsDocumented.add(m[1]);\n }\n\n const undocumented = [...envVarsUsed].filter(v => !envVarsDocumented.has(v) && !v.startsWith(\"NODE_\") && v !== \"npm_\");\n if (undocumented.length > 0) {\n findings.push({\n id: `MFA005-${envExample.path}:1`,\n rule: \"MFA005\",\n severity: \"low\",\n title: \"Env Vars Used But Not in .env.example\",\n description: `These environment variables are used in code but missing from .env.example: ${undocumented.slice(0, 5).join(\", \")}${undocumented.length > 5 ? ` (+${undocumented.length - 5} more)` : \"\"}`,\n file: envExample.path,\n line: 1,\n snippet: getSnippet(envExample.content, 1),\n fix: \"Add missing variables to .env.example so team members know which env vars are needed.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-1188\",\n });\n }\n }\n\n // ── Check 6: Frontend fetching HTTP API in production code ──\n for (const f of files) {\n if (!/\\.(tsx|jsx|ts|js)$/i.test(f.path)) continue;\n if (/(?:test|spec|mock|fixture)/i.test(f.path)) continue;\n\n const httpFetches = f.content.matchAll(/fetch\\s*\\(\\s*[\"'`](http:\\/\\/(?!localhost|127\\.0\\.0\\.1)[^\"'`]+)[\"'`]/g);\n for (const m of httpFetches) {\n const line = f.content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n id: `MFA006-${f.path}:${line}`,\n rule: \"MFA006\",\n severity: \"high\",\n title: \"Frontend Fetching Over HTTP (Not HTTPS)\",\n description: `Fetching from ${m[1].substring(0, 40)}... over HTTP exposes data to interception.`,\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use HTTPS for all API calls in production.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-319\",\n });\n }\n }\n\n return findings;\n}\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { Finding, ScanResult, Severity } from \"../types.js\";\nimport { calculateGrade, detectFramework, type SecurityGrade } from \"../scanners/custom-rules.js\";\n\nconst SEVERITY_COLORS: Record<Severity, (text: string) => string> = {\n critical: chalk.bgRed.white.bold,\n high: chalk.red.bold,\n medium: chalk.yellow.bold,\n low: chalk.blue,\n info: chalk.gray,\n};\n\nconst SEVERITY_ICONS: Record<Severity, string> = {\n critical: \"!!!\",\n high: \" !! \",\n medium: \" ! \",\n low: \" - \",\n info: \" i \",\n};\n\nconst SEVERITY_ORDER: Record<Severity, number> = {\n critical: 0,\n high: 1,\n medium: 2,\n low: 3,\n info: 4,\n};\n\nconst GRADE_COLORS: Record<SecurityGrade, (text: string) => string> = {\n \"A+\": chalk.green.bold,\n \"A\": chalk.green.bold,\n \"B\": chalk.cyan.bold,\n \"C\": chalk.yellow.bold,\n \"D\": chalk.red.bold,\n \"F\": chalk.bgRed.white.bold,\n};\n\nexport function renderTerminalReport(result: ScanResult, files?: { path: string; content: string }[]): void {\n const { findings, filesScanned, duration } = result;\n\n // Header\n console.log(\"\");\n console.log(chalk.bold.cyan(\" xploitscan\") + chalk.gray(\" — security scan results\"));\n console.log(chalk.gray(\" \" + \"─\".repeat(50)));\n console.log(\"\");\n\n // Framework detection\n if (files && files.length > 0) {\n const frameworks = detectFramework(files);\n if (frameworks.length > 0 && frameworks[0] !== \"unknown\") {\n console.log(chalk.gray(\" Frameworks: \") + chalk.white(frameworks.join(\", \")));\n }\n }\n\n // Grade + Benchmark\n const GRADE_PERCENTILES: Record<string, number> = { \"A+\": 98, \"A\": 90, \"B\": 70, \"C\": 45, \"D\": 20, \"F\": 5 };\n const { grade, score, summary } = calculateGrade(findings, filesScanned);\n const gradeColor = GRADE_COLORS[grade];\n const percentile = GRADE_PERCENTILES[grade] ?? 50;\n console.log(chalk.gray(\" Security Grade: \") + gradeColor(` ${grade} `) + chalk.gray(` (${score}/100) — ${summary}`));\n console.log(chalk.gray(` Benchmark: More secure than ${percentile}% of projects scanned`));\n console.log(\"\");\n\n if (findings.length === 0) {\n console.log(chalk.green.bold(\" No vulnerabilities found!\"));\n console.log(chalk.gray(` Scanned ${filesScanned} files in ${(duration / 1000).toFixed(1)}s`));\n console.log(\"\");\n return;\n }\n\n // Sort by severity\n const sorted = [...findings].sort(\n (a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity],\n );\n\n // Summary table\n const counts: Record<Severity, number> = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };\n for (const f of findings) counts[f.severity]++;\n\n const summaryParts: string[] = [];\n if (counts.critical > 0) summaryParts.push(chalk.bgRed.white.bold(` ${counts.critical} CRITICAL `));\n if (counts.high > 0) summaryParts.push(chalk.red.bold(`${counts.high} high`));\n if (counts.medium > 0) summaryParts.push(chalk.yellow.bold(`${counts.medium} medium`));\n if (counts.low > 0) summaryParts.push(chalk.blue(`${counts.low} low`));\n if (counts.info > 0) summaryParts.push(chalk.gray(`${counts.info} info`));\n\n console.log(` Found ${chalk.bold(findings.length.toString())} issues: ${summaryParts.join(chalk.gray(\" | \"))}`);\n console.log(chalk.gray(` Scanned ${filesScanned} files in ${(duration / 1000).toFixed(1)}s`));\n console.log(\"\");\n\n // Individual findings\n for (const finding of sorted) {\n const severityLabel = SEVERITY_COLORS[finding.severity](\n ` ${finding.severity.toUpperCase()} `,\n );\n const sourceLabel = finding.source === \"ai\"\n ? chalk.magenta(\" [AI] \")\n : finding.source === \"dependency\"\n ? chalk.cyan(\" [DEP] \")\n : finding.source === \"entropy\"\n ? chalk.yellow(\" [ENTROPY] \")\n : finding.source === \"config\"\n ? chalk.blue(\" [CFG] \")\n : finding.source === \"multi-file\"\n ? chalk.green(\" [CROSS-FILE] \")\n : chalk.gray(` [${finding.rule}] `);\n const confidenceLabel = finding.confidence\n ? chalk.gray(` (${finding.confidence} confidence)`)\n : \"\";\n\n const complianceTags = [\n finding.owasp ? chalk.yellow(`[${finding.owasp}]`) : \"\",\n finding.cwe ? chalk.blue(`[${finding.cwe}]`) : \"\",\n ].filter(Boolean).join(\" \");\n console.log(` ${severityLabel}${sourceLabel}${chalk.bold(finding.title)} ${complianceTags}${confidenceLabel}`);\n console.log(chalk.gray(` ${finding.file}:${finding.line}`));\n console.log(\"\");\n\n // Description\n console.log(chalk.white(` ${finding.description}`));\n console.log(\"\");\n\n // Code snippet\n if (finding.snippet) {\n const snippetLines = finding.snippet.split(\"\\n\");\n for (const line of snippetLines) {\n if (line.startsWith(\">\")) {\n console.log(chalk.red(` ${line}`));\n } else {\n console.log(chalk.gray(` ${line}`));\n }\n }\n console.log(\"\");\n }\n\n // Fix suggestion\n if (finding.fix) {\n console.log(chalk.green(` Fix: ${finding.fix}`));\n console.log(\"\");\n }\n\n console.log(chalk.gray(\" \" + \"─\".repeat(50)));\n console.log(\"\");\n }\n\n // OWASP Top 10 Summary\n const owaspFindings = findings.filter(f => f.owasp);\n if (owaspFindings.length > 0) {\n const owaspCats: Record<string, { name: string; count: number }> = {\n \"A01:2021\": { name: \"Broken Access Control\", count: 0 },\n \"A02:2021\": { name: \"Cryptographic Failures\", count: 0 },\n \"A03:2021\": { name: \"Injection\", count: 0 },\n \"A04:2021\": { name: \"Insecure Design\", count: 0 },\n \"A05:2021\": { name: \"Security Misconfiguration\", count: 0 },\n \"A06:2021\": { name: \"Vulnerable Components\", count: 0 },\n \"A07:2021\": { name: \"Auth Failures\", count: 0 },\n \"A08:2021\": { name: \"Data Integrity\", count: 0 },\n \"A09:2021\": { name: \"Logging Failures\", count: 0 },\n \"A10:2021\": { name: \"SSRF\", count: 0 },\n };\n for (const f of owaspFindings) {\n if (f.owasp && owaspCats[f.owasp]) owaspCats[f.owasp].count++;\n }\n console.log(chalk.bold(\" OWASP Top 10 Compliance\"));\n for (const [id, { name, count }] of Object.entries(owaspCats)) {\n const status = count > 0 ? chalk.red(`${count} issue${count > 1 ? \"s\" : \"\"}`) : chalk.green(\"PASS\");\n console.log(chalk.gray(` ${id} ${name}: `) + status);\n }\n console.log(\"\");\n }\n\n // Footer\n if (counts.critical > 0) {\n console.log(\n chalk.bgRed.white.bold(\" ACTION REQUIRED \") +\n chalk.red.bold(` ${counts.critical} critical issue${counts.critical > 1 ? \"s\" : \"\"} found. Fix these before deploying.`),\n );\n } else if (counts.high > 0) {\n console.log(\n chalk.yellow.bold(` Recommendation: Address the ${counts.high} high-severity issue${counts.high > 1 ? \"s\" : \"\"} before going to production.`),\n );\n }\n\n console.log(\"\");\n}\n","import type { ScanResult } from \"../types.js\";\n\nexport function renderJsonReport(result: ScanResult): void {\n console.log(JSON.stringify(result, null, 2));\n}\n","import type { Finding, ScanResult, Severity } from \"../types.js\";\n\nconst SEVERITY_TO_SARIF: Record<Severity, string> = {\n critical: \"error\",\n high: \"error\",\n medium: \"warning\",\n low: \"note\",\n info: \"none\",\n};\n\nconst SEVERITY_TO_LEVEL: Record<Severity, number> = {\n critical: 10.0,\n high: 8.0,\n medium: 5.0,\n low: 3.0,\n info: 1.0,\n};\n\ninterface SarifOutput {\n $schema: string;\n version: string;\n runs: Array<{\n tool: {\n driver: {\n name: string;\n version: string;\n informationUri: string;\n rules: Array<{\n id: string;\n shortDescription: { text: string };\n fullDescription: { text: string };\n defaultConfiguration: { level: string };\n properties: { security_severity: string; tags: string[] };\n }>;\n };\n };\n results: Array<{\n ruleId: string;\n ruleIndex: number;\n level: string;\n message: { text: string };\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string };\n region: {\n startLine: number;\n startColumn?: number;\n };\n };\n }>;\n }>;\n }>;\n}\n\nexport function renderSarifReport(result: ScanResult): void {\n // Collect unique rules\n const ruleMap = new Map<string, Finding>();\n for (const f of result.findings) {\n if (!ruleMap.has(f.rule)) {\n ruleMap.set(f.rule, f);\n }\n }\n\n const rules = Array.from(ruleMap.entries()).map(([id, f]) => ({\n id,\n shortDescription: { text: f.title },\n fullDescription: { text: f.description },\n defaultConfiguration: { level: SEVERITY_TO_SARIF[f.severity] },\n properties: {\n security_severity: SEVERITY_TO_LEVEL[f.severity].toFixed(1),\n tags: [\"security\", f.category.toLowerCase().replace(/\\s+/g, \"-\")],\n },\n }));\n\n const ruleIndex = new Map(rules.map((r, i) => [r.id, i]));\n\n const sarif: SarifOutput = {\n $schema: \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"xploitscan\",\n version: \"0.1.0\",\n informationUri: \"https://github.com/bgage72590/xploitscan\",\n rules,\n },\n },\n results: result.findings.map((f) => {\n // SARIF schema requires that any `fixes[]` entry include an\n // `artifactChanges` array, which we don't have because findings\n // are pattern-based, not AST rewrites. Inline the fix text into\n // the message instead so GitHub still surfaces it.\n const messageText = f.fix\n ? `${f.title}: ${f.description}\\nSuggested fix: ${f.fix}`\n : `${f.title}: ${f.description}`;\n return {\n ruleId: f.rule,\n ruleIndex: ruleIndex.get(f.rule) ?? 0,\n level: SEVERITY_TO_SARIF[f.severity],\n message: { text: messageText },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: f.file },\n region: {\n startLine: f.line,\n ...(f.column ? { startColumn: f.column } : {}),\n },\n },\n },\n ],\n };\n }),\n },\n ],\n };\n\n console.log(JSON.stringify(sarif, null, 2));\n}\n","import type { ScanResult, Finding } from \"../types.js\";\n\n/**\n * SIEM-compatible output reporters.\n *\n * Most SIEMs can ingest plain JSON, but each platform expects a specific\n * schema so alerts, dashboards, and detections work out of the box. These\n * reporters emit one event per finding, each event shaped for the target\n * platform. All three are NDJSON (one JSON object per line) — the standard\n * wire format for log forwarders.\n *\n * Usage examples in the CLI:\n * xploitscan scan . --format splunk-hec | curl -H \"Authorization: Splunk <token>\" \\\n * --data-binary @- https://splunk.example.com:8088/services/collector/event\n *\n * xploitscan scan . --format elastic-ecs > findings.ndjson\n * # then: filebeat → Elasticsearch pipeline, or direct POST to _bulk\n *\n * xploitscan scan . --format datadog-logs | \\\n * curl -X POST -H \"DD-API-KEY: $DD_API_KEY\" \\\n * -H \"Content-Type: application/json\" \\\n * --data-binary @- https://http-intake.logs.datadoghq.com/api/v2/logs\n */\n\n// Severity mappings — each SIEM has its own canonical severity scale.\n// XploitScan's 4-level severity maps cleanly to most of them.\n\nconst SPLUNK_SEVERITY: Record<string, string> = {\n critical: \"critical\",\n high: \"high\",\n medium: \"medium\",\n low: \"low\",\n};\n\nconst ECS_SEVERITY: Record<string, number> = {\n // Elastic Common Schema uses 0–100 integer severity.\n critical: 90,\n high: 70,\n medium: 50,\n low: 30,\n};\n\nconst DATADOG_STATUS: Record<string, string> = {\n // Datadog Logs status field accepts emergency/alert/critical/error/warning/notice/info/debug.\n critical: \"critical\",\n high: \"error\",\n medium: \"warning\",\n low: \"info\",\n};\n\nfunction commonFields(result: ScanResult, finding: Finding) {\n return {\n rule: finding.rule,\n title: finding.title,\n description: finding.description,\n severity: finding.severity,\n file: finding.file,\n line: finding.line,\n category: finding.category,\n cwe: finding.cwe ?? null,\n owasp: finding.owasp ?? null,\n fix: finding.fix ?? null,\n scan_timestamp: result.timestamp,\n scan_directory: result.directory,\n files_scanned: result.filesScanned,\n scan_duration_ms: result.duration,\n };\n}\n\n// ────────────────────────────────────────────\n// Splunk HEC (HTTP Event Collector) format\n// ────────────────────────────────────────────\n\n/**\n * Splunk HEC event format:\n * { \"event\": { ...fields }, \"sourcetype\": \"xploitscan:finding\",\n * \"source\": \"xploitscan\", \"index\": \"security\", \"time\": <epoch> }\n *\n * One JSON object per line. POST the whole NDJSON body to the HEC\n * endpoint; Splunk will ingest each line as a separate event.\n */\nexport function renderSplunkReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n time: Math.floor(new Date(result.timestamp).getTime() / 1000),\n sourcetype: \"xploitscan:finding\",\n source: \"xploitscan\",\n event: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n\n// ────────────────────────────────────────────\n// Elastic Common Schema (ECS) — Elasticsearch / OpenSearch / Kibana\n// ────────────────────────────────────────────\n\n/**\n * ECS-compliant event. Uses the `vulnerability.*`, `event.*`, and\n * `@timestamp` fields Elastic's security apps already know how to render.\n * Compatible with Elastic Security and OpenSearch Security Analytics\n * without custom mappings.\n */\nexport function renderElasticReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n \"@timestamp\": result.timestamp,\n ecs: { version: \"8.11.0\" },\n event: {\n kind: \"alert\",\n category: [\"vulnerability\"],\n type: [\"info\"],\n dataset: \"xploitscan.finding\",\n module: \"xploitscan\",\n severity: ECS_SEVERITY[finding.severity] ?? 50,\n action: finding.rule,\n },\n vulnerability: {\n id: finding.rule,\n category: [finding.category],\n description: finding.description,\n severity: finding.severity,\n classification: finding.owasp ?? \"\",\n reference: finding.cwe\n ? `https://cwe.mitre.org/data/definitions/${finding.cwe.replace(/[^\\d]/g, \"\")}.html`\n : \"\",\n },\n file: { path: finding.file },\n log: { level: finding.severity },\n message: `${finding.rule}: ${finding.title}`,\n xploitscan: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n\n// ────────────────────────────────────────────\n// Datadog Logs API format\n// ────────────────────────────────────────────\n\n/**\n * Datadog Logs API v2 format. Includes the tags Datadog uses to pivot\n * dashboards and trigger monitors. POST as NDJSON to /api/v2/logs.\n */\nexport function renderDatadogReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n ddsource: \"xploitscan\",\n ddtags: [\n `severity:${finding.severity}`,\n `rule:${finding.rule}`,\n `category:${finding.category.toLowerCase().replace(/\\s+/g, \"_\")}`,\n finding.owasp ? `owasp:${finding.owasp.replace(/\\s+/g, \"_\")}` : \"\",\n finding.cwe ? `cwe:${finding.cwe}` : \"\",\n ]\n .filter(Boolean)\n .join(\",\"),\n service: \"xploitscan\",\n hostname: \"xploitscan-cli\",\n status: DATADOG_STATUS[finding.severity] ?? \"info\",\n message: `${finding.rule}: ${finding.title} — ${finding.description}`,\n timestamp: result.timestamp,\n xploitscan: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n","import { createServer } from \"node:http\";\nimport { URL } from \"node:url\";\nimport { randomUUID } from \"node:crypto\";\nimport { execFile } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { storeToken, clearToken, getStoredToken, syncUser, clearProRulesCache } from \"../utils/api.js\";\n\nconst CLERK_PUBLISHABLE_KEY = process.env.CLERK_PUBLISHABLE_KEY ?? \"\";\n\n/**\n * Base URL of the xploitscan web app that hosts the CLI-login bridge page.\n * Override via env when testing against a local Next.js dev server, e.g.\n * XPLOITSCAN_WEB_BASE=http://localhost:3847 npx xploitscan auth login\n * Defaults to production.\n */\nconst WEB_BASE = process.env.XPLOITSCAN_WEB_BASE ?? \"https://xploitscan.com\";\n\n/**\n * Opens a browser-based login flow.\n * 1. Starts a local HTTP server on a random port\n * 2. Opens the Clerk sign-in page with redirect back to local server\n * 3. Receives the token via redirect callback\n * 4. Stores the token locally\n */\nexport async function loginCommand(): Promise<void> {\n const existing = getStoredToken();\n if (existing) {\n console.log(chalk.yellow(`Already logged in as ${existing.email}`));\n console.log(chalk.gray(\"Run `xploitscan auth logout` first to switch accounts.\"));\n return;\n }\n\n const spinner = ora(\"Waiting for browser login...\").start();\n\n // Start local callback server\n const { token, email, userId } = await waitForBrowserLogin();\n\n spinner.text = \"Syncing account...\";\n\n // Store token\n storeToken({\n token,\n userId,\n email,\n expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days\n });\n\n // Sync user with API\n const user = await syncUser();\n spinner.stop();\n\n console.log(chalk.green(`Logged in as ${email}`));\n if (user) {\n console.log(chalk.gray(`Plan: ${user.plan}`));\n }\n}\n\nexport async function logoutCommand(): Promise<void> {\n const existing = getStoredToken();\n if (!existing) {\n console.log(chalk.gray(\"Not logged in.\"));\n return;\n }\n\n clearToken();\n clearProRulesCache();\n console.log(chalk.green(\"Logged out successfully.\"));\n}\n\nexport async function whoamiCommand(): Promise<void> {\n const token = getStoredToken();\n if (!token) {\n console.log(chalk.gray(\"Not logged in. Run `xploitscan auth login` to authenticate.\"));\n return;\n }\n\n console.log(chalk.cyan(`Email: ${token.email}`));\n console.log(chalk.gray(`User ID: ${token.userId}`));\n\n const user = await syncUser();\n if (user) {\n const planBadge = user.plan === \"pro\"\n ? chalk.bgGreen.black(\" PRO \")\n : chalk.bgGray.white(\" FREE \");\n console.log(`Plan: ${planBadge}`);\n }\n}\n\nasync function waitForBrowserLogin(): Promise<{\n token: string;\n email: string;\n userId: string;\n}> {\n return new Promise((resolve, reject) => {\n const expectedState = randomUUID();\n\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400);\n res.end(\"Bad request\");\n return;\n }\n\n const url = new URL(req.url, `http://localhost`);\n\n if (url.pathname === \"/callback\") {\n const token = url.searchParams.get(\"token\");\n const email = url.searchParams.get(\"email\");\n const userId = url.searchParams.get(\"user_id\");\n const state = url.searchParams.get(\"state\");\n\n if (!state || state !== expectedState) {\n res.writeHead(403);\n res.end(\"Invalid state parameter — possible CSRF attack.\");\n return;\n }\n\n if (token && email && userId) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(`\n <html>\n <body style=\"font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;\">\n <div style=\"text-align: center;\">\n <h1 style=\"color: #00d4ff;\">xploitscan</h1>\n <p style=\"color: #4ade80; font-size: 1.2rem;\">Login successful!</p>\n <p style=\"color: #888;\">You can close this tab and return to your terminal.</p>\n </div>\n </body>\n </html>\n `);\n\n server.close();\n resolve({ token, email, userId });\n } else {\n res.writeHead(400);\n res.end(\"Missing parameters\");\n }\n } else if (url.pathname === \"/login\") {\n // Serve a launch page that bounces the browser to the web-side\n // /auth/cli-login bridge. That page validates the callback URL,\n // ensures the user is signed in via Clerk (handles signup if not),\n // mints a CLI API key for them, and redirects back to /callback\n // here with token + email + user_id + state attached.\n //\n // Previous version of this handler had no redirect at all — just\n // a static \"Redirecting to login...\" page with a click-here link\n // pointing at `#`. The flow was a dead end. Three redundant\n // redirect mechanisms below so it works under any reasonable\n // browser config: HTTP 302 status (primary), <meta refresh>\n // fallback, JavaScript window.location.replace fallback, and a\n // visible anchor tag if all three fail.\n const port = (server.address() as { port: number }).port;\n const callbackUrl = `http://localhost:${port}/callback`;\n const cliLoginUrl =\n `${WEB_BASE}/auth/cli-login` +\n `?callback=${encodeURIComponent(callbackUrl)}` +\n `&state=${encodeURIComponent(expectedState)}`;\n\n res.writeHead(302, { Location: cliLoginUrl, \"Content-Type\": \"text/html\" });\n res.end(`<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0; url=${cliLoginUrl}\" />\n <title>xploitscan login</title>\n </head>\n <body style=\"font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;\">\n <div style=\"text-align: center;\">\n <h1 style=\"color: #00d4ff;\">xploitscan</h1>\n <p>Redirecting to login...</p>\n <p style=\"color: #888; font-size: 0.85rem;\">\n If not redirected, <a href=\"${cliLoginUrl}\" style=\"color: #00d4ff;\">click here</a>.\n </p>\n </div>\n <script>window.location.replace(${JSON.stringify(cliLoginUrl)});</script>\n </body>\n</html>`);\n } else {\n res.writeHead(302, { Location: \"/login\" });\n res.end();\n }\n });\n\n // Listen on random available port\n server.listen(0, \"127.0.0.1\", () => {\n const addr = server.address() as { port: number };\n const loginUrl = `http://localhost:${addr.port}/login`;\n\n console.log(chalk.cyan(`\\nOpen this URL in your browser to log in:`));\n console.log(chalk.bold.underline(loginUrl));\n console.log(\"\");\n\n // Try to open browser automatically\n const openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start\" : \"xdg-open\";\n execFile(openCmd, [loginUrl], () => {});\n });\n\n // Timeout after 5 minutes\n setTimeout(() => {\n server.close();\n reject(new Error(\"Login timed out. Please try again.\"));\n }, 5 * 60 * 1000);\n });\n}\n","import { execSync } from \"node:child_process\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n chmodSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\n\nconst HOOK_START_MARKER = \"# xploitscan-hook-start\";\nconst HOOK_END_MARKER = \"# xploitscan-hook-end\";\n\nconst HOOK_CONTENT = `${HOOK_START_MARKER}\n# Installed by XploitScan CLI — do not edit this block manually\necho \"\"\necho \"🛡 Running XploitScan security check...\"\nnpx --yes xploitscan scan . --no-ai --diff HEAD\nHOOK_EXIT=$?\nif [ $HOOK_EXIT -ne 0 ]; then\n echo \"\"\n echo \"❌ XploitScan found security issues. Commit blocked.\"\n echo \" Fix the issues above, or use 'git commit --no-verify' to skip.\"\n exit 1\nfi\n${HOOK_END_MARKER}`;\n\nfunction getGitRoot(): string | null {\n try {\n const root = execSync(\"git rev-parse --show-toplevel\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n }).trim();\n return root || null;\n } catch {\n return null;\n }\n}\n\nfunction removeExistingBlock(content: string): string {\n const startIdx = content.indexOf(HOOK_START_MARKER);\n const endIdx = content.indexOf(HOOK_END_MARKER);\n if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {\n return content;\n }\n const before = content.slice(0, startIdx).replace(/\\n+$/, \"\");\n const after = content.slice(endIdx + HOOK_END_MARKER.length).replace(/^\\n+/, \"\");\n if (before && after) return `${before}\\n\\n${after}\\n`;\n if (before) return `${before}\\n`;\n if (after) return `${after}\\n`;\n return \"\";\n}\n\nexport async function installHookCommand(options: { force?: boolean }): Promise<void> {\n const gitRoot = getGitRoot();\n if (!gitRoot) {\n console.log(chalk.red(\"✗ Not a git repository.\"));\n console.log(chalk.gray(\" Run this command from inside a git repo.\"));\n process.exit(1);\n }\n\n const hooksDir = join(gitRoot, \".git\", \"hooks\");\n const hookPath = join(hooksDir, \"pre-commit\");\n\n let existingContent = \"\";\n let hadExisting = false;\n if (existsSync(hookPath)) {\n existingContent = readFileSync(hookPath, \"utf-8\");\n hadExisting = true;\n\n const alreadyInstalled =\n existingContent.includes(HOOK_START_MARKER) && existingContent.includes(HOOK_END_MARKER);\n\n if (alreadyInstalled && !options.force) {\n console.log(chalk.yellow(\"⚠ XploitScan hook is already installed.\"));\n console.log(chalk.gray(\" Use --force to reinstall, or `xploitscan hook uninstall` to remove it.\"));\n return;\n }\n\n if (alreadyInstalled && options.force) {\n existingContent = removeExistingBlock(existingContent);\n }\n }\n\n // Compose new hook file\n let newContent: string;\n if (hadExisting && existingContent.trim()) {\n // Preserve existing hook content, append our block\n const trimmed = existingContent.replace(/\\n+$/, \"\");\n newContent = `${trimmed}\\n\\n${HOOK_CONTENT}\\n`;\n } else {\n newContent = `#!/bin/sh\\n\\n${HOOK_CONTENT}\\n`;\n }\n\n try {\n writeFileSync(hookPath, newContent, \"utf-8\");\n chmodSync(hookPath, 0o755);\n } catch (err) {\n console.log(chalk.red(`✗ Failed to write pre-commit hook: ${(err as Error).message}`));\n process.exit(1);\n }\n\n console.log(chalk.green(\"✓ XploitScan pre-commit hook installed.\"));\n console.log(\"\");\n console.log(chalk.gray(\" XploitScan will now scan your code before every commit.\"));\n console.log(chalk.gray(\" To skip a scan, use: \") + chalk.cyan(\"git commit --no-verify\"));\n console.log(chalk.gray(\" To remove: \") + chalk.cyan(\"xploitscan hook uninstall\"));\n}\n\nexport async function uninstallHookCommand(): Promise<void> {\n const gitRoot = getGitRoot();\n if (!gitRoot) {\n console.log(chalk.red(\"✗ Not a git repository.\"));\n console.log(chalk.gray(\" Run this command from inside a git repo.\"));\n process.exit(1);\n }\n\n const hookPath = join(gitRoot, \".git\", \"hooks\", \"pre-commit\");\n\n if (!existsSync(hookPath)) {\n console.log(chalk.yellow(\"⚠ No pre-commit hook found. Nothing to uninstall.\"));\n return;\n }\n\n const content = readFileSync(hookPath, \"utf-8\");\n if (!content.includes(HOOK_START_MARKER)) {\n console.log(chalk.yellow(\"⚠ XploitScan hook not found in pre-commit file.\"));\n console.log(chalk.gray(\" Nothing to remove.\"));\n return;\n }\n\n const stripped = removeExistingBlock(content).trim();\n const shebangOnly = stripped === \"#!/bin/sh\" || stripped === \"#!/usr/bin/env sh\" || stripped === \"\";\n\n try {\n if (shebangOnly) {\n unlinkSync(hookPath);\n console.log(chalk.green(\"✓ XploitScan hook removed. Pre-commit file deleted (was empty after removal).\"));\n } else {\n writeFileSync(hookPath, stripped + \"\\n\", \"utf-8\");\n chmodSync(hookPath, 0o755);\n console.log(chalk.green(\"✓ XploitScan hook removed.\"));\n console.log(chalk.gray(\" Other hook content was preserved.\"));\n }\n } catch (err) {\n console.log(chalk.red(`✗ Failed to uninstall hook: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n","import chalk from \"chalk\";\nimport { mkdirSync, existsSync, writeFileSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface InstallOptions {\n force?: boolean;\n legacyOnly?: boolean;\n}\n\n/**\n * Install XploitScan security rules into a project so Cursor enforces them\n * at write-time. Drops:\n * - .cursor/rules/xploitscan-security.mdc (modern context-aware format)\n * - .cursorrules (legacy fallback for older Cursor versions)\n *\n * Idempotent: if files exist, won't overwrite without --force.\n */\nexport async function cursorInstallCommand(opts: InstallOptions = {}): Promise<void> {\n const cwd = process.cwd();\n\n // Resolve template paths relative to this file (works after tsup bundling\n // because we copy templates into dist via tsup config)\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, \"templates\"),\n join(here, \"..\", \"templates\"),\n join(here, \"..\", \"src\", \"templates\"),\n ];\n const templatesDir = candidates.find((p) => existsSync(join(p, \"cursor-security.mdc\")));\n if (!templatesDir) {\n console.error(chalk.red(\"Could not locate XploitScan rule templates. Try reinstalling: npm i -g xploitscan@latest\"));\n process.exit(1);\n }\n\n const mdcSrc = readFileSync(join(templatesDir, \"cursor-security.mdc\"), \"utf-8\");\n const legacySrc = readFileSync(join(templatesDir, \"cursorrules-legacy.txt\"), \"utf-8\");\n\n // Modern format\n if (!opts.legacyOnly) {\n const mdcDir = join(cwd, \".cursor\", \"rules\");\n const mdcPath = join(mdcDir, \"xploitscan-security.mdc\");\n if (existsSync(mdcPath) && !opts.force) {\n console.log(chalk.yellow(`Skipping ${mdcPath} (already exists, use --force to overwrite)`));\n } else {\n mkdirSync(mdcDir, { recursive: true });\n writeFileSync(mdcPath, mdcSrc);\n console.log(chalk.green(`Installed ${mdcPath}`));\n }\n }\n\n // Legacy format\n const legacyPath = join(cwd, \".cursorrules\");\n if (existsSync(legacyPath) && !opts.force) {\n console.log(chalk.yellow(`Skipping ${legacyPath} (already exists, use --force to overwrite)`));\n } else {\n writeFileSync(legacyPath, legacySrc);\n console.log(chalk.green(`Installed ${legacyPath}`));\n }\n\n console.log(\"\");\n console.log(chalk.cyan(\"Cursor will pick up these rules automatically the next time you open the project.\"));\n console.log(chalk.gray(\"Run a scan any time to catch what slipped through: npx xploitscan scan .\"));\n}\n"],"mappings":"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","names":["resolve","join","chalk","readFileSync","join","existsSync","join","resolve","execFile","readFile","mkdtemp","rm","existsSync","join","tmpdir","resolve","execFile","mkdtemp","join","tmpdir","existsSync","readFile","rm","lines","getSnippet","getSnippet","getSnippet","resolve","chalk","execSync","join","result","execFile","chalk","ora","chalk","ora","resolve","execFile","existsSync","readFileSync","join","chalk","chalk","existsSync","writeFileSync","readFileSync","join","getStoredToken","chalk","execFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/scan.ts","../src/utils/files.ts","../src/utils/config.ts","../src/scanners/semgrep.ts","../src/scanners/gitleaks.ts","../src/scanners/ai-analyzer.ts","../src/scanners/ast-analyzer.ts","../src/scanners/osv.ts","../src/scanners/dependency-scanner.ts","../src/scanners/config-analyzer.ts","../src/scanners/multi-file-analyzer.ts","../src/reporters/terminal.ts","../src/reporters/json.ts","../src/reporters/sarif.ts","../src/reporters/siem.ts","../src/commands/auth.ts","../src/commands/hook.ts","../src/commands/cursor.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { loginCommand, logoutCommand, whoamiCommand } from \"./commands/auth.js\";\nimport { installHookCommand, uninstallHookCommand } from \"./commands/hook.js\";\nimport { cursorInstallCommand } from \"./commands/cursor.js\";\n\n// Injected at build time by tsup's `define` (see tsup.config.ts). Reads the\n// current version from packages/cli/package.json so `xploitscan --version`\n// always matches the published tarball, without a runtime fs read.\ndeclare const __XPLOITSCAN_VERSION__: string;\n\nconst program = new Command();\n\nprogram\n .name(\"xploitscan\")\n .description(\n \"AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do.\",\n )\n .version(__XPLOITSCAN_VERSION__);\n\nprogram\n .command(\"scan\")\n .description(\"Scan a directory for security vulnerabilities\")\n .argument(\"[directory]\", \"Directory to scan\", \".\")\n .option(\"--no-ai\", \"Skip AI-powered analysis\")\n .option(\n \"-f, --format <format>\",\n \"Output format: terminal, json, sarif, splunk-hec, elastic-ecs, datadog-logs\",\n \"terminal\",\n )\n .option(\"-v, --verbose\", \"Show detailed output\", false)\n .option(\"--diff [base]\", \"Scan only files changed vs base branch (default: main)\")\n .option(\"-w, --watch\", \"Watch for file changes and re-scan automatically\", false)\n .action(async (directory: string, opts: { ai: boolean; format: string; verbose: boolean; diff?: string | boolean; watch: boolean }) => {\n await scanCommand(directory, {\n directory,\n aiAnalysis: opts.ai,\n format: opts.format as \"terminal\" | \"json\" | \"sarif\",\n verbose: opts.verbose,\n diff: opts.diff,\n watch: opts.watch,\n });\n });\n\n// Auth commands\nconst auth = program\n .command(\"auth\")\n .description(\"Manage authentication\");\n\nauth\n .command(\"login\")\n .description(\"Log in to your XploitScan account\")\n .action(loginCommand);\n\nauth\n .command(\"logout\")\n .description(\"Log out of your XploitScan account\")\n .action(logoutCommand);\n\nauth\n .command(\"whoami\")\n .description(\"Show current logged-in user\")\n .action(whoamiCommand);\n\n// Hook commands\nconst hook = program\n .command(\"hook\")\n .description(\"Manage git pre-commit hooks for automatic scanning\");\n\nhook\n .command(\"install\")\n .description(\"Install a git pre-commit hook that runs XploitScan on every commit\")\n .option(\"-f, --force\", \"Overwrite existing XploitScan hook without prompting\", false)\n .action(async (opts: { force?: boolean }) => {\n await installHookCommand({ force: opts.force });\n });\n\nhook\n .command(\"uninstall\")\n .description(\"Remove the XploitScan pre-commit hook\")\n .action(uninstallHookCommand);\n\n// Cursor integration\nconst cursor = program\n .command(\"cursor\")\n .description(\"Manage XploitScan integration with Cursor IDE\");\n\ncursor\n .command(\"install\")\n .description(\"Drop XploitScan security rules into .cursor/rules and .cursorrules so Cursor enforces them at write-time\")\n .option(\"-f, --force\", \"Overwrite existing rule files\", false)\n .option(\"--legacy-only\", \"Only install the legacy .cursorrules file (skip .cursor/rules/*.mdc)\", false)\n .action(async (opts: { force?: boolean; legacyOnly?: boolean }) => {\n await cursorInstallCommand({ force: opts.force, legacyOnly: opts.legacyOnly });\n });\n\n// Upgrade command (shortcut)\nprogram\n .command(\"upgrade\")\n .description(\"Upgrade to XploitScan Pro for unlimited scans\")\n .action(async () => {\n const { getStoredToken, getCheckoutUrl } = await import(\"./utils/api.js\");\n const chalk = (await import(\"chalk\")).default;\n\n const token = getStoredToken();\n if (!token) {\n console.log(chalk.yellow(\"Please log in first: xploitscan auth login\"));\n return;\n }\n\n console.log(chalk.cyan(\"Creating checkout session...\"));\n const url = await getCheckoutUrl();\n if (url) {\n console.log(chalk.green(`\\nOpen this URL to upgrade:`));\n console.log(chalk.bold.underline(url));\n const { execFile } = await import(\"node:child_process\");\n const openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start\" : \"xdg-open\";\n execFile(openCmd, [url], () => {});\n } else {\n console.log(chalk.red(\"Failed to create checkout session. Please try again.\"));\n }\n });\n\nprogram.parse();\n","import { resolve, join, relative } from \"node:path\";\nimport { watch as fsWatch } from \"node:fs\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport type { Finding, ScanOptions, ScanResult } from \"../types.js\";\nimport { collectFiles, readFileContents } from \"../utils/files.js\";\nimport { loadConfig } from \"../utils/config.js\";\nimport { runCustomRules } from \"../scanners/custom-rules.js\";\nimport { runSemgrep } from \"../scanners/semgrep.js\";\nimport { runGitleaks } from \"../scanners/gitleaks.js\";\nimport { analyzeWithAI } from \"../scanners/ai-analyzer.js\";\nimport { filterFalsePositives } from \"xploitscan-shared-rules\";\nimport { buildASTContext } from \"../scanners/ast-analyzer.js\";\nimport { scanDependencies, scanDependenciesOsv } from \"../scanners/dependency-scanner.js\";\nimport { scanEntropy } from \"xploitscan-shared-rules\";\nimport { scanConfigs } from \"../scanners/config-analyzer.js\";\nimport { scanMultiFile } from \"../scanners/multi-file-analyzer.js\";\nimport { renderTerminalReport } from \"../reporters/terminal.js\";\nimport { renderJsonReport } from \"../reporters/json.js\";\nimport { renderSarifReport } from \"../reporters/sarif.js\";\nimport {\n renderSplunkReport,\n renderElasticReport,\n renderDatadogReport,\n} from \"../reporters/siem.js\";\nimport { checkUsage, incrementUsage, uploadScanResults, isAuthenticated, loadCachedProRules, downloadProRulesBundle } from \"../utils/api.js\";\nimport type { CustomRule } from \"../types.js\";\n\nexport async function scanCommand(\n directory: string,\n options: Partial<ScanOptions>,\n): Promise<void> {\n const dir = resolve(directory || \".\");\n const format = options.format ?? \"terminal\";\n const verbose = options.verbose ?? false;\n const startTime = Date.now();\n\n // Load config\n const config = await loadConfig(dir);\n const useAI = (options.aiAnalysis ?? config.ai ?? true) && !!process.env.ANTHROPIC_API_KEY;\n\n const isSilent = format !== \"terminal\";\n\n // Step 0: Check usage limits and determine rule tier.\n //\n // The server collapses Indie, Pro, Team, and Trial all into plan: \"pro\"\n // for this endpoint — the CLI only cares whether to download the full\n // 206-rule bundle (yes for all paid tiers). The underlying scan cap\n // surfaces via `limit` and `remaining`:\n // -1 / -1 → unlimited (Pro/Team/Trial)\n // 500 / N → Indie (monthly cap), server also sets tier: \"indie\"\n // 5 / N → Free (daily cap)\n let tier: \"free\" | \"pro\" = \"free\";\n let userPlan = \"anonymous\";\n\n if (isAuthenticated()) {\n const usage = await checkUsage();\n userPlan = usage.plan;\n if (usage.plan === \"pro\") {\n tier = \"pro\";\n }\n if (!usage.allowed) {\n // Distinguish the Indie (monthly) cap from the Free (daily) cap so\n // the message points users at the right mental model.\n const isMonthlyCap = (usage as { tier?: string }).tier === \"indie\";\n console.log(chalk.red(`\\n${isMonthlyCap ? \"Monthly\" : \"Daily\"} scan limit reached.`));\n console.log(chalk.yellow(\"Upgrade to Pro for unlimited scans: \") + chalk.bold(\"xploitscan upgrade\"));\n console.log(chalk.gray(`Resets ${isMonthlyCap ? \"next month\" : \"tomorrow\"}. Plan: ${(usage as { tier?: string }).tier || usage.plan}\\n`));\n process.exitCode = 1;\n return;\n }\n if (usage.plan === \"free\" && usage.remaining > 0 && usage.remaining <= 2 && !isSilent) {\n console.log(chalk.gray(` ${usage.remaining} free scan${usage.remaining === 1 ? \"\" : \"s\"} remaining today\\n`));\n }\n // Indie — warn when the monthly budget gets thin.\n if ((usage as { tier?: string }).tier === \"indie\" && usage.remaining > 0 && usage.remaining <= 25 && !isSilent) {\n console.log(chalk.gray(` ${usage.remaining} Indie scan${usage.remaining === 1 ? \"\" : \"s\"} remaining this month\\n`));\n }\n }\n\n // Step 0b: Load Pro rules from cache or server\n let proRulesExtra: CustomRule[] = [];\n if (tier === \"pro\") {\n const cached = loadCachedProRules() as CustomRule[] | null;\n if (cached) {\n proRulesExtra = cached;\n } else {\n if (!isSilent) console.log(chalk.gray(\" Downloading Pro rules...\"));\n const downloaded = await downloadProRulesBundle();\n if (downloaded) {\n proRulesExtra = (loadCachedProRules() as CustomRule[] | null) || [];\n }\n if (proRulesExtra.length === 0 && !isSilent) {\n console.log(chalk.yellow(\" Could not load Pro rules — scanning with free rules only\"));\n }\n }\n }\n\n // Step 1: Collect files\n const spinner = ora({\n text: \"Scanning files...\",\n color: \"cyan\",\n isSilent,\n }).start();\n\n let files: string[];\n try {\n files = await collectFiles(dir);\n } catch (error) {\n spinner.fail(\"Failed to scan directory\");\n console.error(chalk.red(`Error: ${error instanceof Error ? error.message : error}`));\n process.exit(1);\n }\n\n // Diff mode: filter to only changed files\n if (options.diff) {\n const base = typeof options.diff === \"string\" ? options.diff : \"main\";\n // Validate `base` before passing to git. Two layers of defense:\n // 1. Reject anything starting with `-` so it can't be interpreted\n // as a git flag (e.g. --upload-pack=evil triggers code exec)\n // 2. Restrict to characters valid in git refspecs (alnum + . _ - / ~ ^)\n // The previous implementation interpolated `base` into a shell string\n // via `execSync(\\`git diff --name-only ${base}\\`)` — VC094 in our own\n // scanner flagged that as command injection. Real risk vector: a\n // GitHub Action workflow passing `--diff \"${EVENT_INPUT}\"` where\n // EVENT_INPUT comes from a malicious PR body. Switching to execFile\n // (no shell) + validation closes both vectors.\n if (base.startsWith(\"-\") || !/^[A-Za-z0-9._/\\-~^]+$/.test(base)) {\n spinner.warn(`Invalid --diff base \"${base}\" — scanning all files`);\n } else {\n try {\n const { execFile } = await import(\"node:child_process\");\n const { promisify } = await import(\"node:util\");\n const execFileAsync = promisify(execFile);\n const { stdout } = await execFileAsync(\n \"git\",\n [\"diff\", \"--name-only\", base],\n { cwd: dir, encoding: \"utf-8\" },\n );\n const changedFiles = stdout.trim().split(\"\\n\").filter(Boolean);\n files = files.filter(f => changedFiles.some(cf => f.endsWith(cf) || cf.endsWith(f)));\n if (!isSilent) {\n spinner.info(chalk.gray(` Diff mode: scanning ${files.length} changed files vs ${base}`));\n }\n } catch {\n spinner.warn(\"Could not run git diff — scanning all files\");\n }\n }\n }\n\n if (files.length === 0) {\n spinner.warn(\"No source files found in this directory\");\n return;\n }\n\n spinner.text = `Found ${files.length} files. Running security rules...`;\n\n // Step 2: Run all static scanners in parallel\n const allFindings: Finding[] = [];\n\n // 2a: Custom rules with AST context (always runs, instant)\n const fileContentsForAnalysis: { path: string; content: string }[] = [];\n for (const filePath of files) {\n const content = readFileContents(dir, filePath);\n if (!content) continue;\n fileContentsForAnalysis.push({ path: filePath, content });\n\n // Build AST context for false positive reduction\n const astCtx = buildASTContext(content, filePath);\n\n // Skip scanner files scanning themselves\n if (astCtx.isScannerFile) continue;\n\n // Run custom rules with AST-stripped content for comment-heavy files\n const findings = runCustomRules(content, filePath, config.disableRules, tier, proRulesExtra);\n\n // Add confidence scores based on AST context\n for (const f of findings) {\n if (astCtx.isTestFile) {\n f.confidence = \"low\";\n } else if (astCtx.isConfigFile) {\n f.confidence = \"medium\";\n } else {\n f.confidence = \"high\";\n }\n }\n\n allFindings.push(...findings);\n }\n\n const customCount = allFindings.length;\n if (verbose && customCount > 0) {\n spinner.info(`Custom rules found ${customCount} issues`);\n }\n\n // 2b: Dependency vulnerability scanning (static allowlist + live OSV.dev)\n spinner.text = \"Scanning dependencies...\";\n const depFindings = scanDependencies(fileContentsForAnalysis);\n for (const f of depFindings) { f.confidence = \"high\"; }\n allFindings.push(...depFindings);\n\n // Track which (package, CVE) pairs the static scanner already caught so the\n // OSV pass doesn't double-report\n const dedupeKeys = new Set<string>();\n for (const f of depFindings) {\n // Static findings use rule = CVE id. Extract the package name from the title.\n const match = f.title.match(/^(\\S+)/);\n if (match) dedupeKeys.add(`${match[1].toLowerCase()}:${f.rule}`);\n }\n\n spinner.text = \"Checking dependencies against OSV.dev...\";\n try {\n const osvFindings = await scanDependenciesOsv(fileContentsForAnalysis, dedupeKeys);\n for (const f of osvFindings) { f.confidence = \"high\"; }\n allFindings.push(...osvFindings);\n if (verbose && osvFindings.length > 0) {\n spinner.info(`OSV.dev found ${osvFindings.length} additional vulnerable dependencies`);\n }\n } catch (err) {\n if (verbose) spinner.info(`OSV.dev lookup skipped: ${err instanceof Error ? err.message : \"unknown error\"}`);\n }\n\n if (verbose && depFindings.length > 0) {\n spinner.info(`Dependency scanner found ${depFindings.length} issues`);\n }\n\n // 2c: Entropy-based secret detection\n spinner.text = \"Scanning for high-entropy secrets...\";\n const entropyFindings = scanEntropy(fileContentsForAnalysis);\n for (const f of entropyFindings) { f.confidence = \"medium\"; }\n allFindings.push(...entropyFindings);\n if (verbose && entropyFindings.length > 0) {\n spinner.info(`Entropy scanner found ${entropyFindings.length} potential secrets`);\n }\n\n // 2d: Configuration file deep analysis\n spinner.text = \"Analyzing configuration files...\";\n const configFindings = scanConfigs(fileContentsForAnalysis);\n for (const f of configFindings) { f.confidence = \"high\"; }\n allFindings.push(...configFindings);\n if (verbose && configFindings.length > 0) {\n spinner.info(`Config analyzer found ${configFindings.length} issues`);\n }\n\n // 2e: Multi-file cross-reference analysis\n spinner.text = \"Running cross-file analysis...\";\n const multiFileFindings = scanMultiFile(fileContentsForAnalysis);\n for (const f of multiFileFindings) { f.confidence = \"medium\"; }\n allFindings.push(...multiFileFindings);\n if (verbose && multiFileFindings.length > 0) {\n spinner.info(`Multi-file analyzer found ${multiFileFindings.length} issues`);\n }\n\n // 2f: Semgrep + Gitleaks (run in parallel, gracefully skip if not installed)\n spinner.text = \"Running external scanners...\";\n spinner.color = \"yellow\";\n\n // Resolve custom rules directory (shipped with xploitscan)\n const rulesDir = resolve(join(import.meta.dirname, \"../../rules\"));\n const fallbackRulesDir = resolve(join(dir, \"../rules\"));\n\n const [semgrepResult, gitleaksResult] = await Promise.allSettled([\n runSemgrep(dir, rulesDir).catch(() => runSemgrep(dir, fallbackRulesDir)).catch(() => ({ findings: [] as Finding[], available: false })),\n runGitleaks(dir).catch(() => ({ findings: [] as Finding[], available: false })),\n ]);\n\n const semgrep = semgrepResult.status === \"fulfilled\" ? semgrepResult.value : { findings: [], available: false };\n const gitleaks = gitleaksResult.status === \"fulfilled\" ? gitleaksResult.value : { findings: [], available: false };\n\n allFindings.push(...semgrep.findings);\n allFindings.push(...gitleaks.findings);\n\n if (verbose) {\n if (semgrep.available) {\n spinner.info(`Semgrep found ${semgrep.findings.length} issues`);\n } else {\n spinner.info(chalk.gray(\"Semgrep not installed — install with: pip install semgrep\"));\n }\n if (gitleaks.available) {\n spinner.info(`Gitleaks found ${gitleaks.findings.length} issues`);\n } else {\n spinner.info(chalk.gray(\"Gitleaks not installed — install with: brew install gitleaks\"));\n }\n }\n\n // Show install hints for missing tools (non-verbose, terminal only)\n if (!isSilent && !verbose) {\n const missing: string[] = [];\n if (!semgrep.available) missing.push(\"semgrep (pip install semgrep)\");\n if (!gitleaks.available) missing.push(\"gitleaks (brew install gitleaks)\");\n if (missing.length > 0) {\n spinner.info(chalk.gray(`Optional: install ${missing.join(\" and \")} for deeper scanning`));\n }\n }\n\n const staticCount = allFindings.length;\n spinner.text = `Static analysis found ${staticCount} issue${staticCount !== 1 ? \"s\" : \"\"}`;\n\n // Step 3: AI analysis (if enabled)\n if (useAI) {\n spinner.text = \"Running AI analysis...\";\n spinner.color = \"magenta\";\n\n try {\n const priorityFiles = files\n .map((path) => ({\n path,\n content: readFileContents(dir, path) ?? \"\",\n }))\n .filter((f) => f.content.length > 0 && f.content.length < 30_000)\n .filter((f) => {\n const isHighPriority =\n /(?:api|server|route|auth|middleware|webhook|payment|stripe|supabase)/i.test(f.path) ||\n allFindings.some((finding) => finding.file === f.path) ||\n /(?:query|execute|fetch|prisma|drizzle|mongoose)/i.test(f.content);\n return isHighPriority;\n })\n .slice(0, 20);\n\n if (priorityFiles.length > 0) {\n const aiFindings = await analyzeWithAI(priorityFiles, allFindings);\n allFindings.push(...aiFindings);\n\n if (verbose && aiFindings.length > 0) {\n spinner.info(`AI analysis found ${aiFindings.length} additional issues`);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n spinner.warn(`AI analysis skipped: ${error.message}`);\n }\n }\n } else if (!process.env.ANTHROPIC_API_KEY && !isSilent) {\n spinner.info(\n chalk.gray(\"Tip: Set ANTHROPIC_API_KEY for AI-powered contextual analysis\"),\n );\n }\n\n // Step 3b: AI false positive filter (if API key present)\n // Reviews regex findings with Claude Haiku to remove false positives.\n // Separate from --no-ai (which disables finding generation). This filter\n // only reviews existing findings, it doesn't generate new ones.\n let aiReviewed = false;\n let aiRemovedCount = 0;\n let aiTotalBefore = 0;\n let aiFilteredFindings: Array<{ finding: Finding; reason: string }> = [];\n if (process.env.ANTHROPIC_API_KEY && allFindings.length > 0) {\n spinner.text = \"AI reviewing findings for false positives...\";\n spinner.color = \"cyan\";\n try {\n const fileContentsMap = new Map<string, string>();\n for (const f of allFindings) {\n if (!fileContentsMap.has(f.file)) {\n const content = readFileContents(dir, f.file);\n if (content) fileContentsMap.set(f.file, content);\n }\n }\n const result = await filterFalsePositives(allFindings, fileContentsMap);\n aiReviewed = result.aiReviewed;\n aiRemovedCount = result.removedCount;\n aiTotalBefore = result.totalBefore;\n aiFilteredFindings = result.filteredFindings;\n if (result.removedCount > 0) {\n allFindings.length = 0;\n allFindings.push(...result.findings);\n }\n } catch {\n // AI filter error — keep all findings (graceful degradation)\n }\n }\n\n spinner.stop();\n\n // Step 4: Deduplicate findings (same file + line + similar rule)\n const seen = new Set<string>();\n const dedupedFindings = allFindings.filter((f) => {\n // Normalize: group by file:line and a simplified rule key\n const ruleKey = f.source === \"gitleaks\" ? `secret:${f.file}:${f.line}` : `${f.rule}:${f.file}:${f.line}`;\n if (seen.has(ruleKey)) return false;\n seen.add(ruleKey);\n return true;\n });\n\n // Step 5: Render results\n const result: ScanResult = {\n findings: dedupedFindings,\n filesScanned: files.length,\n duration: Date.now() - startTime,\n timestamp: new Date().toISOString(),\n directory: dir,\n };\n\n switch (format) {\n case \"json\":\n renderJsonReport(result);\n break;\n case \"sarif\":\n renderSarifReport(result);\n break;\n case \"splunk-hec\":\n renderSplunkReport(result);\n break;\n case \"elastic-ecs\":\n renderElasticReport(result);\n break;\n case \"datadog-logs\":\n renderDatadogReport(result);\n break;\n default:\n renderTerminalReport(result, fileContentsForAnalysis);\n break;\n }\n\n // Step 5b: Show AI review status\n if (aiReviewed && !isSilent) {\n console.log(\"\");\n if (aiRemovedCount > 0) {\n if (dedupedFindings.length > 0) {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.white(`${aiRemovedCount} false positive${aiRemovedCount !== 1 ? \"s\" : \"\"} removed · `) + chalk.green(`${dedupedFindings.length} real issue${dedupedFindings.length !== 1 ? \"s\" : \"\"} confirmed`));\n } else {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.green(`clean scan`) + chalk.gray(` (${aiRemovedCount} false positive${aiRemovedCount !== 1 ? \"s\" : \"\"} removed)`));\n }\n } else {\n console.log(chalk.cyan(\"🤖 AI Review: \") + chalk.green(`all ${dedupedFindings.length} finding${dedupedFindings.length !== 1 ? \"s\" : \"\"} confirmed real`));\n }\n // Show filtered findings if --verbose or if user wants detail\n if (verbose && aiFilteredFindings.length > 0) {\n console.log(chalk.gray(`\\n Filtered findings (AI determined these are false positives):`));\n for (const { finding: f, reason } of aiFilteredFindings) {\n console.log(chalk.gray(` ${f.severity.toUpperCase().padEnd(8)} ${f.rule} ${f.file}:${f.line}`));\n console.log(chalk.gray(` Reason: ${reason}`));\n }\n }\n }\n\n // Step 5c: Show upsell for free tier\n if (tier === \"free\" && !isSilent) {\n console.log(\"\");\n if (userPlan === \"anonymous\") {\n console.log(chalk.gray(\" Scanned with 30 free rules.\") + chalk.cyan(\" Log in to unlock all 206 rules →\") + chalk.bold(\" xploitscan auth login\"));\n } else {\n console.log(chalk.gray(\" Scanned with 30 rules.\") + chalk.cyan(\" Upgrade to Pro for all 206 rules →\") + chalk.bold(\" xploitscan upgrade\"));\n }\n console.log(\"\");\n }\n\n // Step 6: Upload results and increment usage (if authenticated)\n if (isAuthenticated()) {\n await Promise.allSettled([\n incrementUsage(),\n uploadScanResults({\n directory: dir,\n filesScanned: files.length,\n findings: dedupedFindings,\n duration: Date.now() - startTime,\n }),\n ]);\n }\n\n // Exit with error code if critical/high findings exist\n const hasCritical = dedupedFindings.some(\n (f) => f.severity === \"critical\" || f.severity === \"high\",\n );\n if (hasCritical) {\n process.exitCode = 1;\n }\n\n // Step 7: Watch mode\n if (options.watch) {\n const watchExclude = new Set([\"node_modules\", \".git\", \"dist\", \"build\", \".next\"]);\n const debounceMs = 500;\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let isScanning = false;\n\n console.log(chalk.cyan(\"\\nWatching for changes... (press Ctrl+C to stop)\"));\n\n fsWatch(dir, { recursive: true }, (_event, filename) => {\n if (!filename) return;\n\n // Skip excluded directories\n const parts = filename.split(\"/\");\n if (parts.some((p) => watchExclude.has(p))) return;\n\n console.log(chalk.gray(`File changed: ${filename}`));\n\n if (debounceTimer) clearTimeout(debounceTimer);\n\n debounceTimer = setTimeout(async () => {\n if (isScanning) return;\n isScanning = true;\n\n console.log(chalk.cyan(\"\\nRe-scanning...\\n\"));\n try {\n await scanCommand(directory, {\n ...options,\n watch: false, // prevent recursive watch\n });\n } catch (error) {\n console.error(chalk.red(`Re-scan error: ${error instanceof Error ? error.message : error}`));\n } finally {\n isScanning = false;\n console.log(chalk.cyan(\"\\nWatching for changes... (press Ctrl+C to stop)\"));\n }\n }, debounceMs);\n });\n\n // Keep the process alive\n await new Promise(() => {});\n }\n}\n","import fg from \"fast-glob\";\nimport ignore from \"ignore\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\nconst SOURCE_EXTENSIONS = [\n \"js\", \"jsx\", \"ts\", \"tsx\", \"mjs\", \"cjs\",\n \"vue\", \"svelte\", \"astro\",\n \"py\", \"rb\", \"go\", \"rs\", \"java\", \"php\",\n \"swift\", \"kt\", \"kts\", \"dart\", \"cs\",\n \"c\", \"cpp\", \"h\",\n \"sh\", \"bash\", \"zsh\",\n \"env\", \"yaml\", \"yml\", \"toml\", \"json\", \"xml\",\n \"html\", \"htm\", \"sql\",\n \"properties\", \"ini\", \"cfg\", \"conf\",\n \"tf\", \"hcl\", \"dockerfile\",\n \"erb\", \"jinja\", \"j2\",\n \"gradle\",\n \"r\", \"lua\", \"pl\", \"pm\", \"ex\", \"exs\",\n \"ipynb\", \"md\",\n \"prisma\", \"plist\", \"pbxproj\", \"entitlements\", \"rules\", \"csv\",\n];\n\nconst SOURCE_FILENAMES = [\n \"Dockerfile\", \"Makefile\", \"Gemfile\", \"Rakefile\",\n \"package.json\", \"Cargo.toml\", \"go.mod\", \"requirements.txt\", \"Pipfile\",\n \"next.config.js\", \"next.config.mjs\", \"next.config.ts\", \"vercel.json\",\n \"firebase.json\", \".firebaserc\", \"firestore.rules\",\n \"app.json\", \"app.config.js\", \"eas.json\",\n \"wrangler.toml\", \"netlify.toml\",\n \"drizzle.config.ts\", \"drizzle.config.js\",\n \"Procfile\", \"Caddyfile\", \"nginx.conf\",\n \"AndroidManifest.xml\",\n];\n\nconst ALWAYS_IGNORE = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \".nuxt\",\n \".svelte-kit\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n \"coverage\",\n \".turbo\",\n \"*.min.js\",\n \"*.min.css\",\n \"*.map\",\n \"package-lock.json\",\n \"pnpm-lock.yaml\",\n \"yarn.lock\",\n // Intentionally-vulnerable test corpora. Security tools (XploitScan,\n // Semgrep, Bearer, Gitleaks, Snyk Code) all maintain directories of\n // known-bad code samples used to verify that rules fire. Scanning\n // them produces a flood of \"critical\" findings on the scanner's own\n // dogfood, drowning the signal from real product code. Skip by\n // default. Users who genuinely want to scan a fixture directory can\n // override via .xploitscanignore (the negation syntax `!test-\n // fixtures/` un-ignores it).\n //\n // We only match these as directory names anywhere in the tree, so a\n // project whose actual source happens to live under a path called\n // `fixtures-prod/` is unaffected.\n \"test-fixtures\",\n \"test-fixtures/**\",\n \"__fixtures__\",\n \"__fixtures__/**\",\n \"vulnerable-samples\",\n \"vulnerable-samples/**\",\n];\n\nexport async function collectFiles(directory: string): Promise<string[]> {\n const ig = ignore.default();\n\n // Load .gitignore if present\n const gitignorePath = join(directory, \".gitignore\");\n if (existsSync(gitignorePath)) {\n const gitignoreContent = readFileSync(gitignorePath, \"utf-8\");\n ig.add(gitignoreContent);\n }\n\n // Load .xploitscanignore if present\n const xploitscanIgnorePath = join(directory, \".xploitscanignore\");\n if (existsSync(xploitscanIgnorePath)) {\n const xploitscanIgnoreContent = readFileSync(xploitscanIgnorePath, \"utf-8\");\n ig.add(xploitscanIgnoreContent);\n }\n\n // Always ignore these\n ig.add(ALWAYS_IGNORE);\n\n const patterns = SOURCE_EXTENSIONS.map((ext) => `**/*.${ext}`);\n // Also grab dotfiles like .env, .env.local, etc.\n patterns.push(\"**/.env*\");\n // Also grab files matched by name (Dockerfile, Makefile, etc.)\n for (const name of SOURCE_FILENAMES) {\n patterns.push(`**/${name}`);\n }\n\n const files = await fg(patterns, {\n cwd: directory,\n absolute: false,\n dot: true,\n onlyFiles: true,\n ignore: ALWAYS_IGNORE.map((p) => `**/${p}`),\n });\n\n // Apply .gitignore filtering\n return files.filter((file) => !ig.ignores(file));\n}\n\nexport function readFileContents(\n directory: string,\n filePath: string,\n): string | null {\n try {\n const fullPath = resolve(join(directory, filePath));\n if (!fullPath.startsWith(resolve(directory))) {\n throw new Error(\"Path traversal detected\");\n }\n return readFileSync(fullPath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nexport function getSnippet(\n content: string,\n line: number,\n contextLines = 2,\n): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 1 - contextLines);\n const end = Math.min(lines.length, line + contextLines);\n\n return lines\n .slice(start, end)\n .map((l, i) => {\n const lineNum = start + i + 1;\n const marker = lineNum === line ? \">\" : \" \";\n return `${marker} ${lineNum.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n","import { cosmiconfig } from \"cosmiconfig\";\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface XploitScanRcConfig {\n rules?: {\n disable?: string[];\n severityOverride?: Record<string, string>;\n };\n scan?: {\n exclude?: string[];\n maxFileSize?: number;\n extensions?: string[];\n };\n output?: {\n format?: \"terminal\" | \"json\" | \"sarif\";\n verbose?: boolean;\n };\n watch?: {\n debounce?: number;\n exclude?: string[];\n };\n}\n\nexport interface XploitScanConfig {\n /** Glob patterns to exclude from scanning */\n exclude?: string[];\n /** Enable/disable AI analysis (requires ANTHROPIC_API_KEY) */\n ai?: boolean;\n /** Severity threshold to report: only show findings at or above this level */\n severity?: \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\";\n /** Custom rules to disable by ID */\n disableRules?: string[];\n /** Severity overrides by rule ID */\n severityOverride?: Record<string, string>;\n /** Max file size in bytes */\n maxFileSize?: number;\n /** Additional file extensions to scan */\n extensions?: string[];\n /** Output format */\n format?: \"terminal\" | \"json\" | \"sarif\";\n /** Verbose output */\n verbose?: boolean;\n /** Watch mode config */\n watch?: {\n debounce?: number;\n exclude?: string[];\n };\n}\n\nconst defaults: XploitScanConfig = {\n exclude: [],\n ai: true,\n severity: \"low\",\n disableRules: [],\n};\n\nfunction loadXploitscanRc(directory: string): XploitScanRcConfig | null {\n try {\n const rcPath = join(directory, \".xploitscanrc\");\n const content = readFileSync(rcPath, \"utf-8\");\n return JSON.parse(content) as XploitScanRcConfig;\n } catch {\n return null;\n }\n}\n\nfunction mergeRcIntoConfig(\n base: XploitScanConfig,\n rc: XploitScanRcConfig,\n): XploitScanConfig {\n const merged = { ...base };\n\n // Merge rules\n if (rc.rules) {\n if (rc.rules.disable) {\n merged.disableRules = [\n ...new Set([...(merged.disableRules ?? []), ...rc.rules.disable]),\n ];\n }\n if (rc.rules.severityOverride) {\n merged.severityOverride = {\n ...(merged.severityOverride ?? {}),\n ...rc.rules.severityOverride,\n };\n }\n }\n\n // Merge scan settings\n if (rc.scan) {\n if (rc.scan.exclude) {\n merged.exclude = [\n ...new Set([...(merged.exclude ?? []), ...rc.scan.exclude]),\n ];\n }\n if (rc.scan.maxFileSize !== undefined) {\n merged.maxFileSize = rc.scan.maxFileSize;\n }\n if (rc.scan.extensions) {\n merged.extensions = [\n ...new Set([...(merged.extensions ?? []), ...rc.scan.extensions]),\n ];\n }\n }\n\n // Merge output settings (rc takes priority)\n if (rc.output) {\n if (rc.output.format !== undefined) {\n merged.format = rc.output.format;\n }\n if (rc.output.verbose !== undefined) {\n merged.verbose = rc.output.verbose;\n }\n }\n\n // Merge watch settings\n if (rc.watch) {\n merged.watch = {\n ...(merged.watch ?? {}),\n ...rc.watch,\n };\n }\n\n return merged;\n}\n\nexport async function loadConfig(\n directory: string,\n): Promise<XploitScanConfig> {\n const explorer = cosmiconfig(\"xploitscan\");\n\n let config = { ...defaults };\n\n try {\n const result = await explorer.search(directory);\n if (result && result.config) {\n config = { ...defaults, ...result.config };\n }\n } catch {\n // Config loading failed, use defaults\n }\n\n // Load .xploitscanrc and merge (rc takes priority)\n const rc = loadXploitscanRc(directory);\n if (rc) {\n config = mergeRcIntoConfig(config, rc);\n }\n\n return config;\n}\n","import { execFile } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { readFile, writeFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport type { Finding, Severity } from \"../types.js\";\n\ninterface SemgrepSarifResult {\n runs?: Array<{\n results?: Array<{\n ruleId?: string;\n level?: string;\n message?: { text?: string };\n locations?: Array<{\n physicalLocation?: {\n artifactLocation?: { uri?: string };\n region?: {\n startLine?: number;\n startColumn?: number;\n snippet?: { text?: string };\n };\n };\n }>;\n }>;\n }>;\n}\n\nconst SEVERITY_MAP: Record<string, Severity> = {\n error: \"high\",\n warning: \"medium\",\n note: \"low\",\n none: \"info\",\n};\n\nfunction semgrepSeverityToXploitscan(level: string): Severity {\n return SEVERITY_MAP[level] ?? \"medium\";\n}\n\nasync function isSemgrepInstalled(): Promise<boolean> {\n return new Promise((resolve) => {\n execFile(\"semgrep\", [\"--version\"], (error) => {\n resolve(!error);\n });\n });\n}\n\nexport async function runSemgrep(\n directory: string,\n customRulesDir?: string,\n): Promise<{ findings: Finding[]; available: boolean }> {\n const installed = await isSemgrepInstalled();\n if (!installed) {\n return { findings: [], available: false };\n }\n\n const findings: Finding[] = [];\n\n // Create a temp directory for SARIF output\n const tmpDir = await mkdtemp(join(tmpdir(), \"xploitscan-semgrep-\"));\n const sarifPath = join(tmpDir, \"results.sarif\");\n\n try {\n // Build semgrep args\n const args = [\n \"scan\",\n \"--sarif\",\n \"--output\", sarifPath,\n \"--quiet\",\n \"--no-git-ignore\", // We handle .gitignore ourselves\n \"--timeout\", \"30\",\n \"--max-target-bytes\", \"1000000\",\n ];\n\n // Use auto config (community rules) + custom rules if available\n args.push(\"--config\", \"auto\");\n\n if (customRulesDir && existsSync(customRulesDir)) {\n args.push(\"--config\", customRulesDir);\n }\n\n args.push(directory);\n\n // Run semgrep\n await new Promise<void>((resolve, reject) => {\n const proc = execFile(\n \"semgrep\",\n args,\n { timeout: 120_000, maxBuffer: 10 * 1024 * 1024 },\n (error, _stdout, stderr) => {\n // Semgrep returns exit code 1 when findings exist — that's fine\n if (error && error.code !== 1) {\n reject(new Error(`Semgrep failed: ${stderr || error.message}`));\n } else {\n resolve();\n }\n },\n );\n });\n\n // Parse SARIF output\n if (!existsSync(sarifPath)) return { findings, available: true };\n\n const sarifContent = await readFile(sarifPath, \"utf-8\");\n const sarif: SemgrepSarifResult = JSON.parse(sarifContent);\n\n for (const run of sarif.runs ?? []) {\n for (const result of run.results ?? []) {\n const location = result.locations?.[0]?.physicalLocation;\n const filePath = location?.artifactLocation?.uri ?? \"unknown\";\n const line = location?.region?.startLine ?? 1;\n const snippet = location?.region?.snippet?.text ?? \"\";\n\n // Determine category from rule ID\n const ruleId = result.ruleId ?? \"semgrep\";\n const category = categorizeSemgrepRule(ruleId);\n\n findings.push({\n id: `SG-${filePath}:${line}:${ruleId}`,\n rule: ruleId,\n severity: semgrepSeverityToXploitscan(result.level ?? \"warning\"),\n title: truncate(result.message?.text ?? ruleId, 100),\n description: result.message?.text ?? \"\",\n file: filePath.replace(/^file:\\/\\//, \"\"),\n line,\n column: location?.region?.startColumn,\n snippet: formatSnippet(snippet, line),\n category,\n source: \"semgrep\",\n });\n }\n }\n } finally {\n // Clean up temp directory\n await rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n }\n\n return { findings, available: true };\n}\n\nfunction categorizeSemgrepRule(ruleId: string): string {\n const id = ruleId.toLowerCase();\n if (id.includes(\"sql\") || id.includes(\"injection\") || id.includes(\"xss\") || id.includes(\"command\")) return \"Injection\";\n if (id.includes(\"auth\") || id.includes(\"session\")) return \"Authentication\";\n if (id.includes(\"crypto\") || id.includes(\"hash\") || id.includes(\"random\")) return \"Cryptography\";\n if (id.includes(\"cors\") || id.includes(\"header\") || id.includes(\"config\")) return \"Configuration\";\n if (id.includes(\"secret\") || id.includes(\"key\") || id.includes(\"password\") || id.includes(\"credential\")) return \"Secrets\";\n if (id.includes(\"path\") || id.includes(\"traversal\") || id.includes(\"file\")) return \"Path Traversal\";\n if (id.includes(\"deserial\")) return \"Deserialization\";\n return \"Security\";\n}\n\nfunction formatSnippet(text: string, line: number): string {\n if (!text) return \"\";\n const lines = text.split(\"\\n\");\n return lines\n .map((l, i) => {\n const num = line + i;\n return ` ${num.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n}\n\nfunction truncate(str: string, max: number): string {\n return str.length > max ? str.substring(0, max - 3) + \"...\" : str;\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile, mkdtemp, rm } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport type { Finding, Severity } from \"../types.js\";\nimport { getSnippet, readFileContents } from \"../utils/files.js\";\n\ninterface GitleaksResult {\n Description: string;\n File: string;\n StartLine: number;\n EndLine: number;\n StartColumn: number;\n EndColumn: number;\n Match: string;\n Secret: string;\n RuleID: string;\n Entropy: number;\n Tags?: string[];\n}\n\nconst RULE_SEVERITY: Record<string, Severity> = {\n \"aws-access-token\": \"critical\",\n \"aws-secret-access-key\": \"critical\",\n \"stripe-access-token\": \"critical\",\n \"github-pat\": \"critical\",\n \"private-key\": \"critical\",\n \"generic-api-key\": \"high\",\n \"slack-webhook\": \"high\",\n \"twilio-api-key\": \"high\",\n \"sendgrid-api-key\": \"high\",\n \"shopify-access-token\": \"high\",\n \"gcp-api-key\": \"critical\",\n \"heroku-api-key\": \"high\",\n \"npm-access-token\": \"critical\",\n \"pypi-upload-token\": \"critical\",\n \"telegram-bot-api-token\": \"high\",\n \"discord-bot-token\": \"high\",\n \"firebase-api-key\": \"high\",\n};\n\nasync function isGitleaksInstalled(): Promise<boolean> {\n return new Promise((resolve) => {\n execFile(\"gitleaks\", [\"version\"], (error) => {\n resolve(!error);\n });\n });\n}\n\nexport async function runGitleaks(\n directory: string,\n): Promise<{ findings: Finding[]; available: boolean }> {\n const installed = await isGitleaksInstalled();\n if (!installed) {\n return { findings: [], available: false };\n }\n\n const findings: Finding[] = [];\n const tmpDir = await mkdtemp(join(tmpdir(), \"xploitscan-gitleaks-\"));\n const reportPath = join(tmpDir, \"results.json\");\n\n try {\n const args = [\n \"detect\",\n \"--source\", directory,\n \"--report-path\", reportPath,\n \"--report-format\", \"json\",\n \"--no-git\",\n \"--exit-code\", \"0\", // Don't fail on findings\n ];\n\n await new Promise<void>((resolve, reject) => {\n execFile(\n \"gitleaks\",\n args,\n { timeout: 60_000, maxBuffer: 10 * 1024 * 1024 },\n (error, _stdout, stderr) => {\n if (error) {\n const sanitizedError = (stderr || error.message).slice(0, 200);\n reject(new Error(`Gitleaks failed: ${sanitizedError}`));\n } else {\n resolve();\n }\n },\n );\n });\n\n // Parse results\n if (!existsSync(reportPath)) return { findings, available: true };\n\n const reportContent = await readFile(reportPath, \"utf-8\");\n if (!reportContent.trim()) return { findings, available: true };\n\n const results: GitleaksResult[] = JSON.parse(reportContent);\n\n for (const result of results) {\n const filePath = result.File;\n const line = result.StartLine + 1; // Gitleaks uses 0-based lines\n const severity = RULE_SEVERITY[result.RuleID] ?? \"high\";\n\n // Read file content for snippet\n const content = readFileContents(directory, filePath);\n const snippet = content ? getSnippet(content, line) : ` ${result.Match}`;\n\n // Redact the actual secret in the description\n const redactedSecret = result.Secret.length > 8\n ? result.Secret.substring(0, 4) + \"...\" + result.Secret.substring(result.Secret.length - 4)\n : \"****\";\n\n findings.push({\n id: `GL-${filePath}:${line}:${result.RuleID}`,\n rule: `GL:${result.RuleID}`,\n severity,\n title: `${result.Description} (detected by Gitleaks)`,\n description: `A secret matching \"${result.RuleID}\" pattern was found: ${redactedSecret}. If this is a real credential, it may already be compromised. Rotate it immediately and move it to environment variables.`,\n file: filePath,\n line,\n column: result.StartColumn,\n snippet,\n fix: `1. Rotate this credential immediately (it may be in git history)\\n2. Move it to a .env file: ${result.RuleID.toUpperCase().replace(/-/g, \"_\")}=<new-value>\\n3. Add .env to .gitignore\\n4. Remove from git history: git filter-branch or BFG Repo Cleaner`,\n category: \"Secrets\",\n source: \"gitleaks\",\n });\n }\n } finally {\n await rm(tmpDir, { recursive: true, force: true }).catch(() => {});\n }\n\n return { findings, available: true };\n}\n","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { Finding, Severity } from \"../types.js\";\n\nconst SYSTEM_PROMPT = `You are a security auditor specializing in code generated by AI tools (Cursor, Lovable, Bolt, Replit, Claude). Your audience is non-expert developers who may not understand security jargon.\n\nAnalyze the provided code for security vulnerabilities. Focus on issues that AI code generators commonly introduce:\n- Missing authentication/authorization\n- Exposed secrets or credentials\n- SQL injection, XSS, and other injection flaws\n- Insecure direct object references (IDOR)\n- Missing input validation\n- Insecure defaults (permissive CORS, no rate limiting)\n- Client-side security checks without server-side enforcement\n- Supabase RLS misconfigurations\n- Unprotected payment/webhook endpoints\n\nFor each vulnerability found, respond with a JSON array of objects:\n{\n \"title\": \"Short, clear title\",\n \"severity\": \"critical|high|medium|low\",\n \"line\": <approximate line number>,\n \"description\": \"Plain-English explanation a non-developer can understand. What's the risk? What could an attacker do?\",\n \"fix\": \"Step-by-step fix instructions with code example if helpful\",\n \"category\": \"Secrets|Authentication|Authorization|Injection|Configuration|Payment Security|Data Exposure\"\n}\n\nIf no vulnerabilities are found, return an empty array: []\n\nRules:\n- Only report real, exploitable issues — no theoretical concerns\n- Be specific about what an attacker could do\n- Explain fixes in beginner-friendly language\n- Reference specific line numbers`;\n\nexport async function analyzeWithAI(\n files: { path: string; content: string }[],\n existingFindings: Finding[],\n): Promise<Finding[]> {\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (!apiKey) return [];\n\n const client = new Anthropic();\n\n // Build context: send files with existing findings for dedup\n const existingRules = new Set(\n existingFindings.map((f) => `${f.file}:${f.line}:${f.rule}`),\n );\n\n // Batch files into chunks to stay within token limits (~50KB per chunk)\n const chunks = chunkFiles(files, 50_000);\n const allFindings: Finding[] = [];\n\n for (const chunk of chunks) {\n const fileContext = chunk\n .map((f) => `--- ${f.path} ---\\n${f.content}`)\n .join(\"\\n\\n\");\n\n const existingNote = existingFindings.length > 0\n ? `\\n\\nThe following issues have already been found by static rules (do NOT duplicate these):\\n${existingFindings.map((f) => `- ${f.file}:${f.line} — ${f.title}`).join(\"\\n\")}`\n : \"\";\n\n try {\n const response = await client.messages.create({\n model: \"claude-sonnet-4-5-20250514\",\n max_tokens: 4096,\n messages: [\n {\n role: \"user\",\n content: `Analyze these source files for security vulnerabilities:${existingNote}\\n\\n${fileContext}`,\n },\n ],\n system: SYSTEM_PROMPT,\n });\n\n const text = response.content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\n // Extract JSON from response (handles markdown code blocks)\n const jsonMatch = text.match(/\\[[\\s\\S]*\\]/);\n if (!jsonMatch) continue;\n\n const parsed = JSON.parse(jsonMatch[0]) as Array<{\n title: string;\n severity: Severity;\n line: number;\n description: string;\n fix: string;\n category: string;\n }>;\n\n for (const item of parsed) {\n // Find which file this finding belongs to\n const matchFile = chunk.find((f) => {\n const lines = f.content.split(\"\\n\");\n return item.line <= lines.length;\n });\n const file = matchFile?.path ?? chunk[0].path;\n\n const key = `${file}:${item.line}:AI`;\n if (existingRules.has(key)) continue;\n\n const content = chunk.find((f) => f.path === file)?.content ?? \"\";\n const lines = content.split(\"\\n\");\n const snippetStart = Math.max(0, item.line - 3);\n const snippetEnd = Math.min(lines.length, item.line + 2);\n const snippet = lines\n .slice(snippetStart, snippetEnd)\n .map((l, i) => {\n const num = snippetStart + i + 1;\n const marker = num === item.line ? \">\" : \" \";\n return `${marker} ${num.toString().padStart(4)} | ${l}`;\n })\n .join(\"\\n\");\n\n allFindings.push({\n id: `AI-${file}:${item.line}`,\n rule: \"AI\",\n severity: item.severity,\n title: item.title,\n description: item.description,\n file,\n line: item.line,\n snippet,\n fix: item.fix,\n category: item.category,\n source: \"ai\",\n });\n }\n } catch (error) {\n // AI analysis failed for this chunk — continue with others\n if (error instanceof Error && error.message.includes(\"API key\")) {\n throw new Error(\n \"Invalid ANTHROPIC_API_KEY. Get one at https://console.anthropic.com/\",\n );\n }\n }\n }\n\n return allFindings;\n}\n\nfunction chunkFiles(\n files: { path: string; content: string }[],\n maxChars: number,\n): { path: string; content: string }[][] {\n const chunks: { path: string; content: string }[][] = [];\n let current: { path: string; content: string }[] = [];\n let currentSize = 0;\n\n for (const file of files) {\n // Truncate individual files that exceed the chunk limit\n const truncatedContent = file.content.length > maxChars\n ? file.content.slice(0, maxChars) + \"\\n// ... truncated for analysis\"\n : file.content;\n const entry = { path: file.path, content: truncatedContent };\n\n if (currentSize + entry.content.length > maxChars && current.length > 0) {\n chunks.push(current);\n current = [];\n currentSize = 0;\n }\n current.push(entry);\n currentSize += entry.content.length;\n }\n\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n","/**\n * AST-based JavaScript/TypeScript analyzer\n * Uses regex-based pseudo-AST parsing (no external deps) to understand code structure\n * and reduce false positives from comments, strings, and non-executable code.\n */\n\nimport type { Finding, Severity } from \"../types.js\";\n\n// ────────────────────────────────────────────\n// Code Structure Detection\n// ────────────────────────────────────────────\n\ninterface CodeContext {\n isComment: boolean;\n isString: boolean;\n isImport: boolean;\n isTestFile: boolean;\n isTypeDefinition: boolean;\n functionScope: string | null;\n nearestAssignment: string | null;\n}\n\n/** Strip all comments from JS/TS/Python code to prevent false positives */\nexport function stripComments(content: string): string {\n let result = \"\";\n let i = 0;\n let inSingleQuote = false;\n let inDoubleQuote = false;\n let inTemplate = false;\n let inSingleLineComment = false;\n let inMultiLineComment = false;\n\n while (i < content.length) {\n const ch = content[i];\n const next = content[i + 1];\n\n if (inSingleLineComment) {\n if (ch === \"\\n\") {\n inSingleLineComment = false;\n result += ch;\n }\n i++;\n continue;\n }\n\n if (inMultiLineComment) {\n if (ch === \"*\" && next === \"/\") {\n inMultiLineComment = false;\n i += 2;\n } else {\n if (ch === \"\\n\") result += ch; // preserve line numbers\n i++;\n }\n continue;\n }\n\n if (inSingleQuote) {\n result += ch;\n if (ch === \"'\" && content[i - 1] !== \"\\\\\") inSingleQuote = false;\n i++;\n continue;\n }\n\n if (inDoubleQuote) {\n result += ch;\n if (ch === '\"' && content[i - 1] !== \"\\\\\") inDoubleQuote = false;\n i++;\n continue;\n }\n\n if (inTemplate) {\n result += ch;\n if (ch === \"`\" && content[i - 1] !== \"\\\\\") inTemplate = false;\n i++;\n continue;\n }\n\n // Check for comment starts\n if (ch === \"/\" && next === \"/\") {\n inSingleLineComment = true;\n i += 2;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n inMultiLineComment = true;\n i += 2;\n continue;\n }\n // Python/YAML comments\n if (ch === \"#\" && !inSingleQuote && !inDoubleQuote) {\n inSingleLineComment = true;\n i++;\n continue;\n }\n\n // Check for string starts\n if (ch === \"'\") inSingleQuote = true;\n if (ch === '\"') inDoubleQuote = true;\n if (ch === \"`\") inTemplate = true;\n\n result += ch;\n i++;\n }\n\n return result;\n}\n\n/** Detect if a line index falls within a function body */\nexport function getFunctionScope(content: string, lineIndex: number): string | null {\n const lines = content.split(\"\\n\");\n // Walk backwards from the match line to find enclosing function\n for (let i = lineIndex; i >= 0; i--) {\n const line = lines[i];\n const funcMatch = line.match(/(?:function\\s+(\\w+)|(?:const|let|var)\\s+(\\w+)\\s*=\\s*(?:async\\s+)?(?:function|\\([^)]*\\)\\s*=>)|(\\w+)\\s*\\([^)]*\\)\\s*\\{)/);\n if (funcMatch) {\n return funcMatch[1] || funcMatch[2] || funcMatch[3] || \"anonymous\";\n }\n }\n return null;\n}\n\n/** Check if content between two positions has a validation/sanitization pattern */\nexport function hasValidationBetween(content: string, startLine: number, endLine: number): boolean {\n const lines = content.split(\"\\n\");\n const segment = lines.slice(startLine, endLine + 1).join(\"\\n\");\n return /(?:validate|sanitize|escape|check|verify|assert|ensure|guard|protect|filter|whitelist|allowlist|isValid|isAllowed|isSafe)/i.test(segment);\n}\n\n/** Detect data flow: does user input reach a dangerous sink? */\nexport function tracesUserInput(content: string, sinkLine: number, linesBack: number = 15): boolean {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, sinkLine - linesBack);\n const segment = lines.slice(start, sinkLine + 1).join(\"\\n\");\n\n const userInputSources = /(?:req\\.(?:body|query|params|headers)|body\\.|input\\.|params\\.|args\\.|request\\.|formData|searchParams|useSearchParams|URLSearchParams)/i;\n return userInputSources.test(segment);\n}\n\n/** Extract all import/require statements to understand dependencies */\nexport function extractImports(content: string): string[] {\n const imports: string[] = [];\n const patterns = [\n /import\\s+.*?from\\s+[\"'`]([^\"'`]+)[\"'`]/g,\n /require\\s*\\(\\s*[\"'`]([^\"'`]+)[\"'`]\\s*\\)/g,\n ];\n for (const p of patterns) {\n let m;\n while ((m = p.exec(content)) !== null) {\n imports.push(m[1]);\n }\n }\n return imports;\n}\n\n/** Check if a file imports/uses a specific security middleware */\nexport function hasSecurityMiddleware(content: string): { auth: boolean; rateLimit: boolean; helmet: boolean; cors: boolean; csrf: boolean } {\n return {\n auth: /(?:requireAuth|isAuthenticated|authenticate|passport|jwt\\.verify|clerk|auth0|nextauth|session\\.user|getServerSession|getAuth|withAuth|authMiddleware)/i.test(content),\n rateLimit: /(?:rateLimit|rate-limit|express-rate-limit|throttle|slowDown|limiter)/i.test(content),\n helmet: /(?:helmet|security-headers|securityHeaders)/i.test(content),\n cors: /(?:cors\\(|corsMiddleware|allowedOrigins)/i.test(content),\n csrf: /(?:csrf|csurf|csrfToken|_csrf)/i.test(content),\n };\n}\n\n// ────────────────────────────────────────────\n// AST-Enhanced Rule Runner\n// ────────────────────────────────────────────\n\nexport interface ASTContext {\n strippedContent: string; // Code with comments removed\n imports: string[]; // All imports/requires\n middleware: ReturnType<typeof hasSecurityMiddleware>;\n isTestFile: boolean;\n isConfigFile: boolean;\n isScannerFile: boolean; // Self-detection: is this a security scanner?\n}\n\nexport function buildASTContext(content: string, filePath: string): ASTContext {\n return {\n strippedContent: stripComments(content),\n imports: extractImports(content),\n middleware: hasSecurityMiddleware(content),\n isTestFile: /(?:\\.test\\.|\\.spec\\.|__tests__|__mocks__|\\.stories\\.|fixtures?\\/|mocks?\\/|\\.cy\\.|\\.e2e\\.)/i.test(filePath),\n isConfigFile: /(?:\\.config\\.|config\\/|\\.rc$|tsconfig|next\\.config|vite\\.config|webpack\\.config)/i.test(filePath),\n isScannerFile: /(?:custom-rules|scanner|security-check|eslint-plugin|lint)/i.test(filePath) &&\n /(?:findMatches|check\\s*\\(content|rule\\.check|severity|category)/i.test(content),\n };\n}\n","/**\n * OSV.dev client — queries the public Open Source Vulnerabilities database\n * for known CVEs affecting a list of dependencies. Augments the hand-curated\n * allowlist in dependency-scanner.ts with live data so we catch the long tail\n * of vulnerable packages we haven't manually added.\n *\n * API: https://google.github.io/osv.dev/post-v1-querybatch/\n * Batch endpoint accepts up to 1000 package queries per request so a typical\n * project's full dependency list resolves in one HTTP round trip.\n */\n\ninterface OsvQuery {\n package: {\n name: string;\n ecosystem: \"npm\" | \"PyPI\" | \"RubyGems\" | \"Go\" | \"crates.io\" | \"Maven\" | \"Packagist\" | \"NuGet\";\n };\n version: string;\n}\n\ninterface OsvVulnSummary {\n id: string; // e.g. GHSA-xxxx-xxxx-xxxx or CVE-2023-12345\n summary?: string;\n details?: string;\n aliases?: string[];\n severity?: Array<{ type: string; score: string }>;\n}\n\ninterface OsvBatchResponse {\n results: Array<{\n vulns?: Array<{ id: string }>;\n }>;\n}\n\nexport interface OsvMatch {\n ecosystem: string;\n name: string;\n version: string;\n vulns: OsvVulnSummary[];\n}\n\nconst OSV_BATCH_URL = \"https://api.osv.dev/v1/querybatch\";\nconst OSV_VULN_URL = \"https://api.osv.dev/v1/vulns\";\nconst NETWORK_TIMEOUT_MS = 5000;\n\nasync function fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number): Promise<Response> {\n const controller = new AbortController();\n const id = setTimeout(() => controller.abort(), timeoutMs);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } finally {\n clearTimeout(id);\n }\n}\n\n/**\n * Query OSV.dev for every given (ecosystem, name, version) tuple and return\n * any vulnerabilities found. Fails soft on network error / timeout so offline\n * scans still work — the caller just gets an empty array and continues.\n *\n * Rate limit aware: the batch endpoint lets us send the whole dependency\n * list in one request, then we only fetch details for packages that\n * actually matched (avoiding the N+1 lookup pattern).\n */\nexport async function queryOsvBatch(queries: OsvQuery[]): Promise<OsvMatch[]> {\n if (queries.length === 0) return [];\n try {\n // Step 1: batch query returns only vuln IDs per package\n const batchRes = await fetchWithTimeout(\n OSV_BATCH_URL,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ queries }),\n },\n NETWORK_TIMEOUT_MS,\n );\n if (!batchRes.ok) return [];\n const batch = (await batchRes.json()) as OsvBatchResponse;\n if (!Array.isArray(batch.results)) return [];\n\n // Collect unique vuln IDs across all matched packages so we only fetch\n // each one once (many packages can share a CVE)\n const uniqueVulnIds = new Set<string>();\n for (const result of batch.results) {\n for (const v of result.vulns ?? []) uniqueVulnIds.add(v.id);\n }\n\n // Step 2: fetch full details for each unique vuln (sequential with\n // per-request timeout — parallelizing would hammer their rate limiter)\n const vulnDetails = new Map<string, OsvVulnSummary>();\n for (const vulnId of uniqueVulnIds) {\n try {\n const detailRes = await fetchWithTimeout(\n `${OSV_VULN_URL}/${encodeURIComponent(vulnId)}`,\n { method: \"GET\" },\n NETWORK_TIMEOUT_MS,\n );\n if (!detailRes.ok) continue;\n const detail = (await detailRes.json()) as OsvVulnSummary;\n vulnDetails.set(vulnId, detail);\n } catch {\n // Skip individual vuln detail failures\n }\n }\n\n // Step 3: zip results back into the caller's query order\n const matches: OsvMatch[] = [];\n batch.results.forEach((result, i) => {\n const ids = (result.vulns ?? []).map((v) => v.id);\n if (ids.length === 0) return;\n const q = queries[i];\n matches.push({\n ecosystem: q.package.ecosystem,\n name: q.package.name,\n version: q.version,\n vulns: ids.map((id) => vulnDetails.get(id) ?? { id }),\n });\n });\n return matches;\n } catch {\n // Network error, timeout, DNS failure — fail soft so scans still work\n // offline. The manual allowlist in dependency-scanner.ts is the fallback.\n return [];\n }\n}\n\n/**\n * Classify an OSV vuln summary into a Finding severity. OSV ships several\n * severity vectors (CVSS v3, CVSS v4, ecosystem-specific) and most entries\n * have at least one. Falls back to 'medium' when unclassified.\n */\nexport function osvSeverity(vuln: OsvVulnSummary): \"critical\" | \"high\" | \"medium\" | \"low\" {\n const vectors = vuln.severity ?? [];\n for (const v of vectors) {\n // CVSS scores are embedded in a vector string — easiest to yank the first\n // numeric \"base score\" value out of it\n const match = v.score.match(/\\b(\\d+(?:\\.\\d+)?)\\b/);\n if (!match) continue;\n const score = parseFloat(match[1]);\n if (Number.isFinite(score)) {\n if (score >= 9) return \"critical\";\n if (score >= 7) return \"high\";\n if (score >= 4) return \"medium\";\n return \"low\";\n }\n }\n return \"medium\";\n}\n","/**\n * Dependency Vulnerability Scanner\n * Parses package.json, requirements.txt, Gemfile, etc. and checks\n * for known vulnerable packages and risky dependency patterns.\n */\n\nimport type { Finding } from \"../types.js\";\nimport { queryOsvBatch, osvSeverity } from \"./osv.js\";\n\n// Known vulnerable packages and their details\n// Format: package -> { minSafeVersion, cve, severity, description }\ninterface VulnEntry {\n minSafe: string;\n cve: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n title: string;\n description: string;\n fix: string;\n}\n\n// Well-known vulnerable npm packages (manually curated, high-signal)\nconst KNOWN_VULN_NPM: Record<string, VulnEntry[]> = {\n \"lodash\": [{\n minSafe: \"4.17.21\", cve: \"CVE-2021-23337\", severity: \"critical\",\n title: \"Lodash Prototype Pollution\",\n description: \"lodash before 4.17.21 is vulnerable to prototype pollution via set, setWith, and zipObjectDeep.\",\n fix: \"Upgrade lodash to >= 4.17.21: npm install lodash@latest\"\n }],\n \"axios\": [{\n minSafe: \"1.6.0\", cve: \"CVE-2023-45857\", severity: \"high\",\n title: \"Axios CSRF Vulnerability\",\n description: \"axios before 1.6.0 inadvertently leaks XSRF-TOKEN cookie in cross-site requests.\",\n fix: \"Upgrade axios to >= 1.6.0: npm install axios@latest\"\n }],\n \"express\": [{\n minSafe: \"4.19.2\", cve: \"CVE-2024-29041\", severity: \"medium\",\n title: \"Express Open Redirect\",\n description: \"Express before 4.19.2 is vulnerable to open redirect via crafted URLs.\",\n fix: \"Upgrade express to >= 4.19.2: npm install express@latest\"\n }],\n \"jsonwebtoken\": [{\n minSafe: \"9.0.0\", cve: \"CVE-2022-23529\", severity: \"high\",\n title: \"jsonwebtoken Insecure Key Handling\",\n description: \"jsonwebtoken before 9.0.0 allows attackers to set secretOrPublicKey to a malicious object.\",\n fix: \"Upgrade jsonwebtoken to >= 9.0.0: npm install jsonwebtoken@latest\"\n }],\n \"node-fetch\": [{\n minSafe: \"2.6.7\", cve: \"CVE-2022-0235\", severity: \"high\",\n title: \"node-fetch Header Exposure\",\n description: \"node-fetch before 2.6.7 exposes authorization headers on redirect to different origin.\",\n fix: \"Upgrade node-fetch to >= 2.6.7 or switch to native fetch (Node 18+).\"\n }],\n \"minimatch\": [{\n minSafe: \"3.0.5\", cve: \"CVE-2022-3517\", severity: \"high\",\n title: \"Minimatch ReDoS\",\n description: \"minimatch before 3.0.5 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade minimatch to >= 3.0.5: npm install minimatch@latest\"\n }],\n \"moment\": [{\n minSafe: \"999.0.0\", cve: \"N/A\", severity: \"medium\",\n title: \"Moment.js is Deprecated\",\n description: \"moment.js is in maintenance mode with known path traversal issues. Consider alternatives.\",\n fix: \"Migrate to date-fns, dayjs, or Temporal API. See: https://momentjs.com/docs/#/-project-status/\"\n }],\n \"request\": [{\n minSafe: \"999.0.0\", cve: \"N/A\", severity: \"medium\",\n title: \"Request Package Deprecated\",\n description: \"The 'request' package is deprecated and no longer receives security updates.\",\n fix: \"Migrate to node-fetch, axios, got, or native fetch (Node 18+).\"\n }],\n \"underscore\": [{\n minSafe: \"1.13.6\", cve: \"CVE-2021-23358\", severity: \"high\",\n title: \"Underscore.js Arbitrary Code Execution\",\n description: \"underscore before 1.13.6 allows arbitrary code execution via the template function.\",\n fix: \"Upgrade underscore to >= 1.13.6 or migrate to lodash.\"\n }],\n \"tar\": [{\n minSafe: \"6.2.1\", cve: \"CVE-2024-28863\", severity: \"high\",\n title: \"Tar Path Traversal\",\n description: \"tar before 6.2.1 is vulnerable to denial of service via crafted archives.\",\n fix: \"Upgrade tar to >= 6.2.1: npm install tar@latest\"\n }],\n \"semver\": [{\n minSafe: \"7.5.2\", cve: \"CVE-2022-25883\", severity: \"medium\",\n title: \"Semver ReDoS\",\n description: \"semver before 7.5.2 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade semver to >= 7.5.2: npm install semver@latest\"\n }],\n \"xml2js\": [{\n minSafe: \"0.5.0\", cve: \"CVE-2023-0842\", severity: \"medium\",\n title: \"xml2js Prototype Pollution\",\n description: \"xml2js before 0.5.0 is vulnerable to prototype pollution when parsing XML.\",\n fix: \"Upgrade xml2js to >= 0.5.0: npm install xml2js@latest\"\n }],\n};\n\n// Known vulnerable Python packages\nconst KNOWN_VULN_PYTHON: Record<string, VulnEntry[]> = {\n \"django\": [{\n minSafe: \"4.2.11\", cve: \"CVE-2024-27351\", severity: \"high\",\n title: \"Django ReDoS in Truncator\",\n description: \"Django before 4.2.11 is vulnerable to Regular Expression Denial of Service.\",\n fix: \"Upgrade Django to >= 4.2.11: pip install Django --upgrade\"\n }],\n \"flask\": [{\n minSafe: \"2.3.2\", cve: \"CVE-2023-30861\", severity: \"high\",\n title: \"Flask Session Cookie Vulnerability\",\n description: \"Flask before 2.3.2 may set permanent session cookies on redirect responses.\",\n fix: \"Upgrade Flask to >= 2.3.2: pip install Flask --upgrade\"\n }],\n \"pillow\": [{\n minSafe: \"10.2.0\", cve: \"CVE-2023-50447\", severity: \"critical\",\n title: \"Pillow Arbitrary Code Execution\",\n description: \"Pillow before 10.2.0 is vulnerable to arbitrary code execution via crafted images.\",\n fix: \"Upgrade Pillow to >= 10.2.0: pip install Pillow --upgrade\"\n }],\n \"requests\": [{\n minSafe: \"2.31.0\", cve: \"CVE-2023-32681\", severity: \"medium\",\n title: \"Requests Proxy Header Leak\",\n description: \"requests before 2.31.0 leaks Proxy-Authorization headers to destination servers.\",\n fix: \"Upgrade requests to >= 2.31.0: pip install requests --upgrade\"\n }],\n \"pyyaml\": [{\n minSafe: \"6.0.1\", cve: \"CVE-2022-41316\", severity: \"high\",\n title: \"PyYAML Unsafe Loading\",\n description: \"PyYAML has known deserialization vulnerabilities. Always use yaml.safe_load().\",\n fix: \"Upgrade PyYAML to >= 6.0.1 and use yaml.safe_load() instead of yaml.load().\"\n }],\n \"jinja2\": [{\n minSafe: \"3.1.3\", cve: \"CVE-2024-22195\", severity: \"medium\",\n title: \"Jinja2 XSS Vulnerability\",\n description: \"Jinja2 before 3.1.3 is vulnerable to XSS via xmlattr filter.\",\n fix: \"Upgrade Jinja2 to >= 3.1.3: pip install Jinja2 --upgrade\"\n }],\n};\n\n// Risky patterns in dependency configs\ninterface DepPattern {\n id: string;\n title: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n description: string;\n fix: string;\n test: (content: string, filePath: string) => boolean;\n}\n\nconst DEP_PATTERNS: DepPattern[] = [\n {\n id: \"DEP001\", title: \"Wildcard Dependency Version\", severity: \"high\",\n description: \"Using '*' or 'latest' as a dependency version allows any version to be installed, including malicious ones.\",\n fix: \"Pin dependency versions: use exact (1.2.3) or caret (^1.2.3) versions, never '*' or 'latest'.\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /[\"']\\*[\"']|[\"']latest[\"']/.test(content),\n },\n {\n id: \"DEP002\", title: \"Git Dependency Without Commit Hash\", severity: \"medium\",\n description: \"Git dependencies without a pinned commit hash can be replaced with malicious code.\",\n fix: \"Pin git dependencies to a specific commit: git+https://github.com/user/repo#commit-hash\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /git\\+https?:\\/\\/[^#\"]+[\"']/.test(content),\n },\n {\n id: \"DEP003\", title: \"Postinstall Script in Package\", severity: \"medium\",\n description: \"postinstall scripts run automatically after npm install and can execute arbitrary code.\",\n fix: \"Audit postinstall scripts carefully. Use --ignore-scripts flag for untrusted packages.\",\n test: (content, filePath) => filePath.endsWith(\"package.json\") && /\"postinstall\"\\s*:/.test(content),\n },\n {\n id: \"DEP004\", title: \"No Package Lock File\", severity: \"medium\",\n description: \"Without a lockfile, npm install may resolve different versions on different machines.\",\n fix: \"Commit your lockfile (package-lock.json, pnpm-lock.yaml, or yarn.lock).\",\n test: (content, filePath) => filePath.endsWith(\".gitignore\") && /package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock/.test(content),\n },\n {\n id: \"DEP005\", title: \"HTTP Registry URL\", severity: \"high\",\n description: \"Using HTTP (not HTTPS) for npm registry allows man-in-the-middle attacks on package downloads.\",\n fix: \"Use HTTPS for registry: registry=https://registry.npmjs.org/\",\n test: (content, filePath) => filePath.endsWith(\".npmrc\") && /registry\\s*=\\s*http:\\/\\//.test(content),\n },\n];\n\nfunction compareVersions(v1: string, v2: string): number {\n const parts1 = v1.replace(/^[^0-9]*/, \"\").split(\".\").map(Number);\n const parts2 = v2.replace(/^[^0-9]*/, \"\").split(\".\").map(Number);\n for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n const a = parts1[i] || 0;\n const b = parts2[i] || 0;\n if (a < b) return -1;\n if (a > b) return 1;\n }\n return 0;\n}\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\nexport function scanDependencies(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n // npm package.json\n if (filePath.endsWith(\"package.json\") && !filePath.includes(\"node_modules\")) {\n try {\n const pkg = JSON.parse(content);\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n for (const [name, version] of Object.entries(allDeps)) {\n const vulns = KNOWN_VULN_NPM[name];\n if (!vulns) continue;\n\n const versionStr = String(version).replace(/^[\\^~>=<]*/g, \"\");\n for (const vuln of vulns) {\n if (compareVersions(versionStr, vuln.minSafe) < 0) {\n const line = content.split(\"\\n\").findIndex(l => l.includes(`\"${name}\"`)) + 1;\n findings.push({\n id: `${vuln.cve}-${filePath}:${line}`,\n rule: vuln.cve,\n severity: vuln.severity,\n title: vuln.title,\n description: vuln.description,\n file: filePath,\n line,\n snippet: getSnippet(content, line),\n fix: vuln.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n } catch {\n // Invalid JSON — skip\n }\n }\n\n // Python requirements.txt\n if (filePath.match(/requirements.*\\.txt$/i)) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n const match = line.match(/^([a-zA-Z0-9_-]+)(?:[=<>!~]+(.+))?/);\n if (!match) continue;\n\n const name = match[1].toLowerCase();\n const version = match[2] || \"0.0.0\";\n const vulns = KNOWN_VULN_PYTHON[name];\n if (!vulns) continue;\n\n for (const vuln of vulns) {\n if (compareVersions(version, vuln.minSafe) < 0) {\n findings.push({\n id: `${vuln.cve}-${filePath}:${i + 1}`,\n rule: vuln.cve,\n severity: vuln.severity,\n title: vuln.title,\n description: vuln.description,\n file: filePath,\n line: i + 1,\n snippet: getSnippet(content, i + 1),\n fix: vuln.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n }\n\n // Dependency pattern checks\n for (const pattern of DEP_PATTERNS) {\n if (pattern.test(content, filePath)) {\n findings.push({\n id: `${pattern.id}-${filePath}:1`,\n rule: pattern.id,\n severity: pattern.severity,\n title: pattern.title,\n description: pattern.description,\n file: filePath,\n line: 1,\n snippet: getSnippet(content, 1),\n fix: pattern.fix,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Async companion to scanDependencies() that augments the hand-curated\n * allowlist with a live OSV.dev lookup of every package version. Fails\n * soft on network error — offline scans still run the static checks.\n *\n * Returns ONLY the extra findings from OSV so callers can merge with the\n * sync results without double-counting:\n * const findings = [...scanDependencies(files), ...await scanDependenciesOsv(files)];\n *\n * Deduplication is by (package name + CVE id) so a package already caught\n * by the static allowlist doesn't show up twice.\n */\nexport async function scanDependenciesOsv(\n files: { path: string; content: string }[],\n alreadyFoundByRule: Set<string>,\n): Promise<Finding[]> {\n interface PendingLookup {\n ecosystem: \"npm\" | \"PyPI\" | \"RubyGems\" | \"Go\";\n name: string;\n version: string;\n file: string;\n line: number;\n content: string;\n }\n\n const lookups: PendingLookup[] = [];\n\n for (const { path: filePath, content } of files) {\n // npm package.json\n if (filePath.endsWith(\"package.json\") && !filePath.includes(\"node_modules\")) {\n try {\n const pkg = JSON.parse(content);\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };\n for (const [name, rawVersion] of Object.entries(allDeps)) {\n const version = String(rawVersion).replace(/^[\\^~>=<]*/g, \"\").trim();\n if (!version || version === \"*\" || version === \"latest\") continue;\n const line = content.split(\"\\n\").findIndex((l) => l.includes(`\"${name}\"`)) + 1;\n lookups.push({ ecosystem: \"npm\", name, version, file: filePath, line: line || 1, content });\n }\n } catch {\n /* invalid JSON */\n }\n }\n\n // Python requirements.txt (exact version pins only — ranges are tricky\n // without a resolver and we'd rather report nothing than guess wrong)\n if (filePath.match(/requirements.*\\.txt$/i)) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].trim().match(/^([a-zA-Z0-9_-]+)==([\\d.]+)/);\n if (!m) continue;\n lookups.push({\n ecosystem: \"PyPI\",\n name: m[1].toLowerCase(),\n version: m[2],\n file: filePath,\n line: i + 1,\n content,\n });\n }\n }\n\n // Ruby Gemfile.lock (exact pins)\n if (filePath.endsWith(\"Gemfile.lock\")) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(/^\\s{4}([a-z0-9_-]+)\\s+\\(([\\d.]+)\\)/);\n if (!m) continue;\n lookups.push({\n ecosystem: \"RubyGems\",\n name: m[1],\n version: m[2],\n file: filePath,\n line: i + 1,\n content,\n });\n }\n }\n }\n\n if (lookups.length === 0) return [];\n\n // Chunk into batches of 500 to stay well under OSV's 1000-query limit\n const CHUNK = 500;\n const findings: Finding[] = [];\n for (let start = 0; start < lookups.length; start += CHUNK) {\n const chunk = lookups.slice(start, start + CHUNK);\n const matches = await queryOsvBatch(\n chunk.map((l) => ({ package: { name: l.name, ecosystem: l.ecosystem }, version: l.version })),\n );\n // Zip matches back to their lookup rows by index within the chunk\n const matchByKey = new Map<string, (typeof matches)[0]>();\n for (const m of matches) matchByKey.set(`${m.ecosystem}:${m.name}:${m.version}`, m);\n\n for (const lookup of chunk) {\n const key = `${lookup.ecosystem}:${lookup.name}:${lookup.version}`;\n const match = matchByKey.get(key);\n if (!match) continue;\n for (const vuln of match.vulns) {\n // Skip if the static allowlist already reported this CVE for this package\n const dedupeKey = `${lookup.name}:${vuln.id}`;\n if (alreadyFoundByRule.has(dedupeKey)) continue;\n alreadyFoundByRule.add(dedupeKey);\n\n const severity = osvSeverity(vuln);\n const title = vuln.summary || `${lookup.name} vulnerability ${vuln.id}`;\n const description =\n vuln.details ||\n vuln.summary ||\n `${lookup.name}@${lookup.version} is affected by ${vuln.id}. See https://osv.dev/vulnerability/${vuln.id} for details.`;\n findings.push({\n id: `${vuln.id}-${lookup.file}:${lookup.line}`,\n rule: vuln.id,\n severity,\n title,\n description: description.slice(0, 500),\n file: lookup.file,\n line: lookup.line,\n snippet: getSnippet(lookup.content, lookup.line),\n fix: `Upgrade ${lookup.name} to a version that resolves ${vuln.id}. See https://osv.dev/vulnerability/${vuln.id}`,\n category: \"Dependencies\",\n source: \"custom\",\n owasp: \"A06:2021\",\n cwe: \"CWE-1395\",\n });\n }\n }\n }\n\n return findings;\n}\n","/**\n * Configuration File Deep Analyzer\n * Analyzes tsconfig, next.config, Dockerfile, GitHub Actions,\n * and other config files for security misconfigurations.\n */\n\nimport type { Finding } from \"../types.js\";\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\ninterface ConfigCheck {\n id: string;\n title: string;\n severity: \"critical\" | \"high\" | \"medium\" | \"low\";\n category: string;\n description: string;\n fix: string;\n owasp: string;\n cwe: string;\n filePattern: RegExp;\n check: (content: string, filePath: string) => { line: number; snippet: string } | null;\n}\n\nconst CONFIG_CHECKS: ConfigCheck[] = [\n // ── TypeScript Config ──\n {\n id: \"CFG001\", title: \"TypeScript Strict Mode Disabled\", severity: \"medium\",\n category: \"Configuration\", description: \"TypeScript without strict mode misses type errors that can cause runtime bugs and security issues.\",\n fix: 'Enable strict mode in tsconfig.json: \"strict\": true',\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /tsconfig\\.json$/,\n check(content) {\n try {\n if (/\"strict\"\\s*:\\s*false/.test(content)) {\n const line = content.split(\"\\n\").findIndex(l => /strict/.test(l)) + 1;\n return { line, snippet: getSnippet(content, line) };\n }\n if (!/\"strict\"\\s*:\\s*true/.test(content) && /\"compilerOptions\"/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n } catch {}\n return null;\n },\n },\n {\n id: \"CFG002\", title: \"TypeScript allowJs Without checkJs\", severity: \"low\",\n category: \"Configuration\", description: \"allowJs without checkJs means JavaScript files bypass type checking entirely.\",\n fix: 'Add \"checkJs\": true alongside \"allowJs\": true in tsconfig.json.',\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /tsconfig\\.json$/,\n check(content) {\n if (/\"allowJs\"\\s*:\\s*true/.test(content) && !/\"checkJs\"\\s*:\\s*true/.test(content)) {\n const line = content.split(\"\\n\").findIndex(l => /allowJs/.test(l)) + 1;\n return { line, snippet: getSnippet(content, line) };\n }\n return null;\n },\n },\n\n // ── Next.js Config ──\n {\n id: \"CFG003\", title: \"Next.js Missing Security Headers\", severity: \"medium\",\n category: \"Configuration\", description: \"Next.js app without security headers in next.config is missing important protections.\",\n fix: \"Add a headers() function to next.config.js with X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Content-Security-Policy.\",\n owasp: \"A05:2021\", cwe: \"CWE-693\",\n filePattern: /next\\.config\\.(js|mjs|ts)$/,\n check(content) {\n if (!/headers\\s*\\(/.test(content) && !/securityHeaders|security-headers/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n {\n id: \"CFG004\", title: \"Next.js Powered-By Header Not Disabled\", severity: \"low\",\n category: \"Configuration\", description: \"Next.js exposes X-Powered-By header by default, revealing your tech stack.\",\n fix: 'Add poweredByHeader: false to next.config.js.',\n owasp: \"A05:2021\", cwe: \"CWE-200\",\n filePattern: /next\\.config\\.(js|mjs|ts)$/,\n check(content) {\n if (!/poweredByHeader\\s*:\\s*false/.test(content)) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n\n // ── Dockerfile ──\n {\n id: \"CFG005\", title: \"Dockerfile Using Latest Tag\", severity: \"medium\",\n category: \"Configuration\", description: \"Using :latest tag in FROM makes builds non-reproducible and may pull vulnerable images.\",\n fix: \"Pin to a specific version: FROM node:20-alpine instead of FROM node:latest.\",\n owasp: \"A06:2021\", cwe: \"CWE-1395\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/^FROM\\s+\\S+:latest/i.test(lines[i].trim()) || /^FROM\\s+\\w+\\s*$/i.test(lines[i].trim())) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n {\n id: \"CFG006\", title: \"Dockerfile Not Using Multi-Stage Build\", severity: \"low\",\n category: \"Configuration\", description: \"Single-stage Docker builds include build tools in the final image, increasing attack surface.\",\n fix: \"Use multi-stage builds: first stage for building, second stage (FROM alpine) for running.\",\n owasp: \"A05:2021\", cwe: \"CWE-1059\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const fromCount = (content.match(/^FROM\\s/gmi) || []).length;\n if (fromCount <= 1 && content.includes(\"npm\") && content.length > 200) {\n return { line: 1, snippet: getSnippet(content, 1) };\n }\n return null;\n },\n },\n {\n id: \"CFG007\", title: \"Dockerfile Copies .env File\", severity: \"critical\",\n category: \"Configuration\", description: \"COPY that includes .env files bakes secrets into the Docker image layer.\",\n fix: \"Add .env to .dockerignore. Use build args or runtime env vars instead.\",\n owasp: \"A02:2021\", cwe: \"CWE-312\",\n filePattern: /Dockerfile$/i,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/^COPY\\s.*\\.env\\b/i.test(lines[i].trim())) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── GitHub Actions ──\n {\n id: \"CFG008\", title: \"GitHub Actions with Broad Permissions\", severity: \"high\",\n category: \"Configuration\", description: \"GitHub Actions with write-all or broad permissions can be exploited if a dependency is compromised.\",\n fix: \"Use minimal permissions: permissions: { contents: read }. Only grant what's needed.\",\n owasp: \"A01:2021\", cwe: \"CWE-250\",\n filePattern: /\\.github\\/workflows\\/.*\\.(yml|yaml)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/permissions\\s*:\\s*write-all/i.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n {\n id: \"CFG009\", title: \"GitHub Actions Using Unpinned Action\", severity: \"medium\",\n category: \"Configuration\", description: \"Using actions with @main or @master instead of a pinned SHA allows supply chain attacks.\",\n fix: \"Pin actions to a specific SHA: uses: actions/checkout@abc123... instead of @main.\",\n owasp: \"A06:2021\", cwe: \"CWE-1395\",\n filePattern: /\\.github\\/workflows\\/.*\\.(yml|yaml)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/uses:\\s*\\S+@(?:main|master|dev|latest)\\s*$/i.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── Vite Config ──\n {\n id: \"CFG010\", title: \"Vite Server Open to Network\", severity: \"medium\",\n category: \"Configuration\", description: \"Vite dev server with host: true exposes it to the entire network.\",\n fix: \"Remove host: true or use host: '127.0.0.1' for local-only access during development.\",\n owasp: \"A05:2021\", cwe: \"CWE-668\",\n filePattern: /vite\\.config\\.(js|ts|mjs)$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/host\\s*:\\s*true/.test(lines[i]) || /host\\s*:\\s*['\"]0\\.0\\.0\\.0['\"]/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── .npmrc ──\n {\n id: \"CFG011\", title: \"NPM Auth Token in .npmrc\", severity: \"critical\",\n category: \"Secrets\", description: \"NPM auth tokens in committed .npmrc files expose registry credentials.\",\n fix: \"Remove the token and add .npmrc to .gitignore. Use NPM_TOKEN env var instead.\",\n owasp: \"A02:2021\", cwe: \"CWE-798\",\n filePattern: /\\.npmrc$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/_authToken|_auth=|\\/\\/registry.*:_password/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n\n // ── ESLint Config ──\n {\n id: \"CFG012\", title: \"Security ESLint Rules Disabled\", severity: \"medium\",\n category: \"Configuration\", description: \"Disabling security-related ESLint rules removes an important safety net.\",\n fix: \"Re-enable security rules or use eslint-plugin-security for automated checks.\",\n owasp: \"A05:2021\", cwe: \"CWE-710\",\n filePattern: /\\.eslint(rc)?(\\.(js|json|yml|yaml|cjs|mjs))?$/,\n check(content) {\n const lines = content.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n if (/no-eval|no-implied-eval|no-new-func|no-script-url|security\\/detect/i.test(lines[i]) && /[\"']off[\"']|:\\s*0/.test(lines[i])) {\n return { line: i + 1, snippet: getSnippet(content, i + 1) };\n }\n }\n return null;\n },\n },\n];\n\nexport function scanConfigs(files: { path: string; content: string }[]): Finding[] {\n const findings: Finding[] = [];\n\n for (const { path: filePath, content } of files) {\n for (const check of CONFIG_CHECKS) {\n if (!check.filePattern.test(filePath)) continue;\n\n const result = check.check(content, filePath);\n if (result) {\n findings.push({\n id: `${check.id}-${filePath}:${result.line}`,\n rule: check.id,\n severity: check.severity,\n title: check.title,\n description: check.description,\n file: filePath,\n line: result.line,\n snippet: result.snippet,\n fix: check.fix,\n category: check.category,\n source: \"custom\",\n owasp: check.owasp,\n cwe: check.cwe,\n });\n }\n }\n }\n\n return findings;\n}\n","/**\n * Multi-File Analysis Scanner\n * Analyzes relationships between files to detect cross-file vulnerabilities\n * that single-file scanners miss.\n */\n\nimport type { Finding } from \"../types.js\";\n\nfunction getSnippet(content: string, line: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, line - 2);\n const end = Math.min(lines.length, line + 2);\n return lines.slice(start, end).map((l, i) => {\n const lineNum = start + i + 1;\n const prefix = lineNum === line ? \">\" : \" \";\n return `${prefix} ${String(lineNum).padStart(5)} | ${l}`;\n }).join(\"\\n\");\n}\n\ninterface FileInfo {\n path: string;\n content: string;\n}\n\nexport function scanMultiFile(files: FileInfo[]): Finding[] {\n const findings: Finding[] = [];\n\n // Build indexes\n const fileMap = new Map<string, string>();\n for (const f of files) {\n fileMap.set(f.path, f.content);\n }\n\n // ── Check 1: API routes without middleware protection ──\n // If there's a middleware file that handles auth, routes are protected.\n // But if routes exist WITHOUT middleware, they're exposed.\n const hasGlobalMiddleware = files.some(f =>\n /middleware\\.(ts|js)$/.test(f.path) &&\n /(?:auth|session|clerk|nextauth|getToken)/i.test(f.content)\n );\n const hasAuthModule = files.some(f =>\n /(?:auth|middleware)\\.(ts|js)$/i.test(f.path) &&\n /(?:requireAuth|isAuthenticated|authenticate|verify)/i.test(f.content)\n );\n\n // Check API route files for auth\n for (const f of files) {\n if (!/(?:\\/api\\/|routes?\\/|controllers?\\/)/i.test(f.path)) continue;\n if (/middleware/i.test(f.path)) continue;\n\n // Skip health/status endpoints\n if (/(?:health|status|ping|ready|live)\\.(?:ts|js)/i.test(f.path)) continue;\n\n const hasLocalAuth = /(?:requireAuth|isAuthenticated|authenticate|verify|getAuth|getSession|getServerSession|withAuth|jwt\\.verify|clerk)/i.test(f.content);\n const hasAuthImport = /import.*(?:auth|session|middleware|verify|clerk)/i.test(f.content);\n\n if (!hasLocalAuth && !hasAuthImport && !hasGlobalMiddleware) {\n // Only flag if the file has actual route handlers\n if (/\\.(get|post|put|delete|patch)\\s*\\(|export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(f.content)) {\n const line = f.content.split(\"\\n\").findIndex(l =>\n /\\.(get|post|put|delete|patch)\\s*\\(|export\\s+(?:async\\s+)?function\\s+(?:GET|POST|PUT|DELETE|PATCH)/i.test(l)\n ) + 1;\n findings.push({\n id: `MFA001-${f.path}:${line}`,\n rule: \"MFA001\",\n severity: \"high\",\n title: \"API Route Without Auth (Cross-File Check)\",\n description: \"This API route file has no authentication checks and no auth middleware was detected in the project.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Add authentication middleware or import your auth module. If using Next.js, create middleware.ts in your app root.\",\n category: \"Authentication\",\n source: \"custom\",\n owasp: \"A01:2021\",\n cwe: \"CWE-862\",\n });\n }\n }\n }\n\n // ── Check 2: .env file exists but .gitignore doesn't exclude it ──\n const hasEnvFile = files.some(f => /^\\.env$|\\/\\.env$/.test(f.path));\n const gitignore = files.find(f => f.path.endsWith(\".gitignore\"));\n if (hasEnvFile && gitignore) {\n if (!/^\\.env$/m.test(gitignore.content) && !/^\\*\\.env$/m.test(gitignore.content)) {\n findings.push({\n id: \"MFA002-.gitignore:1\",\n rule: \"MFA002\",\n severity: \"critical\",\n title: \".env File Not in .gitignore\",\n description: \"An .env file exists in the project but .gitignore doesn't exclude it. Secrets may be committed.\",\n file: \".gitignore\",\n line: 1,\n snippet: getSnippet(gitignore.content, 1),\n fix: \"Add .env to .gitignore: echo '.env' >> .gitignore\",\n category: \"Secrets\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-312\",\n });\n }\n }\n\n // ── Check 3: Database client without connection pooling ──\n const dbFiles = files.filter(f =>\n /(?:db|database|prisma|drizzle|sequelize|mongoose|knex)\\.(ts|js)$/i.test(f.path) ||\n /(?:createClient|createPool|createConnection|PrismaClient)/i.test(f.content)\n );\n for (const f of dbFiles) {\n // Check if DB client is created at module level (not in a function)\n if (/new PrismaClient/i.test(f.content)) {\n // Good pattern: globalThis.prisma = globalThis.prisma || new PrismaClient()\n if (!/globalThis|global\\./i.test(f.content)) {\n const line = f.content.split(\"\\n\").findIndex(l => /new PrismaClient/.test(l)) + 1;\n findings.push({\n id: `MFA003-${f.path}:${line}`,\n rule: \"MFA003\",\n severity: \"medium\",\n title: \"Database Client Not Cached (Connection Leak)\",\n description: \"Creating a new PrismaClient on every request leaks database connections. Use a singleton pattern.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use singleton: globalThis.prisma = globalThis.prisma || new PrismaClient(). This prevents connection exhaustion in serverless.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-400\",\n });\n }\n }\n }\n\n // ── Check 4: CORS origin mismatch ──\n // Check if CORS is configured but the origin doesn't match APP_URL or frontend URL\n for (const f of files) {\n if (!/(?:server|app|index|main)\\.(ts|js)$/i.test(f.path)) continue;\n const corsMatch = f.content.match(/origin\\s*:\\s*[\"'`](https?:\\/\\/[^\"'`]+)[\"'`]/);\n if (corsMatch) {\n const corsOrigin = corsMatch[1];\n if (corsOrigin.includes(\"localhost\") || corsOrigin.includes(\"127.0.0.1\")) {\n const line = f.content.split(\"\\n\").findIndex(l => l.includes(corsOrigin)) + 1;\n findings.push({\n id: `MFA004-${f.path}:${line}`,\n rule: \"MFA004\",\n severity: \"medium\",\n title: \"CORS Origin Set to Localhost\",\n description: \"CORS origin is hardcoded to localhost. This will block requests from your production frontend.\",\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use environment variable: origin: process.env.FRONTEND_URL || 'http://localhost:3000'\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-942\",\n });\n }\n }\n }\n\n // ── Check 5: Secret referenced but not in .env.example ──\n const envExample = files.find(f => /\\.env\\.example$|\\.env\\.sample$|\\.env\\.template$/.test(f.path));\n if (envExample) {\n const envVarsUsed = new Set<string>();\n for (const f of files) {\n if (/\\.(ts|js|tsx|jsx)$/i.test(f.path)) {\n const matches = f.content.matchAll(/process\\.env\\.([A-Z_][A-Z0-9_]*)/g);\n for (const m of matches) {\n envVarsUsed.add(m[1]);\n }\n }\n }\n const envVarsDocumented = new Set<string>();\n const exampleMatches = envExample.content.matchAll(/^([A-Z_][A-Z0-9_]*)=/gm);\n for (const m of exampleMatches) {\n envVarsDocumented.add(m[1]);\n }\n\n const undocumented = [...envVarsUsed].filter(v => !envVarsDocumented.has(v) && !v.startsWith(\"NODE_\") && v !== \"npm_\");\n if (undocumented.length > 0) {\n findings.push({\n id: `MFA005-${envExample.path}:1`,\n rule: \"MFA005\",\n severity: \"low\",\n title: \"Env Vars Used But Not in .env.example\",\n description: `These environment variables are used in code but missing from .env.example: ${undocumented.slice(0, 5).join(\", \")}${undocumented.length > 5 ? ` (+${undocumented.length - 5} more)` : \"\"}`,\n file: envExample.path,\n line: 1,\n snippet: getSnippet(envExample.content, 1),\n fix: \"Add missing variables to .env.example so team members know which env vars are needed.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A05:2021\",\n cwe: \"CWE-1188\",\n });\n }\n }\n\n // ── Check 6: Frontend fetching HTTP API in production code ──\n for (const f of files) {\n if (!/\\.(tsx|jsx|ts|js)$/i.test(f.path)) continue;\n if (/(?:test|spec|mock|fixture)/i.test(f.path)) continue;\n\n const httpFetches = f.content.matchAll(/fetch\\s*\\(\\s*[\"'`](http:\\/\\/(?!localhost|127\\.0\\.0\\.1)[^\"'`]+)[\"'`]/g);\n for (const m of httpFetches) {\n const line = f.content.substring(0, m.index).split(\"\\n\").length;\n findings.push({\n id: `MFA006-${f.path}:${line}`,\n rule: \"MFA006\",\n severity: \"high\",\n title: \"Frontend Fetching Over HTTP (Not HTTPS)\",\n description: `Fetching from ${m[1].substring(0, 40)}... over HTTP exposes data to interception.`,\n file: f.path,\n line,\n snippet: getSnippet(f.content, line),\n fix: \"Use HTTPS for all API calls in production.\",\n category: \"Configuration\",\n source: \"custom\",\n owasp: \"A02:2021\",\n cwe: \"CWE-319\",\n });\n }\n }\n\n return findings;\n}\n","import chalk from \"chalk\";\nimport Table from \"cli-table3\";\nimport type { Finding, ScanResult, Severity } from \"../types.js\";\nimport { calculateGrade, detectFramework, type SecurityGrade } from \"../scanners/custom-rules.js\";\n\nconst SEVERITY_COLORS: Record<Severity, (text: string) => string> = {\n critical: chalk.bgRed.white.bold,\n high: chalk.red.bold,\n medium: chalk.yellow.bold,\n low: chalk.blue,\n info: chalk.gray,\n};\n\nconst SEVERITY_ICONS: Record<Severity, string> = {\n critical: \"!!!\",\n high: \" !! \",\n medium: \" ! \",\n low: \" - \",\n info: \" i \",\n};\n\nconst SEVERITY_ORDER: Record<Severity, number> = {\n critical: 0,\n high: 1,\n medium: 2,\n low: 3,\n info: 4,\n};\n\nconst GRADE_COLORS: Record<SecurityGrade, (text: string) => string> = {\n \"A+\": chalk.green.bold,\n \"A\": chalk.green.bold,\n \"B\": chalk.cyan.bold,\n \"C\": chalk.yellow.bold,\n \"D\": chalk.red.bold,\n \"F\": chalk.bgRed.white.bold,\n};\n\nexport function renderTerminalReport(result: ScanResult, files?: { path: string; content: string }[]): void {\n const { findings, filesScanned, duration } = result;\n\n // Header\n console.log(\"\");\n console.log(chalk.bold.cyan(\" xploitscan\") + chalk.gray(\" — security scan results\"));\n console.log(chalk.gray(\" \" + \"─\".repeat(50)));\n console.log(\"\");\n\n // Framework detection\n if (files && files.length > 0) {\n const frameworks = detectFramework(files);\n if (frameworks.length > 0 && frameworks[0] !== \"unknown\") {\n console.log(chalk.gray(\" Frameworks: \") + chalk.white(frameworks.join(\", \")));\n }\n }\n\n // Grade + Benchmark\n const GRADE_PERCENTILES: Record<string, number> = { \"A+\": 98, \"A\": 90, \"B\": 70, \"C\": 45, \"D\": 20, \"F\": 5 };\n const { grade, score, summary } = calculateGrade(findings, filesScanned);\n const gradeColor = GRADE_COLORS[grade];\n const percentile = GRADE_PERCENTILES[grade] ?? 50;\n console.log(chalk.gray(\" Security Grade: \") + gradeColor(` ${grade} `) + chalk.gray(` (${score}/100) — ${summary}`));\n console.log(chalk.gray(` Benchmark: More secure than ${percentile}% of projects scanned`));\n console.log(\"\");\n\n if (findings.length === 0) {\n console.log(chalk.green.bold(\" No vulnerabilities found!\"));\n console.log(chalk.gray(` Scanned ${filesScanned} files in ${(duration / 1000).toFixed(1)}s`));\n console.log(\"\");\n return;\n }\n\n // Sort by severity\n const sorted = [...findings].sort(\n (a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity],\n );\n\n // Summary table\n const counts: Record<Severity, number> = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };\n for (const f of findings) counts[f.severity]++;\n\n const summaryParts: string[] = [];\n if (counts.critical > 0) summaryParts.push(chalk.bgRed.white.bold(` ${counts.critical} CRITICAL `));\n if (counts.high > 0) summaryParts.push(chalk.red.bold(`${counts.high} high`));\n if (counts.medium > 0) summaryParts.push(chalk.yellow.bold(`${counts.medium} medium`));\n if (counts.low > 0) summaryParts.push(chalk.blue(`${counts.low} low`));\n if (counts.info > 0) summaryParts.push(chalk.gray(`${counts.info} info`));\n\n console.log(` Found ${chalk.bold(findings.length.toString())} issues: ${summaryParts.join(chalk.gray(\" | \"))}`);\n console.log(chalk.gray(` Scanned ${filesScanned} files in ${(duration / 1000).toFixed(1)}s`));\n console.log(\"\");\n\n // Individual findings\n for (const finding of sorted) {\n const severityLabel = SEVERITY_COLORS[finding.severity](\n ` ${finding.severity.toUpperCase()} `,\n );\n const sourceLabel = finding.source === \"ai\"\n ? chalk.magenta(\" [AI] \")\n : finding.source === \"dependency\"\n ? chalk.cyan(\" [DEP] \")\n : finding.source === \"entropy\"\n ? chalk.yellow(\" [ENTROPY] \")\n : finding.source === \"config\"\n ? chalk.blue(\" [CFG] \")\n : finding.source === \"multi-file\"\n ? chalk.green(\" [CROSS-FILE] \")\n : chalk.gray(` [${finding.rule}] `);\n const confidenceLabel = finding.confidence\n ? chalk.gray(` (${finding.confidence} confidence)`)\n : \"\";\n\n const complianceTags = [\n finding.owasp ? chalk.yellow(`[${finding.owasp}]`) : \"\",\n finding.cwe ? chalk.blue(`[${finding.cwe}]`) : \"\",\n ].filter(Boolean).join(\" \");\n console.log(` ${severityLabel}${sourceLabel}${chalk.bold(finding.title)} ${complianceTags}${confidenceLabel}`);\n console.log(chalk.gray(` ${finding.file}:${finding.line}`));\n console.log(\"\");\n\n // Description\n console.log(chalk.white(` ${finding.description}`));\n console.log(\"\");\n\n // Code snippet\n if (finding.snippet) {\n const snippetLines = finding.snippet.split(\"\\n\");\n for (const line of snippetLines) {\n if (line.startsWith(\">\")) {\n console.log(chalk.red(` ${line}`));\n } else {\n console.log(chalk.gray(` ${line}`));\n }\n }\n console.log(\"\");\n }\n\n // Fix suggestion\n if (finding.fix) {\n console.log(chalk.green(` Fix: ${finding.fix}`));\n console.log(\"\");\n }\n\n console.log(chalk.gray(\" \" + \"─\".repeat(50)));\n console.log(\"\");\n }\n\n // OWASP Top 10 Summary\n const owaspFindings = findings.filter(f => f.owasp);\n if (owaspFindings.length > 0) {\n const owaspCats: Record<string, { name: string; count: number }> = {\n \"A01:2021\": { name: \"Broken Access Control\", count: 0 },\n \"A02:2021\": { name: \"Cryptographic Failures\", count: 0 },\n \"A03:2021\": { name: \"Injection\", count: 0 },\n \"A04:2021\": { name: \"Insecure Design\", count: 0 },\n \"A05:2021\": { name: \"Security Misconfiguration\", count: 0 },\n \"A06:2021\": { name: \"Vulnerable Components\", count: 0 },\n \"A07:2021\": { name: \"Auth Failures\", count: 0 },\n \"A08:2021\": { name: \"Data Integrity\", count: 0 },\n \"A09:2021\": { name: \"Logging Failures\", count: 0 },\n \"A10:2021\": { name: \"SSRF\", count: 0 },\n };\n for (const f of owaspFindings) {\n if (f.owasp && owaspCats[f.owasp]) owaspCats[f.owasp].count++;\n }\n console.log(chalk.bold(\" OWASP Top 10 Compliance\"));\n for (const [id, { name, count }] of Object.entries(owaspCats)) {\n const status = count > 0 ? chalk.red(`${count} issue${count > 1 ? \"s\" : \"\"}`) : chalk.green(\"PASS\");\n console.log(chalk.gray(` ${id} ${name}: `) + status);\n }\n console.log(\"\");\n }\n\n // Footer\n if (counts.critical > 0) {\n console.log(\n chalk.bgRed.white.bold(\" ACTION REQUIRED \") +\n chalk.red.bold(` ${counts.critical} critical issue${counts.critical > 1 ? \"s\" : \"\"} found. Fix these before deploying.`),\n );\n } else if (counts.high > 0) {\n console.log(\n chalk.yellow.bold(` Recommendation: Address the ${counts.high} high-severity issue${counts.high > 1 ? \"s\" : \"\"} before going to production.`),\n );\n }\n\n console.log(\"\");\n}\n","import type { ScanResult } from \"../types.js\";\n\nexport function renderJsonReport(result: ScanResult): void {\n console.log(JSON.stringify(result, null, 2));\n}\n","import type { Finding, ScanResult, Severity } from \"../types.js\";\n\nconst SEVERITY_TO_SARIF: Record<Severity, string> = {\n critical: \"error\",\n high: \"error\",\n medium: \"warning\",\n low: \"note\",\n info: \"none\",\n};\n\nconst SEVERITY_TO_LEVEL: Record<Severity, number> = {\n critical: 10.0,\n high: 8.0,\n medium: 5.0,\n low: 3.0,\n info: 1.0,\n};\n\ninterface SarifOutput {\n $schema: string;\n version: string;\n runs: Array<{\n tool: {\n driver: {\n name: string;\n version: string;\n informationUri: string;\n rules: Array<{\n id: string;\n shortDescription: { text: string };\n fullDescription: { text: string };\n defaultConfiguration: { level: string };\n properties: { security_severity: string; tags: string[] };\n }>;\n };\n };\n results: Array<{\n ruleId: string;\n ruleIndex: number;\n level: string;\n message: { text: string };\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string };\n region: {\n startLine: number;\n startColumn?: number;\n };\n };\n }>;\n }>;\n }>;\n}\n\nexport function renderSarifReport(result: ScanResult): void {\n // Collect unique rules\n const ruleMap = new Map<string, Finding>();\n for (const f of result.findings) {\n if (!ruleMap.has(f.rule)) {\n ruleMap.set(f.rule, f);\n }\n }\n\n const rules = Array.from(ruleMap.entries()).map(([id, f]) => ({\n id,\n shortDescription: { text: f.title },\n fullDescription: { text: f.description },\n defaultConfiguration: { level: SEVERITY_TO_SARIF[f.severity] },\n properties: {\n security_severity: SEVERITY_TO_LEVEL[f.severity].toFixed(1),\n tags: [\"security\", f.category.toLowerCase().replace(/\\s+/g, \"-\")],\n },\n }));\n\n const ruleIndex = new Map(rules.map((r, i) => [r.id, i]));\n\n const sarif: SarifOutput = {\n $schema: \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"xploitscan\",\n version: \"0.1.0\",\n informationUri: \"https://github.com/bgage72590/xploitscan\",\n rules,\n },\n },\n results: result.findings.map((f) => {\n // SARIF schema requires that any `fixes[]` entry include an\n // `artifactChanges` array, which we don't have because findings\n // are pattern-based, not AST rewrites. Inline the fix text into\n // the message instead so GitHub still surfaces it.\n const messageText = f.fix\n ? `${f.title}: ${f.description}\\nSuggested fix: ${f.fix}`\n : `${f.title}: ${f.description}`;\n return {\n ruleId: f.rule,\n ruleIndex: ruleIndex.get(f.rule) ?? 0,\n level: SEVERITY_TO_SARIF[f.severity],\n message: { text: messageText },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: f.file },\n region: {\n startLine: f.line,\n ...(f.column ? { startColumn: f.column } : {}),\n },\n },\n },\n ],\n };\n }),\n },\n ],\n };\n\n console.log(JSON.stringify(sarif, null, 2));\n}\n","import type { ScanResult, Finding } from \"../types.js\";\n\n/**\n * SIEM-compatible output reporters.\n *\n * Most SIEMs can ingest plain JSON, but each platform expects a specific\n * schema so alerts, dashboards, and detections work out of the box. These\n * reporters emit one event per finding, each event shaped for the target\n * platform. All three are NDJSON (one JSON object per line) — the standard\n * wire format for log forwarders.\n *\n * Usage examples in the CLI:\n * xploitscan scan . --format splunk-hec | curl -H \"Authorization: Splunk <token>\" \\\n * --data-binary @- https://splunk.example.com:8088/services/collector/event\n *\n * xploitscan scan . --format elastic-ecs > findings.ndjson\n * # then: filebeat → Elasticsearch pipeline, or direct POST to _bulk\n *\n * xploitscan scan . --format datadog-logs | \\\n * curl -X POST -H \"DD-API-KEY: $DD_API_KEY\" \\\n * -H \"Content-Type: application/json\" \\\n * --data-binary @- https://http-intake.logs.datadoghq.com/api/v2/logs\n */\n\n// Severity mappings — each SIEM has its own canonical severity scale.\n// XploitScan's 4-level severity maps cleanly to most of them.\n\nconst SPLUNK_SEVERITY: Record<string, string> = {\n critical: \"critical\",\n high: \"high\",\n medium: \"medium\",\n low: \"low\",\n};\n\nconst ECS_SEVERITY: Record<string, number> = {\n // Elastic Common Schema uses 0–100 integer severity.\n critical: 90,\n high: 70,\n medium: 50,\n low: 30,\n};\n\nconst DATADOG_STATUS: Record<string, string> = {\n // Datadog Logs status field accepts emergency/alert/critical/error/warning/notice/info/debug.\n critical: \"critical\",\n high: \"error\",\n medium: \"warning\",\n low: \"info\",\n};\n\nfunction commonFields(result: ScanResult, finding: Finding) {\n return {\n rule: finding.rule,\n title: finding.title,\n description: finding.description,\n severity: finding.severity,\n file: finding.file,\n line: finding.line,\n category: finding.category,\n cwe: finding.cwe ?? null,\n owasp: finding.owasp ?? null,\n fix: finding.fix ?? null,\n scan_timestamp: result.timestamp,\n scan_directory: result.directory,\n files_scanned: result.filesScanned,\n scan_duration_ms: result.duration,\n };\n}\n\n// ────────────────────────────────────────────\n// Splunk HEC (HTTP Event Collector) format\n// ────────────────────────────────────────────\n\n/**\n * Splunk HEC event format:\n * { \"event\": { ...fields }, \"sourcetype\": \"xploitscan:finding\",\n * \"source\": \"xploitscan\", \"index\": \"security\", \"time\": <epoch> }\n *\n * One JSON object per line. POST the whole NDJSON body to the HEC\n * endpoint; Splunk will ingest each line as a separate event.\n */\nexport function renderSplunkReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n time: Math.floor(new Date(result.timestamp).getTime() / 1000),\n sourcetype: \"xploitscan:finding\",\n source: \"xploitscan\",\n event: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n\n// ────────────────────────────────────────────\n// Elastic Common Schema (ECS) — Elasticsearch / OpenSearch / Kibana\n// ────────────────────────────────────────────\n\n/**\n * ECS-compliant event. Uses the `vulnerability.*`, `event.*`, and\n * `@timestamp` fields Elastic's security apps already know how to render.\n * Compatible with Elastic Security and OpenSearch Security Analytics\n * without custom mappings.\n */\nexport function renderElasticReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n \"@timestamp\": result.timestamp,\n ecs: { version: \"8.11.0\" },\n event: {\n kind: \"alert\",\n category: [\"vulnerability\"],\n type: [\"info\"],\n dataset: \"xploitscan.finding\",\n module: \"xploitscan\",\n severity: ECS_SEVERITY[finding.severity] ?? 50,\n action: finding.rule,\n },\n vulnerability: {\n id: finding.rule,\n category: [finding.category],\n description: finding.description,\n severity: finding.severity,\n classification: finding.owasp ?? \"\",\n reference: finding.cwe\n ? `https://cwe.mitre.org/data/definitions/${finding.cwe.replace(/[^\\d]/g, \"\")}.html`\n : \"\",\n },\n file: { path: finding.file },\n log: { level: finding.severity },\n message: `${finding.rule}: ${finding.title}`,\n xploitscan: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n\n// ────────────────────────────────────────────\n// Datadog Logs API format\n// ────────────────────────────────────────────\n\n/**\n * Datadog Logs API v2 format. Includes the tags Datadog uses to pivot\n * dashboards and trigger monitors. POST as NDJSON to /api/v2/logs.\n */\nexport function renderDatadogReport(result: ScanResult): void {\n for (const finding of result.findings) {\n const event = {\n ddsource: \"xploitscan\",\n ddtags: [\n `severity:${finding.severity}`,\n `rule:${finding.rule}`,\n `category:${finding.category.toLowerCase().replace(/\\s+/g, \"_\")}`,\n finding.owasp ? `owasp:${finding.owasp.replace(/\\s+/g, \"_\")}` : \"\",\n finding.cwe ? `cwe:${finding.cwe}` : \"\",\n ]\n .filter(Boolean)\n .join(\",\"),\n service: \"xploitscan\",\n hostname: \"xploitscan-cli\",\n status: DATADOG_STATUS[finding.severity] ?? \"info\",\n message: `${finding.rule}: ${finding.title} — ${finding.description}`,\n timestamp: result.timestamp,\n xploitscan: commonFields(result, finding),\n };\n console.log(JSON.stringify(event));\n }\n}\n","import { createServer } from \"node:http\";\nimport { URL } from \"node:url\";\nimport { randomUUID } from \"node:crypto\";\nimport { execFile } from \"node:child_process\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { storeToken, clearToken, getStoredToken, syncUser, clearProRulesCache } from \"../utils/api.js\";\n\nconst CLERK_PUBLISHABLE_KEY = process.env.CLERK_PUBLISHABLE_KEY ?? \"\";\n\n/**\n * Base URL of the xploitscan web app that hosts the CLI-login bridge page.\n * Override via env when testing against a local Next.js dev server, e.g.\n * XPLOITSCAN_WEB_BASE=http://localhost:3847 npx xploitscan auth login\n * Defaults to production.\n */\nconst WEB_BASE = process.env.XPLOITSCAN_WEB_BASE ?? \"https://xploitscan.com\";\n\n/**\n * Opens a browser-based login flow.\n * 1. Starts a local HTTP server on a random port\n * 2. Opens the Clerk sign-in page with redirect back to local server\n * 3. Receives the token via redirect callback\n * 4. Stores the token locally\n */\nexport async function loginCommand(): Promise<void> {\n const existing = getStoredToken();\n if (existing) {\n console.log(chalk.yellow(`Already logged in as ${existing.email}`));\n console.log(chalk.gray(\"Run `xploitscan auth logout` first to switch accounts.\"));\n return;\n }\n\n const spinner = ora(\"Waiting for browser login...\").start();\n\n // Start local callback server\n const { token, email, userId } = await waitForBrowserLogin();\n\n spinner.text = \"Syncing account...\";\n\n // Store token\n storeToken({\n token,\n userId,\n email,\n expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days\n });\n\n // Sync user with API\n const user = await syncUser();\n spinner.stop();\n\n console.log(chalk.green(`Logged in as ${email}`));\n if (user) {\n console.log(chalk.gray(`Plan: ${user.plan}`));\n }\n}\n\nexport async function logoutCommand(): Promise<void> {\n const existing = getStoredToken();\n if (!existing) {\n console.log(chalk.gray(\"Not logged in.\"));\n return;\n }\n\n clearToken();\n clearProRulesCache();\n console.log(chalk.green(\"Logged out successfully.\"));\n}\n\nexport async function whoamiCommand(): Promise<void> {\n const token = getStoredToken();\n if (!token) {\n console.log(chalk.gray(\"Not logged in. Run `xploitscan auth login` to authenticate.\"));\n return;\n }\n\n console.log(chalk.cyan(`Email: ${token.email}`));\n console.log(chalk.gray(`User ID: ${token.userId}`));\n\n const user = await syncUser();\n if (user) {\n const planBadge = user.plan === \"pro\"\n ? chalk.bgGreen.black(\" PRO \")\n : chalk.bgGray.white(\" FREE \");\n console.log(`Plan: ${planBadge}`);\n }\n}\n\nasync function waitForBrowserLogin(): Promise<{\n token: string;\n email: string;\n userId: string;\n}> {\n return new Promise((resolve, reject) => {\n const expectedState = randomUUID();\n\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400);\n res.end(\"Bad request\");\n return;\n }\n\n const url = new URL(req.url, `http://localhost`);\n\n if (url.pathname === \"/callback\") {\n const token = url.searchParams.get(\"token\");\n const email = url.searchParams.get(\"email\");\n const userId = url.searchParams.get(\"user_id\");\n const state = url.searchParams.get(\"state\");\n\n if (!state || state !== expectedState) {\n res.writeHead(403);\n res.end(\"Invalid state parameter — possible CSRF attack.\");\n return;\n }\n\n if (token && email && userId) {\n res.writeHead(200, { \"Content-Type\": \"text/html\" });\n res.end(`\n <html>\n <body style=\"font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;\">\n <div style=\"text-align: center;\">\n <h1 style=\"color: #00d4ff;\">xploitscan</h1>\n <p style=\"color: #4ade80; font-size: 1.2rem;\">Login successful!</p>\n <p style=\"color: #888;\">You can close this tab and return to your terminal.</p>\n </div>\n </body>\n </html>\n `);\n\n server.close();\n resolve({ token, email, userId });\n } else {\n res.writeHead(400);\n res.end(\"Missing parameters\");\n }\n } else if (url.pathname === \"/login\") {\n // Serve a launch page that bounces the browser to the web-side\n // /auth/cli-login bridge. That page validates the callback URL,\n // ensures the user is signed in via Clerk (handles signup if not),\n // mints a CLI API key for them, and redirects back to /callback\n // here with token + email + user_id + state attached.\n //\n // Previous version of this handler had no redirect at all — just\n // a static \"Redirecting to login...\" page with a click-here link\n // pointing at `#`. The flow was a dead end. Three redundant\n // redirect mechanisms below so it works under any reasonable\n // browser config: HTTP 302 status (primary), <meta refresh>\n // fallback, JavaScript window.location.replace fallback, and a\n // visible anchor tag if all three fail.\n const port = (server.address() as { port: number }).port;\n const callbackUrl = `http://localhost:${port}/callback`;\n const cliLoginUrl =\n `${WEB_BASE}/auth/cli-login` +\n `?callback=${encodeURIComponent(callbackUrl)}` +\n `&state=${encodeURIComponent(expectedState)}`;\n\n res.writeHead(302, { Location: cliLoginUrl, \"Content-Type\": \"text/html\" });\n res.end(`<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"refresh\" content=\"0; url=${cliLoginUrl}\" />\n <title>xploitscan login</title>\n </head>\n <body style=\"font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;\">\n <div style=\"text-align: center;\">\n <h1 style=\"color: #00d4ff;\">xploitscan</h1>\n <p>Redirecting to login...</p>\n <p style=\"color: #888; font-size: 0.85rem;\">\n If not redirected, <a href=\"${cliLoginUrl}\" style=\"color: #00d4ff;\">click here</a>.\n </p>\n </div>\n <script>window.location.replace(${JSON.stringify(cliLoginUrl)});</script>\n </body>\n</html>`);\n } else {\n res.writeHead(302, { Location: \"/login\" });\n res.end();\n }\n });\n\n // Listen on random available port\n server.listen(0, \"127.0.0.1\", () => {\n const addr = server.address() as { port: number };\n const loginUrl = `http://localhost:${addr.port}/login`;\n\n console.log(chalk.cyan(`\\nOpen this URL in your browser to log in:`));\n console.log(chalk.bold.underline(loginUrl));\n console.log(\"\");\n\n // Try to open browser automatically\n const openCmd = process.platform === \"darwin\" ? \"open\" : process.platform === \"win32\" ? \"start\" : \"xdg-open\";\n execFile(openCmd, [loginUrl], () => {});\n });\n\n // Timeout after 5 minutes\n setTimeout(() => {\n server.close();\n reject(new Error(\"Login timed out. Please try again.\"));\n }, 5 * 60 * 1000);\n });\n}\n","import { execSync } from \"node:child_process\";\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n chmodSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\n\nconst HOOK_START_MARKER = \"# xploitscan-hook-start\";\nconst HOOK_END_MARKER = \"# xploitscan-hook-end\";\n\nconst HOOK_CONTENT = `${HOOK_START_MARKER}\n# Installed by XploitScan CLI — do not edit this block manually\necho \"\"\necho \"🛡 Running XploitScan security check...\"\nnpx --yes xploitscan scan . --no-ai --diff HEAD\nHOOK_EXIT=$?\nif [ $HOOK_EXIT -ne 0 ]; then\n echo \"\"\n echo \"❌ XploitScan found security issues. Commit blocked.\"\n echo \" Fix the issues above, or use 'git commit --no-verify' to skip.\"\n exit 1\nfi\n${HOOK_END_MARKER}`;\n\nfunction getGitRoot(): string | null {\n try {\n const root = execSync(\"git rev-parse --show-toplevel\", {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n }).trim();\n return root || null;\n } catch {\n return null;\n }\n}\n\nfunction removeExistingBlock(content: string): string {\n const startIdx = content.indexOf(HOOK_START_MARKER);\n const endIdx = content.indexOf(HOOK_END_MARKER);\n if (startIdx === -1 || endIdx === -1 || endIdx < startIdx) {\n return content;\n }\n const before = content.slice(0, startIdx).replace(/\\n+$/, \"\");\n const after = content.slice(endIdx + HOOK_END_MARKER.length).replace(/^\\n+/, \"\");\n if (before && after) return `${before}\\n\\n${after}\\n`;\n if (before) return `${before}\\n`;\n if (after) return `${after}\\n`;\n return \"\";\n}\n\nexport async function installHookCommand(options: { force?: boolean }): Promise<void> {\n const gitRoot = getGitRoot();\n if (!gitRoot) {\n console.log(chalk.red(\"✗ Not a git repository.\"));\n console.log(chalk.gray(\" Run this command from inside a git repo.\"));\n process.exit(1);\n }\n\n const hooksDir = join(gitRoot, \".git\", \"hooks\");\n const hookPath = join(hooksDir, \"pre-commit\");\n\n let existingContent = \"\";\n let hadExisting = false;\n if (existsSync(hookPath)) {\n existingContent = readFileSync(hookPath, \"utf-8\");\n hadExisting = true;\n\n const alreadyInstalled =\n existingContent.includes(HOOK_START_MARKER) && existingContent.includes(HOOK_END_MARKER);\n\n if (alreadyInstalled && !options.force) {\n console.log(chalk.yellow(\"⚠ XploitScan hook is already installed.\"));\n console.log(chalk.gray(\" Use --force to reinstall, or `xploitscan hook uninstall` to remove it.\"));\n return;\n }\n\n if (alreadyInstalled && options.force) {\n existingContent = removeExistingBlock(existingContent);\n }\n }\n\n // Compose new hook file\n let newContent: string;\n if (hadExisting && existingContent.trim()) {\n // Preserve existing hook content, append our block\n const trimmed = existingContent.replace(/\\n+$/, \"\");\n newContent = `${trimmed}\\n\\n${HOOK_CONTENT}\\n`;\n } else {\n newContent = `#!/bin/sh\\n\\n${HOOK_CONTENT}\\n`;\n }\n\n try {\n writeFileSync(hookPath, newContent, \"utf-8\");\n chmodSync(hookPath, 0o755);\n } catch (err) {\n console.log(chalk.red(`✗ Failed to write pre-commit hook: ${(err as Error).message}`));\n process.exit(1);\n }\n\n console.log(chalk.green(\"✓ XploitScan pre-commit hook installed.\"));\n console.log(\"\");\n console.log(chalk.gray(\" XploitScan will now scan your code before every commit.\"));\n console.log(chalk.gray(\" To skip a scan, use: \") + chalk.cyan(\"git commit --no-verify\"));\n console.log(chalk.gray(\" To remove: \") + chalk.cyan(\"xploitscan hook uninstall\"));\n}\n\nexport async function uninstallHookCommand(): Promise<void> {\n const gitRoot = getGitRoot();\n if (!gitRoot) {\n console.log(chalk.red(\"✗ Not a git repository.\"));\n console.log(chalk.gray(\" Run this command from inside a git repo.\"));\n process.exit(1);\n }\n\n const hookPath = join(gitRoot, \".git\", \"hooks\", \"pre-commit\");\n\n if (!existsSync(hookPath)) {\n console.log(chalk.yellow(\"⚠ No pre-commit hook found. Nothing to uninstall.\"));\n return;\n }\n\n const content = readFileSync(hookPath, \"utf-8\");\n if (!content.includes(HOOK_START_MARKER)) {\n console.log(chalk.yellow(\"⚠ XploitScan hook not found in pre-commit file.\"));\n console.log(chalk.gray(\" Nothing to remove.\"));\n return;\n }\n\n const stripped = removeExistingBlock(content).trim();\n const shebangOnly = stripped === \"#!/bin/sh\" || stripped === \"#!/usr/bin/env sh\" || stripped === \"\";\n\n try {\n if (shebangOnly) {\n unlinkSync(hookPath);\n console.log(chalk.green(\"✓ XploitScan hook removed. Pre-commit file deleted (was empty after removal).\"));\n } else {\n writeFileSync(hookPath, stripped + \"\\n\", \"utf-8\");\n chmodSync(hookPath, 0o755);\n console.log(chalk.green(\"✓ XploitScan hook removed.\"));\n console.log(chalk.gray(\" Other hook content was preserved.\"));\n }\n } catch (err) {\n console.log(chalk.red(`✗ Failed to uninstall hook: ${(err as Error).message}`));\n process.exit(1);\n }\n}\n","import chalk from \"chalk\";\nimport { mkdirSync, existsSync, writeFileSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface InstallOptions {\n force?: boolean;\n legacyOnly?: boolean;\n}\n\n/**\n * Install XploitScan security rules into a project so Cursor enforces them\n * at write-time. Drops:\n * - .cursor/rules/xploitscan-security.mdc (modern context-aware format)\n * - .cursorrules (legacy fallback for older Cursor versions)\n *\n * Idempotent: if files exist, won't overwrite without --force.\n */\nexport async function cursorInstallCommand(opts: InstallOptions = {}): Promise<void> {\n const cwd = process.cwd();\n\n // Resolve template paths relative to this file (works after tsup bundling\n // because we copy templates into dist via tsup config)\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, \"templates\"),\n join(here, \"..\", \"templates\"),\n join(here, \"..\", \"src\", \"templates\"),\n ];\n const templatesDir = candidates.find((p) => existsSync(join(p, \"cursor-security.mdc\")));\n if (!templatesDir) {\n console.error(chalk.red(\"Could not locate XploitScan rule templates. Try reinstalling: npm i -g xploitscan@latest\"));\n process.exit(1);\n }\n\n const mdcSrc = readFileSync(join(templatesDir, \"cursor-security.mdc\"), \"utf-8\");\n const legacySrc = readFileSync(join(templatesDir, \"cursorrules-legacy.txt\"), \"utf-8\");\n\n // Modern format\n if (!opts.legacyOnly) {\n const mdcDir = join(cwd, \".cursor\", \"rules\");\n const mdcPath = join(mdcDir, \"xploitscan-security.mdc\");\n if (existsSync(mdcPath) && !opts.force) {\n console.log(chalk.yellow(`Skipping ${mdcPath} (already exists, use --force to overwrite)`));\n } else {\n mkdirSync(mdcDir, { recursive: true });\n writeFileSync(mdcPath, mdcSrc);\n console.log(chalk.green(`Installed ${mdcPath}`));\n }\n }\n\n // Legacy format\n const legacyPath = join(cwd, \".cursorrules\");\n if (existsSync(legacyPath) && !opts.force) {\n console.log(chalk.yellow(`Skipping ${legacyPath} (already exists, use --force to overwrite)`));\n } else {\n writeFileSync(legacyPath, legacySrc);\n console.log(chalk.green(`Installed ${legacyPath}`));\n }\n\n console.log(\"\");\n console.log(chalk.cyan(\"Cursor will pick up these rules automatically the next time you open the project.\"));\n console.log(chalk.gray(\"Run a scan any time to catch what slipped through: npx xploitscan scan .\"));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,WAAAA,UAAS,QAAAC,aAAsB;AACxC,SAAS,SAAS,eAAe;AACjC,OAAO,SAAS;AAChB,OAAOC,YAAW;;;ACHlB,OAAO,QAAQ;AACf,OAAO,YAAY;AACnB,SAAS,cAAc,kBAAkB;AACzC,SAAS,MAAM,eAAe;AAE9B,IAAM,oBAAoB;AAAA,EACxB;AAAA,EAAM;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAO;AAAA,EACjC;AAAA,EAAO;AAAA,EAAU;AAAA,EACjB;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAQ;AAAA,EAChC;AAAA,EAAS;AAAA,EAAM;AAAA,EAAO;AAAA,EAAQ;AAAA,EAC9B;AAAA,EAAK;AAAA,EAAO;AAAA,EACZ;AAAA,EAAM;AAAA,EAAQ;AAAA,EACd;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACtC;AAAA,EAAQ;AAAA,EAAO;AAAA,EACf;AAAA,EAAc;AAAA,EAAO;AAAA,EAAO;AAAA,EAC5B;AAAA,EAAM;AAAA,EAAO;AAAA,EACb;AAAA,EAAO;AAAA,EAAS;AAAA,EAChB;AAAA,EACA;AAAA,EAAK;AAAA,EAAO;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAC9B;AAAA,EAAS;AAAA,EACT;AAAA,EAAU;AAAA,EAAS;AAAA,EAAW;AAAA,EAAgB;AAAA,EAAS;AACzD;AAEA,IAAM,mBAAmB;AAAA,EACvB;AAAA,EAAc;AAAA,EAAY;AAAA,EAAW;AAAA,EACrC;AAAA,EAAgB;AAAA,EAAc;AAAA,EAAU;AAAA,EAAoB;AAAA,EAC5D;AAAA,EAAkB;AAAA,EAAmB;AAAA,EAAkB;AAAA,EACvD;AAAA,EAAiB;AAAA,EAAe;AAAA,EAChC;AAAA,EAAY;AAAA,EAAiB;AAAA,EAC7B;AAAA,EAAiB;AAAA,EACjB;AAAA,EAAqB;AAAA,EACrB;AAAA,EAAY;AAAA,EAAa;AAAA,EACzB;AACF;AAEA,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,eAAsB,aAAa,WAAsC;AACvE,QAAM,KAAK,OAAO,QAAQ;AAG1B,QAAM,gBAAgB,KAAK,WAAW,YAAY;AAClD,MAAI,WAAW,aAAa,GAAG;AAC7B,UAAM,mBAAmB,aAAa,eAAe,OAAO;AAC5D,OAAG,IAAI,gBAAgB;AAAA,EACzB;AAGA,QAAM,uBAAuB,KAAK,WAAW,mBAAmB;AAChE,MAAI,WAAW,oBAAoB,GAAG;AACpC,UAAM,0BAA0B,aAAa,sBAAsB,OAAO;AAC1E,OAAG,IAAI,uBAAuB;AAAA,EAChC;AAGA,KAAG,IAAI,aAAa;AAEpB,QAAM,WAAW,kBAAkB,IAAI,CAAC,QAAQ,QAAQ,GAAG,EAAE;AAE7D,WAAS,KAAK,UAAU;AAExB,aAAW,QAAQ,kBAAkB;AACnC,aAAS,KAAK,MAAM,IAAI,EAAE;AAAA,EAC5B;AAEA,QAAM,QAAQ,MAAM,GAAG,UAAU;AAAA,IAC/B,KAAK;AAAA,IACL,UAAU;AAAA,IACV,KAAK;AAAA,IACL,WAAW;AAAA,IACX,QAAQ,cAAc,IAAI,CAAC,MAAM,MAAM,CAAC,EAAE;AAAA,EAC5C,CAAC;AAGD,SAAO,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,QAAQ,IAAI,CAAC;AACjD;AAEO,SAAS,iBACd,WACA,UACe;AACf,MAAI;AACF,UAAM,WAAW,QAAQ,KAAK,WAAW,QAAQ,CAAC;AAClD,QAAI,CAAC,SAAS,WAAW,QAAQ,SAAS,CAAC,GAAG;AAC5C,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,WAAO,aAAa,UAAU,OAAO;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WACd,SACA,MACA,eAAe,GACP;AACR,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,IAAI,YAAY;AACjD,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,OAAO,YAAY;AAEtD,SAAO,MACJ,MAAM,OAAO,GAAG,EAChB,IAAI,CAAC,GAAG,MAAM;AACb,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,YAAY,OAAO,MAAM;AACxC,WAAO,GAAG,MAAM,IAAI,QAAQ,SAAS,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,EAC3D,CAAC,EACA,KAAK,IAAI;AACd;;;ACnJA,SAAS,mBAAmB;AAC5B,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,QAAAC,aAAY;AAgDrB,IAAM,WAA6B;AAAA,EACjC,SAAS,CAAC;AAAA,EACV,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,cAAc,CAAC;AACjB;AAEA,SAAS,iBAAiB,WAA8C;AACtE,MAAI;AACF,UAAM,SAASA,MAAK,WAAW,eAAe;AAC9C,UAAM,UAAUD,cAAa,QAAQ,OAAO;AAC5C,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBACP,MACA,IACkB;AAClB,QAAM,SAAS,EAAE,GAAG,KAAK;AAGzB,MAAI,GAAG,OAAO;AACZ,QAAI,GAAG,MAAM,SAAS;AACpB,aAAO,eAAe;AAAA,QACpB,GAAG,oBAAI,IAAI,CAAC,GAAI,OAAO,gBAAgB,CAAC,GAAI,GAAG,GAAG,MAAM,OAAO,CAAC;AAAA,MAClE;AAAA,IACF;AACA,QAAI,GAAG,MAAM,kBAAkB;AAC7B,aAAO,mBAAmB;AAAA,QACxB,GAAI,OAAO,oBAAoB,CAAC;AAAA,QAChC,GAAG,GAAG,MAAM;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAGA,MAAI,GAAG,MAAM;AACX,QAAI,GAAG,KAAK,SAAS;AACnB,aAAO,UAAU;AAAA,QACf,GAAG,oBAAI,IAAI,CAAC,GAAI,OAAO,WAAW,CAAC,GAAI,GAAG,GAAG,KAAK,OAAO,CAAC;AAAA,MAC5D;AAAA,IACF;AACA,QAAI,GAAG,KAAK,gBAAgB,QAAW;AACrC,aAAO,cAAc,GAAG,KAAK;AAAA,IAC/B;AACA,QAAI,GAAG,KAAK,YAAY;AACtB,aAAO,aAAa;AAAA,QAClB,GAAG,oBAAI,IAAI,CAAC,GAAI,OAAO,cAAc,CAAC,GAAI,GAAG,GAAG,KAAK,UAAU,CAAC;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAGA,MAAI,GAAG,QAAQ;AACb,QAAI,GAAG,OAAO,WAAW,QAAW;AAClC,aAAO,SAAS,GAAG,OAAO;AAAA,IAC5B;AACA,QAAI,GAAG,OAAO,YAAY,QAAW;AACnC,aAAO,UAAU,GAAG,OAAO;AAAA,IAC7B;AAAA,EACF;AAGA,MAAI,GAAG,OAAO;AACZ,WAAO,QAAQ;AAAA,MACb,GAAI,OAAO,SAAS,CAAC;AAAA,MACrB,GAAG,GAAG;AAAA,IACR;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,WACpB,WAC2B;AAC3B,QAAM,WAAW,YAAY,YAAY;AAEzC,MAAI,SAAS,EAAE,GAAG,SAAS;AAE3B,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,OAAO,SAAS;AAC9C,QAAI,UAAU,OAAO,QAAQ;AAC3B,eAAS,EAAE,GAAG,UAAU,GAAG,OAAO,OAAO;AAAA,IAC3C;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,KAAK,iBAAiB,SAAS;AACrC,MAAI,IAAI;AACN,aAAS,kBAAkB,QAAQ,EAAE;AAAA,EACvC;AAEA,SAAO;AACT;;;ACrJA,SAAS,gBAAgB;AACzB,SAAS,cAAAE,mBAAkB;AAC3B,SAAS,UAAqB,SAAS,UAAU;AACjD,SAAS,QAAAC,aAAY;AACrB,SAAS,cAAc;AAuBvB,IAAM,eAAyC;AAAA,EAC7C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AACR;AAEA,SAAS,4BAA4B,OAAyB;AAC5D,SAAO,aAAa,KAAK,KAAK;AAChC;AAEA,eAAe,qBAAuC;AACpD,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,aAAS,WAAW,CAAC,WAAW,GAAG,CAAC,UAAU;AAC5C,MAAAA,SAAQ,CAAC,KAAK;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,WACpB,WACA,gBACsD;AACtD,QAAM,YAAY,MAAM,mBAAmB;AAC3C,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,UAAU,CAAC,GAAG,WAAW,MAAM;AAAA,EAC1C;AAEA,QAAM,WAAsB,CAAC;AAG7B,QAAM,SAAS,MAAM,QAAQD,MAAK,OAAO,GAAG,qBAAqB,CAAC;AAClE,QAAM,YAAYA,MAAK,QAAQ,eAAe;AAE9C,MAAI;AAEF,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MAAY;AAAA,MACZ;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MAAa;AAAA,MACb;AAAA,MAAsB;AAAA,IACxB;AAGA,SAAK,KAAK,YAAY,MAAM;AAE5B,QAAI,kBAAkBD,YAAW,cAAc,GAAG;AAChD,WAAK,KAAK,YAAY,cAAc;AAAA,IACtC;AAEA,SAAK,KAAK,SAAS;AAGnB,UAAM,IAAI,QAAc,CAACE,UAAS,WAAW;AAC3C,YAAM,OAAO;AAAA,QACX;AAAA,QACA;AAAA,QACA,EAAE,SAAS,MAAS,WAAW,KAAK,OAAO,KAAK;AAAA,QAChD,CAAC,OAAO,SAAS,WAAW;AAE1B,cAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,mBAAO,IAAI,MAAM,mBAAmB,UAAU,MAAM,OAAO,EAAE,CAAC;AAAA,UAChE,OAAO;AACL,YAAAA,SAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,CAACF,YAAW,SAAS,EAAG,QAAO,EAAE,UAAU,WAAW,KAAK;AAE/D,UAAM,eAAe,MAAM,SAAS,WAAW,OAAO;AACtD,UAAM,QAA4B,KAAK,MAAM,YAAY;AAEzD,eAAW,OAAO,MAAM,QAAQ,CAAC,GAAG;AAClC,iBAAW,UAAU,IAAI,WAAW,CAAC,GAAG;AACtC,cAAM,WAAW,OAAO,YAAY,CAAC,GAAG;AACxC,cAAM,WAAW,UAAU,kBAAkB,OAAO;AACpD,cAAM,OAAO,UAAU,QAAQ,aAAa;AAC5C,cAAM,UAAU,UAAU,QAAQ,SAAS,QAAQ;AAGnD,cAAM,SAAS,OAAO,UAAU;AAChC,cAAM,WAAW,sBAAsB,MAAM;AAE7C,iBAAS,KAAK;AAAA,UACZ,IAAI,MAAM,QAAQ,IAAI,IAAI,IAAI,MAAM;AAAA,UACpC,MAAM;AAAA,UACN,UAAU,4BAA4B,OAAO,SAAS,SAAS;AAAA,UAC/D,OAAO,SAAS,OAAO,SAAS,QAAQ,QAAQ,GAAG;AAAA,UACnD,aAAa,OAAO,SAAS,QAAQ;AAAA,UACrC,MAAM,SAAS,QAAQ,cAAc,EAAE;AAAA,UACvC;AAAA,UACA,QAAQ,UAAU,QAAQ;AAAA,UAC1B,SAAS,cAAc,SAAS,IAAI;AAAA,UACpC;AAAA,UACA,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,UAAE;AAEA,UAAM,GAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE;AAEA,SAAO,EAAE,UAAU,WAAW,KAAK;AACrC;AAEA,SAAS,sBAAsB,QAAwB;AACrD,QAAM,KAAK,OAAO,YAAY;AAC9B,MAAI,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,WAAW,KAAK,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,SAAS,EAAG,QAAO;AAC3G,MAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,SAAS,EAAG,QAAO;AAC1D,MAAI,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClF,MAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,QAAQ,EAAG,QAAO;AAClF,MAAI,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS,KAAK,KAAK,GAAG,SAAS,UAAU,KAAK,GAAG,SAAS,YAAY,EAAG,QAAO;AAChH,MAAI,GAAG,SAAS,MAAM,KAAK,GAAG,SAAS,WAAW,KAAK,GAAG,SAAS,MAAM,EAAG,QAAO;AACnF,MAAI,GAAG,SAAS,UAAU,EAAG,QAAO;AACpC,SAAO;AACT;AAEA,SAAS,cAAc,MAAc,MAAsB;AACzD,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,SAAO,MACJ,IAAI,CAAC,GAAG,MAAM;AACb,UAAM,MAAM,OAAO;AACnB,WAAO,KAAK,IAAI,SAAS,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,EAC/C,CAAC,EACA,KAAK,IAAI;AACd;AAEA,SAAS,SAAS,KAAa,KAAqB;AAClD,SAAO,IAAI,SAAS,MAAM,IAAI,UAAU,GAAG,MAAM,CAAC,IAAI,QAAQ;AAChE;;;ACpKA,SAAS,YAAAG,iBAAgB;AACzB,SAAS,YAAAC,WAAU,WAAAC,UAAS,MAAAC,WAAU;AACtC,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,QAAAC,aAAY;AACrB,SAAS,UAAAC,eAAc;AAkBvB,IAAM,gBAA0C;AAAA,EAC9C,oBAAoB;AAAA,EACpB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,qBAAqB;AAAA,EACrB,oBAAoB;AACtB;AAEA,eAAe,sBAAwC;AACrD,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,IAAAC,UAAS,YAAY,CAAC,SAAS,GAAG,CAAC,UAAU;AAC3C,MAAAD,SAAQ,CAAC,KAAK;AAAA,IAChB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,YACpB,WACsD;AACtD,QAAM,YAAY,MAAM,oBAAoB;AAC5C,MAAI,CAAC,WAAW;AACd,WAAO,EAAE,UAAU,CAAC,GAAG,WAAW,MAAM;AAAA,EAC1C;AAEA,QAAM,WAAsB,CAAC;AAC7B,QAAM,SAAS,MAAME,SAAQC,MAAKC,QAAO,GAAG,sBAAsB,CAAC;AACnE,QAAM,aAAaD,MAAK,QAAQ,cAAc;AAE9C,MAAI;AACF,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MAAY;AAAA,MACZ;AAAA,MAAiB;AAAA,MACjB;AAAA,MAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MAAe;AAAA;AAAA,IACjB;AAEA,UAAM,IAAI,QAAc,CAACH,UAAS,WAAW;AAC3C,MAAAC;AAAA,QACE;AAAA,QACA;AAAA,QACA,EAAE,SAAS,KAAQ,WAAW,KAAK,OAAO,KAAK;AAAA,QAC/C,CAAC,OAAO,SAAS,WAAW;AAC1B,cAAI,OAAO;AACT,kBAAM,kBAAkB,UAAU,MAAM,SAAS,MAAM,GAAG,GAAG;AAC7D,mBAAO,IAAI,MAAM,oBAAoB,cAAc,EAAE,CAAC;AAAA,UACxD,OAAO;AACL,YAAAD,SAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,CAACK,YAAW,UAAU,EAAG,QAAO,EAAE,UAAU,WAAW,KAAK;AAEhE,UAAM,gBAAgB,MAAMC,UAAS,YAAY,OAAO;AACxD,QAAI,CAAC,cAAc,KAAK,EAAG,QAAO,EAAE,UAAU,WAAW,KAAK;AAE9D,UAAM,UAA4B,KAAK,MAAM,aAAa;AAE1D,eAAW,UAAU,SAAS;AAC5B,YAAM,WAAW,OAAO;AACxB,YAAM,OAAO,OAAO,YAAY;AAChC,YAAM,WAAW,cAAc,OAAO,MAAM,KAAK;AAGjD,YAAM,UAAU,iBAAiB,WAAW,QAAQ;AACpD,YAAM,UAAU,UAAU,WAAW,SAAS,IAAI,IAAI,KAAK,OAAO,KAAK;AAGvE,YAAM,iBAAiB,OAAO,OAAO,SAAS,IAC1C,OAAO,OAAO,UAAU,GAAG,CAAC,IAAI,QAAQ,OAAO,OAAO,UAAU,OAAO,OAAO,SAAS,CAAC,IACxF;AAEJ,eAAS,KAAK;AAAA,QACZ,IAAI,MAAM,QAAQ,IAAI,IAAI,IAAI,OAAO,MAAM;AAAA,QAC3C,MAAM,MAAM,OAAO,MAAM;AAAA,QACzB;AAAA,QACA,OAAO,GAAG,OAAO,WAAW;AAAA,QAC5B,aAAa,sBAAsB,OAAO,MAAM,wBAAwB,cAAc;AAAA,QACtF,MAAM;AAAA,QACN;AAAA,QACA,QAAQ,OAAO;AAAA,QACf;AAAA,QACA,KAAK;AAAA,6BAAgG,OAAO,OAAO,YAAY,EAAE,QAAQ,MAAM,GAAG,CAAC;AAAA;AAAA;AAAA,QACnJ,UAAU;AAAA,QACV,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF,UAAE;AACA,UAAMC,IAAG,QAAQ,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnE;AAEA,SAAO,EAAE,UAAU,WAAW,KAAK;AACrC;;;AClIA,OAAO,eAAe;AAGtB,IAAM,gBAAgB;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;AA+BtB,eAAsB,cACpB,OACA,kBACoB;AACpB,QAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,QAAM,SAAS,IAAI,UAAU;AAG7B,QAAM,gBAAgB,IAAI;AAAA,IACxB,iBAAiB,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE;AAAA,EAC7D;AAGA,QAAM,SAAS,WAAW,OAAO,GAAM;AACvC,QAAM,cAAyB,CAAC;AAEhC,aAAW,SAAS,QAAQ;AAC1B,UAAM,cAAc,MACjB,IAAI,CAAC,MAAM,OAAO,EAAE,IAAI;AAAA,EAAS,EAAE,OAAO,EAAE,EAC5C,KAAK,MAAM;AAEd,UAAM,eAAe,iBAAiB,SAAS,IAC3C;AAAA;AAAA;AAAA,EAA+F,iBAAiB,IAAI,CAAC,MAAM,KAAK,EAAE,IAAI,IAAI,EAAE,IAAI,WAAM,EAAE,KAAK,EAAE,EAAE,KAAK,IAAI,CAAC,KAC3K;AAEJ,QAAI;AACF,YAAM,WAAW,MAAM,OAAO,SAAS,OAAO;AAAA,QAC5C,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS,2DAA2D,YAAY;AAAA;AAAA,EAAO,WAAW;AAAA,UACpG;AAAA,QACF;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAED,YAAM,OAAO,SAAS,QACnB,OAAO,CAAC,UAAwC,MAAM,SAAS,MAAM,EACrE,IAAI,CAAC,UAAU,MAAM,IAAI,EACzB,KAAK,EAAE;AAGV,YAAM,YAAY,KAAK,MAAM,aAAa;AAC1C,UAAI,CAAC,UAAW;AAEhB,YAAM,SAAS,KAAK,MAAM,UAAU,CAAC,CAAC;AAStC,iBAAW,QAAQ,QAAQ;AAEzB,cAAM,YAAY,MAAM,KAAK,CAAC,MAAM;AAClC,gBAAMC,SAAQ,EAAE,QAAQ,MAAM,IAAI;AAClC,iBAAO,KAAK,QAAQA,OAAM;AAAA,QAC5B,CAAC;AACD,cAAM,OAAO,WAAW,QAAQ,MAAM,CAAC,EAAE;AAEzC,cAAM,MAAM,GAAG,IAAI,IAAI,KAAK,IAAI;AAChC,YAAI,cAAc,IAAI,GAAG,EAAG;AAE5B,cAAM,UAAU,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG,WAAW;AAC/D,cAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,cAAM,eAAe,KAAK,IAAI,GAAG,KAAK,OAAO,CAAC;AAC9C,cAAM,aAAa,KAAK,IAAI,MAAM,QAAQ,KAAK,OAAO,CAAC;AACvD,cAAM,UAAU,MACb,MAAM,cAAc,UAAU,EAC9B,IAAI,CAAC,GAAG,MAAM;AACb,gBAAM,MAAM,eAAe,IAAI;AAC/B,gBAAM,SAAS,QAAQ,KAAK,OAAO,MAAM;AACzC,iBAAO,GAAG,MAAM,IAAI,IAAI,SAAS,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,QACvD,CAAC,EACA,KAAK,IAAI;AAEZ,oBAAY,KAAK;AAAA,UACf,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI;AAAA,UAC3B,MAAM;AAAA,UACN,UAAU,KAAK;AAAA,UACf,OAAO,KAAK;AAAA,UACZ,aAAa,KAAK;AAAA,UAClB;AAAA,UACA,MAAM,KAAK;AAAA,UACX;AAAA,UACA,KAAK,KAAK;AAAA,UACV,UAAU,KAAK;AAAA,UACf,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,SAAS,GAAG;AAC/D,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,WACP,OACA,UACuC;AACvC,QAAM,SAAgD,CAAC;AACvD,MAAI,UAA+C,CAAC;AACpD,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO;AAExB,UAAM,mBAAmB,KAAK,QAAQ,SAAS,WAC3C,KAAK,QAAQ,MAAM,GAAG,QAAQ,IAAI,oCAClC,KAAK;AACT,UAAM,QAAQ,EAAE,MAAM,KAAK,MAAM,SAAS,iBAAiB;AAE3D,QAAI,cAAc,MAAM,QAAQ,SAAS,YAAY,QAAQ,SAAS,GAAG;AACvE,aAAO,KAAK,OAAO;AACnB,gBAAU,CAAC;AACX,oBAAc;AAAA,IAChB;AACA,YAAQ,KAAK,KAAK;AAClB,mBAAe,MAAM,QAAQ;AAAA,EAC/B;AAEA,MAAI,QAAQ,SAAS,EAAG,QAAO,KAAK,OAAO;AAC3C,SAAO;AACT;;;AClJO,SAAS,cAAc,SAAyB;AACrD,MAAI,SAAS;AACb,MAAI,IAAI;AACR,MAAI,gBAAgB;AACpB,MAAI,gBAAgB;AACpB,MAAI,aAAa;AACjB,MAAI,sBAAsB;AAC1B,MAAI,qBAAqB;AAEzB,SAAO,IAAI,QAAQ,QAAQ;AACzB,UAAM,KAAK,QAAQ,CAAC;AACpB,UAAM,OAAO,QAAQ,IAAI,CAAC;AAE1B,QAAI,qBAAqB;AACvB,UAAI,OAAO,MAAM;AACf,8BAAsB;AACtB,kBAAU;AAAA,MACZ;AACA;AACA;AAAA,IACF;AAEA,QAAI,oBAAoB;AACtB,UAAI,OAAO,OAAO,SAAS,KAAK;AAC9B,6BAAqB;AACrB,aAAK;AAAA,MACP,OAAO;AACL,YAAI,OAAO,KAAM,WAAU;AAC3B;AAAA,MACF;AACA;AAAA,IACF;AAEA,QAAI,eAAe;AACjB,gBAAU;AACV,UAAI,OAAO,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAM,iBAAgB;AAC3D;AACA;AAAA,IACF;AAEA,QAAI,eAAe;AACjB,gBAAU;AACV,UAAI,OAAO,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAM,iBAAgB;AAC3D;AACA;AAAA,IACF;AAEA,QAAI,YAAY;AACd,gBAAU;AACV,UAAI,OAAO,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAM,cAAa;AACxD;AACA;AAAA,IACF;AAGA,QAAI,OAAO,OAAO,SAAS,KAAK;AAC9B,4BAAsB;AACtB,WAAK;AACL;AAAA,IACF;AACA,QAAI,OAAO,OAAO,SAAS,KAAK;AAC9B,2BAAqB;AACrB,WAAK;AACL;AAAA,IACF;AAEA,QAAI,OAAO,OAAO,CAAC,iBAAiB,CAAC,eAAe;AAClD,4BAAsB;AACtB;AACA;AAAA,IACF;AAGA,QAAI,OAAO,IAAK,iBAAgB;AAChC,QAAI,OAAO,IAAK,iBAAgB;AAChC,QAAI,OAAO,IAAK,cAAa;AAE7B,cAAU;AACV;AAAA,EACF;AAEA,SAAO;AACT;AAkCO,SAAS,eAAe,SAA2B;AACxD,QAAM,UAAoB,CAAC;AAC3B,QAAM,WAAW;AAAA,IACf;AAAA,IACA;AAAA,EACF;AACA,aAAW,KAAK,UAAU;AACxB,QAAI;AACJ,YAAQ,IAAI,EAAE,KAAK,OAAO,OAAO,MAAM;AACrC,cAAQ,KAAK,EAAE,CAAC,CAAC;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,sBAAsB,SAAuG;AAC3I,SAAO;AAAA,IACL,MAAM,yJAAyJ,KAAK,OAAO;AAAA,IAC3K,WAAW,yEAAyE,KAAK,OAAO;AAAA,IAChG,QAAQ,+CAA+C,KAAK,OAAO;AAAA,IACnE,MAAM,4CAA4C,KAAK,OAAO;AAAA,IAC9D,MAAM,kCAAkC,KAAK,OAAO;AAAA,EACtD;AACF;AAeO,SAAS,gBAAgB,SAAiB,UAA8B;AAC7E,SAAO;AAAA,IACL,iBAAiB,cAAc,OAAO;AAAA,IACtC,SAAS,eAAe,OAAO;AAAA,IAC/B,YAAY,sBAAsB,OAAO;AAAA,IACzC,YAAY,6FAA6F,KAAK,QAAQ;AAAA,IACtH,cAAc,oFAAoF,KAAK,QAAQ;AAAA,IAC/G,eAAe,8DAA8D,KAAK,QAAQ,KACxF,mEAAmE,KAAK,OAAO;AAAA,EACnF;AACF;;;ACpJA,IAAM,gBAAgB;AACtB,IAAM,eAAe;AACrB,IAAM,qBAAqB;AAE3B,eAAe,iBAAiB,KAAa,MAAmB,WAAsC;AACpG,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,KAAK,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AACzD,MAAI;AACF,WAAO,MAAM,MAAM,KAAK,EAAE,GAAG,MAAM,QAAQ,WAAW,OAAO,CAAC;AAAA,EAChE,UAAE;AACA,iBAAa,EAAE;AAAA,EACjB;AACF;AAWA,eAAsB,cAAc,SAA0C;AAC5E,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,MAAI;AAEF,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,CAAC;AAAA,MAClC;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,SAAS,GAAI,QAAO,CAAC;AAC1B,UAAM,QAAS,MAAM,SAAS,KAAK;AACnC,QAAI,CAAC,MAAM,QAAQ,MAAM,OAAO,EAAG,QAAO,CAAC;AAI3C,UAAM,gBAAgB,oBAAI,IAAY;AACtC,eAAW,UAAU,MAAM,SAAS;AAClC,iBAAW,KAAK,OAAO,SAAS,CAAC,EAAG,eAAc,IAAI,EAAE,EAAE;AAAA,IAC5D;AAIA,UAAM,cAAc,oBAAI,IAA4B;AACpD,eAAW,UAAU,eAAe;AAClC,UAAI;AACF,cAAM,YAAY,MAAM;AAAA,UACtB,GAAG,YAAY,IAAI,mBAAmB,MAAM,CAAC;AAAA,UAC7C,EAAE,QAAQ,MAAM;AAAA,UAChB;AAAA,QACF;AACA,YAAI,CAAC,UAAU,GAAI;AACnB,cAAM,SAAU,MAAM,UAAU,KAAK;AACrC,oBAAY,IAAI,QAAQ,MAAM;AAAA,MAChC,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,UAAM,UAAsB,CAAC;AAC7B,UAAM,QAAQ,QAAQ,CAAC,QAAQ,MAAM;AACnC,YAAM,OAAO,OAAO,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE;AAChD,UAAI,IAAI,WAAW,EAAG;AACtB,YAAM,IAAI,QAAQ,CAAC;AACnB,cAAQ,KAAK;AAAA,QACX,WAAW,EAAE,QAAQ;AAAA,QACrB,MAAM,EAAE,QAAQ;AAAA,QAChB,SAAS,EAAE;AAAA,QACX,OAAO,IAAI,IAAI,CAAC,OAAO,YAAY,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;AAAA,MACtD,CAAC;AAAA,IACH,CAAC;AACD,WAAO;AAAA,EACT,QAAQ;AAGN,WAAO,CAAC;AAAA,EACV;AACF;AAOO,SAAS,YAAY,MAA8D;AACxF,QAAM,UAAU,KAAK,YAAY,CAAC;AAClC,aAAW,KAAK,SAAS;AAGvB,UAAM,QAAQ,EAAE,MAAM,MAAM,qBAAqB;AACjD,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,WAAW,MAAM,CAAC,CAAC;AACjC,QAAI,OAAO,SAAS,KAAK,GAAG;AAC1B,UAAI,SAAS,EAAG,QAAO;AACvB,UAAI,SAAS,EAAG,QAAO;AACvB,UAAI,SAAS,EAAG,QAAO;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;;;AC9HA,IAAM,iBAA8C;AAAA,EAClD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAW,KAAK;AAAA,IAAkB,UAAU;AAAA,IACrD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,SAAS,CAAC;AAAA,IACR,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,WAAW,CAAC;AAAA,IACV,SAAS;AAAA,IAAU,KAAK;AAAA,IAAkB,UAAU;AAAA,IACpD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,gBAAgB,CAAC;AAAA,IACf,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,cAAc,CAAC;AAAA,IACb,SAAS;AAAA,IAAS,KAAK;AAAA,IAAiB,UAAU;AAAA,IAClD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,aAAa,CAAC;AAAA,IACZ,SAAS;AAAA,IAAS,KAAK;AAAA,IAAiB,UAAU;AAAA,IAClD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAW,KAAK;AAAA,IAAO,UAAU;AAAA,IAC1C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,WAAW,CAAC;AAAA,IACV,SAAS;AAAA,IAAW,KAAK;AAAA,IAAO,UAAU;AAAA,IAC1C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,cAAc,CAAC;AAAA,IACb,SAAS;AAAA,IAAU,KAAK;AAAA,IAAkB,UAAU;AAAA,IACpD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,OAAO,CAAC;AAAA,IACN,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAS,KAAK;AAAA,IAAiB,UAAU;AAAA,IAClD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AACH;AAGA,IAAM,oBAAiD;AAAA,EACrD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAU,KAAK;AAAA,IAAkB,UAAU;AAAA,IACpD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,SAAS,CAAC;AAAA,IACR,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAU,KAAK;AAAA,IAAkB,UAAU;AAAA,IACpD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,YAAY,CAAC;AAAA,IACX,SAAS;AAAA,IAAU,KAAK;AAAA,IAAkB,UAAU;AAAA,IACpD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AAAA,EACD,UAAU,CAAC;AAAA,IACT,SAAS;AAAA,IAAS,KAAK;AAAA,IAAkB,UAAU;AAAA,IACnD,OAAO;AAAA,IACP,aAAa;AAAA,IACb,KAAK;AAAA,EACP,CAAC;AACH;AAYA,IAAM,eAA6B;AAAA,EACjC;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA+B,UAAU;AAAA,IAC9D,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,SAAS,SAAS,cAAc,KAAK,4BAA4B,KAAK,OAAO;AAAA,EAC5G;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAsC,UAAU;AAAA,IACrE,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,SAAS,SAAS,cAAc,KAAK,6BAA6B,KAAK,OAAO;AAAA,EAC7G;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAiC,UAAU;AAAA,IAChE,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,SAAS,SAAS,cAAc,KAAK,oBAAoB,KAAK,OAAO;AAAA,EACpG;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAwB,UAAU;AAAA,IACvD,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,SAAS,SAAS,YAAY,KAAK,gDAAgD,KAAK,OAAO;AAAA,EAC9H;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAqB,UAAU;AAAA,IACpD,aAAa;AAAA,IACb,KAAK;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,SAAS,SAAS,QAAQ,KAAK,2BAA2B,KAAK,OAAO;AAAA,EACrG;AACF;AAEA,SAAS,gBAAgB,IAAY,IAAoB;AACvD,QAAM,SAAS,GAAG,QAAQ,YAAY,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAC/D,QAAM,SAAS,GAAG,QAAQ,YAAY,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAC/D,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,QAAQ,OAAO,MAAM,GAAG,KAAK;AAC/D,UAAM,IAAI,OAAO,CAAC,KAAK;AACvB,UAAM,IAAI,OAAO,CAAC,KAAK;AACvB,QAAI,IAAI,EAAG,QAAO;AAClB,QAAI,IAAI,EAAG,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAASC,YAAW,SAAiB,MAAsB;AACzD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC;AAClC,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,OAAO,CAAC;AAC3C,SAAO,MAAM,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC,GAAG,MAAM;AAC3C,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,YAAY,OAAO,MAAM;AACxC,WAAO,GAAG,MAAM,IAAI,OAAO,OAAO,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,EACxD,CAAC,EAAE,KAAK,IAAI;AACd;AAEO,SAAS,iBAAiB,OAAuD;AACtF,QAAM,WAAsB,CAAC;AAE7B,aAAW,EAAE,MAAM,UAAU,QAAQ,KAAK,OAAO;AAE/C,QAAI,SAAS,SAAS,cAAc,KAAK,CAAC,SAAS,SAAS,cAAc,GAAG;AAC3E,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,cAAM,UAAU,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAE9D,mBAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,GAAG;AACrD,gBAAM,QAAQ,eAAe,IAAI;AACjC,cAAI,CAAC,MAAO;AAEZ,gBAAM,aAAa,OAAO,OAAO,EAAE,QAAQ,eAAe,EAAE;AAC5D,qBAAW,QAAQ,OAAO;AACxB,gBAAI,gBAAgB,YAAY,KAAK,OAAO,IAAI,GAAG;AACjD,oBAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,UAAU,OAAK,EAAE,SAAS,IAAI,IAAI,GAAG,CAAC,IAAI;AAC3E,uBAAS,KAAK;AAAA,gBACZ,IAAI,GAAG,KAAK,GAAG,IAAI,QAAQ,IAAI,IAAI;AAAA,gBACnC,MAAM,KAAK;AAAA,gBACX,UAAU,KAAK;AAAA,gBACf,OAAO,KAAK;AAAA,gBACZ,aAAa,KAAK;AAAA,gBAClB,MAAM;AAAA,gBACN;AAAA,gBACA,SAASA,YAAW,SAAS,IAAI;AAAA,gBACjC,KAAK,KAAK;AAAA,gBACV,UAAU;AAAA,gBACV,QAAQ;AAAA,gBACR,OAAO;AAAA,gBACP,KAAK;AAAA,cACP,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAGA,QAAI,SAAS,MAAM,uBAAuB,GAAG;AAC3C,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,cAAM,QAAQ,KAAK,MAAM,oCAAoC;AAC7D,YAAI,CAAC,MAAO;AAEZ,cAAM,OAAO,MAAM,CAAC,EAAE,YAAY;AAClC,cAAM,UAAU,MAAM,CAAC,KAAK;AAC5B,cAAM,QAAQ,kBAAkB,IAAI;AACpC,YAAI,CAAC,MAAO;AAEZ,mBAAW,QAAQ,OAAO;AACxB,cAAI,gBAAgB,SAAS,KAAK,OAAO,IAAI,GAAG;AAC9C,qBAAS,KAAK;AAAA,cACZ,IAAI,GAAG,KAAK,GAAG,IAAI,QAAQ,IAAI,IAAI,CAAC;AAAA,cACpC,MAAM,KAAK;AAAA,cACX,UAAU,KAAK;AAAA,cACf,OAAO,KAAK;AAAA,cACZ,aAAa,KAAK;AAAA,cAClB,MAAM;AAAA,cACN,MAAM,IAAI;AAAA,cACV,SAASA,YAAW,SAAS,IAAI,CAAC;AAAA,cAClC,KAAK,KAAK;AAAA,cACV,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,KAAK;AAAA,YACP,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,eAAW,WAAW,cAAc;AAClC,UAAI,QAAQ,KAAK,SAAS,QAAQ,GAAG;AACnC,iBAAS,KAAK;AAAA,UACZ,IAAI,GAAG,QAAQ,EAAE,IAAI,QAAQ;AAAA,UAC7B,MAAM,QAAQ;AAAA,UACd,UAAU,QAAQ;AAAA,UAClB,OAAO,QAAQ;AAAA,UACf,aAAa,QAAQ;AAAA,UACrB,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAASA,YAAW,SAAS,CAAC;AAAA,UAC9B,KAAK,QAAQ;AAAA,UACb,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,KAAK;AAAA,QACP,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAcA,eAAsB,oBACpB,OACA,oBACoB;AAUpB,QAAM,UAA2B,CAAC;AAElC,aAAW,EAAE,MAAM,UAAU,QAAQ,KAAK,OAAO;AAE/C,QAAI,SAAS,SAAS,cAAc,KAAK,CAAC,SAAS,SAAS,cAAc,GAAG;AAC3E,UAAI;AACF,cAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,cAAM,UAAU,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC9D,mBAAW,CAAC,MAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,GAAG;AACxD,gBAAM,UAAU,OAAO,UAAU,EAAE,QAAQ,eAAe,EAAE,EAAE,KAAK;AACnE,cAAI,CAAC,WAAW,YAAY,OAAO,YAAY,SAAU;AACzD,gBAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI,GAAG,CAAC,IAAI;AAC7E,kBAAQ,KAAK,EAAE,WAAW,OAAO,MAAM,SAAS,MAAM,UAAU,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAAA,QAC5F;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAIA,QAAI,SAAS,MAAM,uBAAuB,GAAG;AAC3C,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,6BAA6B;AAC7D,YAAI,CAAC,EAAG;AACR,gBAAQ,KAAK;AAAA,UACX,WAAW;AAAA,UACX,MAAM,EAAE,CAAC,EAAE,YAAY;AAAA,UACvB,SAAS,EAAE,CAAC;AAAA,UACZ,MAAM;AAAA,UACN,MAAM,IAAI;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,cAAc,GAAG;AACrC,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,IAAI,MAAM,CAAC,EAAE,MAAM,oCAAoC;AAC7D,YAAI,CAAC,EAAG;AACR,gBAAQ,KAAK;AAAA,UACX,WAAW;AAAA,UACX,MAAM,EAAE,CAAC;AAAA,UACT,SAAS,EAAE,CAAC;AAAA,UACZ,MAAM;AAAA,UACN,MAAM,IAAI;AAAA,UACV;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAGlC,QAAM,QAAQ;AACd,QAAM,WAAsB,CAAC;AAC7B,WAAS,QAAQ,GAAG,QAAQ,QAAQ,QAAQ,SAAS,OAAO;AAC1D,UAAM,QAAQ,QAAQ,MAAM,OAAO,QAAQ,KAAK;AAChD,UAAM,UAAU,MAAM;AAAA,MACpB,MAAM,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,EAAE,UAAU,GAAG,SAAS,EAAE,QAAQ,EAAE;AAAA,IAC9F;AAEA,UAAM,aAAa,oBAAI,IAAiC;AACxD,eAAW,KAAK,QAAS,YAAW,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,IAAI,IAAI,EAAE,OAAO,IAAI,CAAC;AAElF,eAAW,UAAU,OAAO;AAC1B,YAAM,MAAM,GAAG,OAAO,SAAS,IAAI,OAAO,IAAI,IAAI,OAAO,OAAO;AAChE,YAAM,QAAQ,WAAW,IAAI,GAAG;AAChC,UAAI,CAAC,MAAO;AACZ,iBAAW,QAAQ,MAAM,OAAO;AAE9B,cAAM,YAAY,GAAG,OAAO,IAAI,IAAI,KAAK,EAAE;AAC3C,YAAI,mBAAmB,IAAI,SAAS,EAAG;AACvC,2BAAmB,IAAI,SAAS;AAEhC,cAAM,WAAW,YAAY,IAAI;AACjC,cAAM,QAAQ,KAAK,WAAW,GAAG,OAAO,IAAI,kBAAkB,KAAK,EAAE;AACrE,cAAM,cACJ,KAAK,WACL,KAAK,WACL,GAAG,OAAO,IAAI,IAAI,OAAO,OAAO,mBAAmB,KAAK,EAAE,uCAAuC,KAAK,EAAE;AAC1G,iBAAS,KAAK;AAAA,UACZ,IAAI,GAAG,KAAK,EAAE,IAAI,OAAO,IAAI,IAAI,OAAO,IAAI;AAAA,UAC5C,MAAM,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA,aAAa,YAAY,MAAM,GAAG,GAAG;AAAA,UACrC,MAAM,OAAO;AAAA,UACb,MAAM,OAAO;AAAA,UACb,SAASA,YAAW,OAAO,SAAS,OAAO,IAAI;AAAA,UAC/C,KAAK,WAAW,OAAO,IAAI,+BAA+B,KAAK,EAAE,uCAAuC,KAAK,EAAE;AAAA,UAC/G,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,KAAK;AAAA,QACP,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACzaA,SAASC,YAAW,SAAiB,MAAsB;AACzD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC;AAClC,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,OAAO,CAAC;AAC3C,SAAO,MAAM,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC,GAAG,MAAM;AAC3C,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,YAAY,OAAO,MAAM;AACxC,WAAO,GAAG,MAAM,IAAI,OAAO,OAAO,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,EACxD,CAAC,EAAE,KAAK,IAAI;AACd;AAeA,IAAM,gBAA+B;AAAA;AAAA,EAEnC;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAmC,UAAU;AAAA,IAClE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,UAAI;AACF,YAAI,uBAAuB,KAAK,OAAO,GAAG;AACxC,gBAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,UAAU,OAAK,SAAS,KAAK,CAAC,CAAC,IAAI;AACpE,iBAAO,EAAE,MAAM,SAASA,YAAW,SAAS,IAAI,EAAE;AAAA,QACpD;AACA,YAAI,CAAC,sBAAsB,KAAK,OAAO,KAAK,oBAAoB,KAAK,OAAO,GAAG;AAC7E,iBAAO,EAAE,MAAM,GAAG,SAASA,YAAW,SAAS,CAAC,EAAE;AAAA,QACpD;AAAA,MACF,QAAQ;AAAA,MAAC;AACT,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAsC,UAAU;AAAA,IACrE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,UAAI,uBAAuB,KAAK,OAAO,KAAK,CAAC,uBAAuB,KAAK,OAAO,GAAG;AACjF,cAAM,OAAO,QAAQ,MAAM,IAAI,EAAE,UAAU,OAAK,UAAU,KAAK,CAAC,CAAC,IAAI;AACrE,eAAO,EAAE,MAAM,SAASA,YAAW,SAAS,IAAI,EAAE;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAoC,UAAU;AAAA,IACnE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,UAAI,CAAC,eAAe,KAAK,OAAO,KAAK,CAAC,mCAAmC,KAAK,OAAO,GAAG;AACtF,eAAO,EAAE,MAAM,GAAG,SAASA,YAAW,SAAS,CAAC,EAAE;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA0C,UAAU;AAAA,IACzE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,UAAI,CAAC,8BAA8B,KAAK,OAAO,GAAG;AAChD,eAAO,EAAE,MAAM,GAAG,SAASA,YAAW,SAAS,CAAC,EAAE;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA+B,UAAU;AAAA,IAC9D,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,sBAAsB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,mBAAmB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG;AAC3F,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA0C,UAAU;AAAA,IACzE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,aAAa,QAAQ,MAAM,YAAY,KAAK,CAAC,GAAG;AACtD,UAAI,aAAa,KAAK,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,KAAK;AACrE,eAAO,EAAE,MAAM,GAAG,SAASA,YAAW,SAAS,CAAC,EAAE;AAAA,MACpD;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA+B,UAAU;AAAA,IAC9D,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,oBAAoB,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG;AAC7C,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAyC,UAAU;AAAA,IACxE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,+BAA+B,KAAK,MAAM,CAAC,CAAC,GAAG;AACjD,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAwC,UAAU;AAAA,IACvE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,8CAA8C,KAAK,MAAM,CAAC,CAAC,GAAG;AAChE,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA+B,UAAU;AAAA,IAC9D,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,kBAAkB,KAAK,MAAM,CAAC,CAAC,KAAK,gCAAgC,KAAK,MAAM,CAAC,CAAC,GAAG;AACtF,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAA4B,UAAU;AAAA,IAC3D,UAAU;AAAA,IAAW,aAAa;AAAA,IAClC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,6CAA6C,KAAK,MAAM,CAAC,CAAC,GAAG;AAC/D,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE,IAAI;AAAA,IAAU,OAAO;AAAA,IAAkC,UAAU;AAAA,IACjE,UAAU;AAAA,IAAiB,aAAa;AAAA,IACxC,KAAK;AAAA,IACL,OAAO;AAAA,IAAY,KAAK;AAAA,IACxB,aAAa;AAAA,IACb,MAAM,SAAS;AACb,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,sEAAsE,KAAK,MAAM,CAAC,CAAC,KAAK,oBAAoB,KAAK,MAAM,CAAC,CAAC,GAAG;AAC9H,iBAAO,EAAE,MAAM,IAAI,GAAG,SAASA,YAAW,SAAS,IAAI,CAAC,EAAE;AAAA,QAC5D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,YAAY,OAAuD;AACjF,QAAM,WAAsB,CAAC;AAE7B,aAAW,EAAE,MAAM,UAAU,QAAQ,KAAK,OAAO;AAC/C,eAAW,SAAS,eAAe;AACjC,UAAI,CAAC,MAAM,YAAY,KAAK,QAAQ,EAAG;AAEvC,YAAM,SAAS,MAAM,MAAM,SAAS,QAAQ;AAC5C,UAAI,QAAQ;AACV,iBAAS,KAAK;AAAA,UACZ,IAAI,GAAG,MAAM,EAAE,IAAI,QAAQ,IAAI,OAAO,IAAI;AAAA,UAC1C,MAAM,MAAM;AAAA,UACZ,UAAU,MAAM;AAAA,UAChB,OAAO,MAAM;AAAA,UACb,aAAa,MAAM;AAAA,UACnB,MAAM;AAAA,UACN,MAAM,OAAO;AAAA,UACb,SAAS,OAAO;AAAA,UAChB,KAAK,MAAM;AAAA,UACX,UAAU,MAAM;AAAA,UAChB,QAAQ;AAAA,UACR,OAAO,MAAM;AAAA,UACb,KAAK,MAAM;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AC9PA,SAASC,YAAW,SAAiB,MAAsB;AACzD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,GAAG,OAAO,CAAC;AAClC,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,OAAO,CAAC;AAC3C,SAAO,MAAM,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC,GAAG,MAAM;AAC3C,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,SAAS,YAAY,OAAO,MAAM;AACxC,WAAO,GAAG,MAAM,IAAI,OAAO,OAAO,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC;AAAA,EACxD,CAAC,EAAE,KAAK,IAAI;AACd;AAOO,SAAS,cAAc,OAA8B;AAC1D,QAAM,WAAsB,CAAC;AAG7B,QAAM,UAAU,oBAAI,IAAoB;AACxC,aAAW,KAAK,OAAO;AACrB,YAAQ,IAAI,EAAE,MAAM,EAAE,OAAO;AAAA,EAC/B;AAKA,QAAM,sBAAsB,MAAM;AAAA,IAAK,OACrC,uBAAuB,KAAK,EAAE,IAAI,KAClC,4CAA4C,KAAK,EAAE,OAAO;AAAA,EAC5D;AACA,QAAM,gBAAgB,MAAM;AAAA,IAAK,OAC/B,iCAAiC,KAAK,EAAE,IAAI,KAC5C,uDAAuD,KAAK,EAAE,OAAO;AAAA,EACvE;AAGA,aAAW,KAAK,OAAO;AACrB,QAAI,CAAC,wCAAwC,KAAK,EAAE,IAAI,EAAG;AAC3D,QAAI,cAAc,KAAK,EAAE,IAAI,EAAG;AAGhC,QAAI,gDAAgD,KAAK,EAAE,IAAI,EAAG;AAElE,UAAM,eAAe,sHAAsH,KAAK,EAAE,OAAO;AACzJ,UAAM,gBAAgB,oDAAoD,KAAK,EAAE,OAAO;AAExF,QAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,qBAAqB;AAE3D,UAAI,qGAAqG,KAAK,EAAE,OAAO,GAAG;AACxH,cAAM,OAAO,EAAE,QAAQ,MAAM,IAAI,EAAE;AAAA,UAAU,OAC3C,qGAAqG,KAAK,CAAC;AAAA,QAC7G,IAAI;AACJ,iBAAS,KAAK;AAAA,UACZ,IAAI,UAAU,EAAE,IAAI,IAAI,IAAI;AAAA,UAC5B,MAAM;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,aAAa;AAAA,UACb,MAAM,EAAE;AAAA,UACR;AAAA,UACA,SAASA,YAAW,EAAE,SAAS,IAAI;AAAA,UACnC,KAAK;AAAA,UACL,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,KAAK;AAAA,QACP,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,KAAK,OAAK,mBAAmB,KAAK,EAAE,IAAI,CAAC;AAClE,QAAM,YAAY,MAAM,KAAK,OAAK,EAAE,KAAK,SAAS,YAAY,CAAC;AAC/D,MAAI,cAAc,WAAW;AAC3B,QAAI,CAAC,WAAW,KAAK,UAAU,OAAO,KAAK,CAAC,aAAa,KAAK,UAAU,OAAO,GAAG;AAChF,eAAS,KAAK;AAAA,QACZ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAASA,YAAW,UAAU,SAAS,CAAC;AAAA,QACxC,KAAK;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,UAAU,MAAM;AAAA,IAAO,OAC3B,oEAAoE,KAAK,EAAE,IAAI,KAC/E,6DAA6D,KAAK,EAAE,OAAO;AAAA,EAC7E;AACA,aAAW,KAAK,SAAS;AAEvB,QAAI,oBAAoB,KAAK,EAAE,OAAO,GAAG;AAEvC,UAAI,CAAC,uBAAuB,KAAK,EAAE,OAAO,GAAG;AAC3C,cAAM,OAAO,EAAE,QAAQ,MAAM,IAAI,EAAE,UAAU,OAAK,mBAAmB,KAAK,CAAC,CAAC,IAAI;AAChF,iBAAS,KAAK;AAAA,UACZ,IAAI,UAAU,EAAE,IAAI,IAAI,IAAI;AAAA,UAC5B,MAAM;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,aAAa;AAAA,UACb,MAAM,EAAE;AAAA,UACR;AAAA,UACA,SAASA,YAAW,EAAE,SAAS,IAAI;AAAA,UACnC,KAAK;AAAA,UACL,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,KAAK;AAAA,QACP,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAIA,aAAW,KAAK,OAAO;AACrB,QAAI,CAAC,uCAAuC,KAAK,EAAE,IAAI,EAAG;AAC1D,UAAM,YAAY,EAAE,QAAQ,MAAM,6CAA6C;AAC/E,QAAI,WAAW;AACb,YAAM,aAAa,UAAU,CAAC;AAC9B,UAAI,WAAW,SAAS,WAAW,KAAK,WAAW,SAAS,WAAW,GAAG;AACxE,cAAM,OAAO,EAAE,QAAQ,MAAM,IAAI,EAAE,UAAU,OAAK,EAAE,SAAS,UAAU,CAAC,IAAI;AAC5E,iBAAS,KAAK;AAAA,UACZ,IAAI,UAAU,EAAE,IAAI,IAAI,IAAI;AAAA,UAC5B,MAAM;AAAA,UACN,UAAU;AAAA,UACV,OAAO;AAAA,UACP,aAAa;AAAA,UACb,MAAM,EAAE;AAAA,UACR;AAAA,UACA,SAASA,YAAW,EAAE,SAAS,IAAI;AAAA,UACnC,KAAK;AAAA,UACL,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,KAAK;AAAA,QACP,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,KAAK,OAAK,kDAAkD,KAAK,EAAE,IAAI,CAAC;AACjG,MAAI,YAAY;AACd,UAAM,cAAc,oBAAI,IAAY;AACpC,eAAW,KAAK,OAAO;AACrB,UAAI,sBAAsB,KAAK,EAAE,IAAI,GAAG;AACtC,cAAM,UAAU,EAAE,QAAQ,SAAS,mCAAmC;AACtE,mBAAW,KAAK,SAAS;AACvB,sBAAY,IAAI,EAAE,CAAC,CAAC;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AACA,UAAM,oBAAoB,oBAAI,IAAY;AAC1C,UAAM,iBAAiB,WAAW,QAAQ,SAAS,wBAAwB;AAC3E,eAAW,KAAK,gBAAgB;AAC9B,wBAAkB,IAAI,EAAE,CAAC,CAAC;AAAA,IAC5B;AAEA,UAAM,eAAe,CAAC,GAAG,WAAW,EAAE,OAAO,OAAK,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,EAAE,WAAW,OAAO,KAAK,MAAM,MAAM;AACrH,QAAI,aAAa,SAAS,GAAG;AAC3B,eAAS,KAAK;AAAA,QACZ,IAAI,UAAU,WAAW,IAAI;AAAA,QAC7B,MAAM;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa,+EAA+E,aAAa,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,aAAa,SAAS,IAAI,MAAM,aAAa,SAAS,CAAC,WAAW,EAAE;AAAA,QACtM,MAAM,WAAW;AAAA,QACjB,MAAM;AAAA,QACN,SAASA,YAAW,WAAW,SAAS,CAAC;AAAA,QACzC,KAAK;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,aAAW,KAAK,OAAO;AACrB,QAAI,CAAC,sBAAsB,KAAK,EAAE,IAAI,EAAG;AACzC,QAAI,8BAA8B,KAAK,EAAE,IAAI,EAAG;AAEhD,UAAM,cAAc,EAAE,QAAQ,SAAS,sEAAsE;AAC7G,eAAW,KAAK,aAAa;AAC3B,YAAM,OAAO,EAAE,QAAQ,UAAU,GAAG,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE;AACzD,eAAS,KAAK;AAAA,QACZ,IAAI,UAAU,EAAE,IAAI,IAAI,IAAI;AAAA,QAC5B,MAAM;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,QACP,aAAa,iBAAiB,EAAE,CAAC,EAAE,UAAU,GAAG,EAAE,CAAC;AAAA,QACnD,MAAM,EAAE;AAAA,QACR;AAAA,QACA,SAASA,YAAW,EAAE,SAAS,IAAI;AAAA,QACnC,KAAK;AAAA,QACL,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;ACnOA,OAAO,WAAW;AAKlB,IAAM,kBAA8D;AAAA,EAClE,UAAU,MAAM,MAAM,MAAM;AAAA,EAC5B,MAAM,MAAM,IAAI;AAAA,EAChB,QAAQ,MAAM,OAAO;AAAA,EACrB,KAAK,MAAM;AAAA,EACX,MAAM,MAAM;AACd;AAUA,IAAM,iBAA2C;AAAA,EAC/C,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR;AAEA,IAAM,eAAgE;AAAA,EACpE,MAAM,MAAM,MAAM;AAAA,EAClB,KAAK,MAAM,MAAM;AAAA,EACjB,KAAK,MAAM,KAAK;AAAA,EAChB,KAAK,MAAM,OAAO;AAAA,EAClB,KAAK,MAAM,IAAI;AAAA,EACf,KAAK,MAAM,MAAM,MAAM;AACzB;AAEO,SAAS,qBAAqB,QAAoB,OAAmD;AAC1G,QAAM,EAAE,UAAU,cAAc,SAAS,IAAI;AAG7C,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,MAAM,KAAK,KAAK,cAAc,IAAI,MAAM,KAAK,+BAA0B,CAAC;AACpF,UAAQ,IAAI,MAAM,KAAK,OAAO,SAAI,OAAO,EAAE,CAAC,CAAC;AAC7C,UAAQ,IAAI,EAAE;AAGd,MAAI,SAAS,MAAM,SAAS,GAAG;AAC7B,UAAM,aAAa,gBAAgB,KAAK;AACxC,QAAI,WAAW,SAAS,KAAK,WAAW,CAAC,MAAM,WAAW;AACxD,cAAQ,IAAI,MAAM,KAAK,gBAAgB,IAAI,MAAM,MAAM,WAAW,KAAK,IAAI,CAAC,CAAC;AAAA,IAC/E;AAAA,EACF;AAGA,QAAM,oBAA4C,EAAE,MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;AACzG,QAAM,EAAE,OAAO,OAAO,QAAQ,IAAI,eAAe,UAAU,YAAY;AACvE,QAAM,aAAa,aAAa,KAAK;AACrC,QAAM,aAAa,kBAAkB,KAAK,KAAK;AAC/C,UAAQ,IAAI,MAAM,KAAK,oBAAoB,IAAI,WAAW,IAAI,KAAK,GAAG,IAAI,MAAM,KAAK,KAAK,KAAK,gBAAW,OAAO,EAAE,CAAC;AACpH,UAAQ,IAAI,MAAM,KAAK,iCAAiC,UAAU,uBAAuB,CAAC;AAC1F,UAAQ,IAAI,EAAE;AAEd,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,MAAM,MAAM,KAAK,6BAA6B,CAAC;AAC3D,YAAQ,IAAI,MAAM,KAAK,aAAa,YAAY,cAAc,WAAW,KAAM,QAAQ,CAAC,CAAC,GAAG,CAAC;AAC7F,YAAQ,IAAI,EAAE;AACd;AAAA,EACF;AAGA,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE;AAAA,IAC3B,CAAC,GAAG,MAAM,eAAe,EAAE,QAAQ,IAAI,eAAe,EAAE,QAAQ;AAAA,EAClE;AAGA,QAAM,SAAmC,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,EAAE;AAC5F,aAAW,KAAK,SAAU,QAAO,EAAE,QAAQ;AAE3C,QAAM,eAAyB,CAAC;AAChC,MAAI,OAAO,WAAW,EAAG,cAAa,KAAK,MAAM,MAAM,MAAM,KAAK,IAAI,OAAO,QAAQ,YAAY,CAAC;AAClG,MAAI,OAAO,OAAO,EAAG,cAAa,KAAK,MAAM,IAAI,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC;AAC5E,MAAI,OAAO,SAAS,EAAG,cAAa,KAAK,MAAM,OAAO,KAAK,GAAG,OAAO,MAAM,SAAS,CAAC;AACrF,MAAI,OAAO,MAAM,EAAG,cAAa,KAAK,MAAM,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AACrE,MAAI,OAAO,OAAO,EAAG,cAAa,KAAK,MAAM,KAAK,GAAG,OAAO,IAAI,OAAO,CAAC;AAExE,UAAQ,IAAI,WAAW,MAAM,KAAK,SAAS,OAAO,SAAS,CAAC,CAAC,YAAY,aAAa,KAAK,MAAM,KAAK,KAAK,CAAC,CAAC,EAAE;AAC/G,UAAQ,IAAI,MAAM,KAAK,aAAa,YAAY,cAAc,WAAW,KAAM,QAAQ,CAAC,CAAC,GAAG,CAAC;AAC7F,UAAQ,IAAI,EAAE;AAGd,aAAW,WAAW,QAAQ;AAC5B,UAAM,gBAAgB,gBAAgB,QAAQ,QAAQ;AAAA,MACpD,IAAI,QAAQ,SAAS,YAAY,CAAC;AAAA,IACpC;AACA,UAAM,cAAc,QAAQ,WAAW,OACnC,MAAM,QAAQ,QAAQ,IACtB,QAAQ,WAAW,eACnB,MAAM,KAAK,SAAS,IACpB,QAAQ,WAAW,YACnB,MAAM,OAAO,aAAa,IAC1B,QAAQ,WAAW,WACnB,MAAM,KAAK,SAAS,IACpB,QAAQ,WAAW,eACnB,MAAM,MAAM,gBAAgB,IAC5B,MAAM,KAAK,KAAK,QAAQ,IAAI,IAAI;AACpC,UAAM,kBAAkB,QAAQ,aAC5B,MAAM,KAAK,KAAK,QAAQ,UAAU,cAAc,IAChD;AAEJ,UAAM,iBAAiB;AAAA,MACrB,QAAQ,QAAQ,MAAM,OAAO,IAAI,QAAQ,KAAK,GAAG,IAAI;AAAA,MACrD,QAAQ,MAAM,MAAM,KAAK,IAAI,QAAQ,GAAG,GAAG,IAAI;AAAA,IACjD,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC1B,YAAQ,IAAI,KAAK,aAAa,GAAG,WAAW,GAAG,MAAM,KAAK,QAAQ,KAAK,CAAC,IAAI,cAAc,GAAG,eAAe,EAAE;AAC9G,YAAQ,IAAI,MAAM,KAAK,KAAK,QAAQ,IAAI,IAAI,QAAQ,IAAI,EAAE,CAAC;AAC3D,YAAQ,IAAI,EAAE;AAGd,YAAQ,IAAI,MAAM,MAAM,KAAK,QAAQ,WAAW,EAAE,CAAC;AACnD,YAAQ,IAAI,EAAE;AAGd,QAAI,QAAQ,SAAS;AACnB,YAAM,eAAe,QAAQ,QAAQ,MAAM,IAAI;AAC/C,iBAAW,QAAQ,cAAc;AAC/B,YAAI,KAAK,WAAW,GAAG,GAAG;AACxB,kBAAQ,IAAI,MAAM,IAAI,OAAO,IAAI,EAAE,CAAC;AAAA,QACtC,OAAO;AACL,kBAAQ,IAAI,MAAM,KAAK,OAAO,IAAI,EAAE,CAAC;AAAA,QACvC;AAAA,MACF;AACA,cAAQ,IAAI,EAAE;AAAA,IAChB;AAGA,QAAI,QAAQ,KAAK;AACf,cAAQ,IAAI,MAAM,MAAM,UAAU,QAAQ,GAAG,EAAE,CAAC;AAChD,cAAQ,IAAI,EAAE;AAAA,IAChB;AAEA,YAAQ,IAAI,MAAM,KAAK,OAAO,SAAI,OAAO,EAAE,CAAC,CAAC;AAC7C,YAAQ,IAAI,EAAE;AAAA,EAChB;AAGA,QAAM,gBAAgB,SAAS,OAAO,OAAK,EAAE,KAAK;AAClD,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,YAA6D;AAAA,MACjE,YAAY,EAAE,MAAM,yBAAyB,OAAO,EAAE;AAAA,MACtD,YAAY,EAAE,MAAM,0BAA0B,OAAO,EAAE;AAAA,MACvD,YAAY,EAAE,MAAM,aAAa,OAAO,EAAE;AAAA,MAC1C,YAAY,EAAE,MAAM,mBAAmB,OAAO,EAAE;AAAA,MAChD,YAAY,EAAE,MAAM,6BAA6B,OAAO,EAAE;AAAA,MAC1D,YAAY,EAAE,MAAM,yBAAyB,OAAO,EAAE;AAAA,MACtD,YAAY,EAAE,MAAM,iBAAiB,OAAO,EAAE;AAAA,MAC9C,YAAY,EAAE,MAAM,kBAAkB,OAAO,EAAE;AAAA,MAC/C,YAAY,EAAE,MAAM,oBAAoB,OAAO,EAAE;AAAA,MACjD,YAAY,EAAE,MAAM,QAAQ,OAAO,EAAE;AAAA,IACvC;AACA,eAAW,KAAK,eAAe;AAC7B,UAAI,EAAE,SAAS,UAAU,EAAE,KAAK,EAAG,WAAU,EAAE,KAAK,EAAE;AAAA,IACxD;AACA,YAAQ,IAAI,MAAM,KAAK,2BAA2B,CAAC;AACnD,eAAW,CAAC,IAAI,EAAE,MAAM,MAAM,CAAC,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC7D,YAAM,SAAS,QAAQ,IAAI,MAAM,IAAI,GAAG,KAAK,SAAS,QAAQ,IAAI,MAAM,EAAE,EAAE,IAAI,MAAM,MAAM,MAAM;AAClG,cAAQ,IAAI,MAAM,KAAK,OAAO,EAAE,IAAI,IAAI,IAAI,IAAI,MAAM;AAAA,IACxD;AACA,YAAQ,IAAI,EAAE;AAAA,EAChB;AAGA,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ;AAAA,MACN,MAAM,MAAM,MAAM,KAAK,mBAAmB,IACxC,MAAM,IAAI,KAAK,IAAI,OAAO,QAAQ,kBAAkB,OAAO,WAAW,IAAI,MAAM,EAAE,qCAAqC;AAAA,IAC3H;AAAA,EACF,WAAW,OAAO,OAAO,GAAG;AAC1B,YAAQ;AAAA,MACN,MAAM,OAAO,KAAK,iCAAiC,OAAO,IAAI,uBAAuB,OAAO,OAAO,IAAI,MAAM,EAAE,8BAA8B;AAAA,IAC/I;AAAA,EACF;AAEA,UAAQ,IAAI,EAAE;AAChB;;;ACvLO,SAAS,iBAAiB,QAA0B;AACzD,UAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC7C;;;ACFA,IAAM,oBAA8C;AAAA,EAClD,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR;AAEA,IAAM,oBAA8C;AAAA,EAClD,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,MAAM;AACR;AAsCO,SAAS,kBAAkB,QAA0B;AAE1D,QAAM,UAAU,oBAAI,IAAqB;AACzC,aAAW,KAAK,OAAO,UAAU;AAC/B,QAAI,CAAC,QAAQ,IAAI,EAAE,IAAI,GAAG;AACxB,cAAQ,IAAI,EAAE,MAAM,CAAC;AAAA,IACvB;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,KAAK,QAAQ,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO;AAAA,IAC5D;AAAA,IACA,kBAAkB,EAAE,MAAM,EAAE,MAAM;AAAA,IAClC,iBAAiB,EAAE,MAAM,EAAE,YAAY;AAAA,IACvC,sBAAsB,EAAE,OAAO,kBAAkB,EAAE,QAAQ,EAAE;AAAA,IAC7D,YAAY;AAAA,MACV,mBAAmB,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,MAC1D,MAAM,CAAC,YAAY,EAAE,SAAS,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAAA,IAClE;AAAA,EACF,EAAE;AAEF,QAAM,YAAY,IAAI,IAAI,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAExD,QAAM,QAAqB;AAAA,IACzB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,MACJ;AAAA,QACE,MAAM;AAAA,UACJ,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,gBAAgB;AAAA,YAChB;AAAA,UACF;AAAA,QACF;AAAA,QACA,SAAS,OAAO,SAAS,IAAI,CAAC,MAAM;AAKlC,gBAAM,cAAc,EAAE,MAClB,GAAG,EAAE,KAAK,KAAK,EAAE,WAAW;AAAA,iBAAoB,EAAE,GAAG,KACrD,GAAG,EAAE,KAAK,KAAK,EAAE,WAAW;AAChC,iBAAO;AAAA,YACL,QAAQ,EAAE;AAAA,YACV,WAAW,UAAU,IAAI,EAAE,IAAI,KAAK;AAAA,YACpC,OAAO,kBAAkB,EAAE,QAAQ;AAAA,YACnC,SAAS,EAAE,MAAM,YAAY;AAAA,YAC7B,WAAW;AAAA,cACT;AAAA,gBACE,kBAAkB;AAAA,kBAChB,kBAAkB,EAAE,KAAK,EAAE,KAAK;AAAA,kBAChC,QAAQ;AAAA,oBACN,WAAW,EAAE;AAAA,oBACb,GAAI,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,IAAI,CAAC;AAAA,kBAC9C;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC5C;;;ACtFA,IAAM,eAAuC;AAAA;AAAA,EAE3C,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AACP;AAEA,IAAM,iBAAyC;AAAA;AAAA,EAE7C,UAAU;AAAA,EACV,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,KAAK;AACP;AAEA,SAAS,aAAa,QAAoB,SAAkB;AAC1D,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,OAAO,QAAQ;AAAA,IACf,aAAa,QAAQ;AAAA,IACrB,UAAU,QAAQ;AAAA,IAClB,MAAM,QAAQ;AAAA,IACd,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB,KAAK,QAAQ,OAAO;AAAA,IACpB,OAAO,QAAQ,SAAS;AAAA,IACxB,KAAK,QAAQ,OAAO;AAAA,IACpB,gBAAgB,OAAO;AAAA,IACvB,gBAAgB,OAAO;AAAA,IACvB,eAAe,OAAO;AAAA,IACtB,kBAAkB,OAAO;AAAA,EAC3B;AACF;AAcO,SAAS,mBAAmB,QAA0B;AAC3D,aAAW,WAAW,OAAO,UAAU;AACrC,UAAM,QAAQ;AAAA,MACZ,MAAM,KAAK,MAAM,IAAI,KAAK,OAAO,SAAS,EAAE,QAAQ,IAAI,GAAI;AAAA,MAC5D,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,aAAa,QAAQ,OAAO;AAAA,IACrC;AACA,YAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,EACnC;AACF;AAYO,SAAS,oBAAoB,QAA0B;AAC5D,aAAW,WAAW,OAAO,UAAU;AACrC,UAAM,QAAQ;AAAA,MACZ,cAAc,OAAO;AAAA,MACrB,KAAK,EAAE,SAAS,SAAS;AAAA,MACzB,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC,eAAe;AAAA,QAC1B,MAAM,CAAC,MAAM;AAAA,QACb,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,UAAU,aAAa,QAAQ,QAAQ,KAAK;AAAA,QAC5C,QAAQ,QAAQ;AAAA,MAClB;AAAA,MACA,eAAe;AAAA,QACb,IAAI,QAAQ;AAAA,QACZ,UAAU,CAAC,QAAQ,QAAQ;AAAA,QAC3B,aAAa,QAAQ;AAAA,QACrB,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ,SAAS;AAAA,QACjC,WAAW,QAAQ,MACf,0CAA0C,QAAQ,IAAI,QAAQ,UAAU,EAAE,CAAC,UAC3E;AAAA,MACN;AAAA,MACA,MAAM,EAAE,MAAM,QAAQ,KAAK;AAAA,MAC3B,KAAK,EAAE,OAAO,QAAQ,SAAS;AAAA,MAC/B,SAAS,GAAG,QAAQ,IAAI,KAAK,QAAQ,KAAK;AAAA,MAC1C,YAAY,aAAa,QAAQ,OAAO;AAAA,IAC1C;AACA,YAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,EACnC;AACF;AAUO,SAAS,oBAAoB,QAA0B;AAC5D,aAAW,WAAW,OAAO,UAAU;AACrC,UAAM,QAAQ;AAAA,MACZ,UAAU;AAAA,MACV,QAAQ;AAAA,QACN,YAAY,QAAQ,QAAQ;AAAA,QAC5B,QAAQ,QAAQ,IAAI;AAAA,QACpB,YAAY,QAAQ,SAAS,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAAA,QAC/D,QAAQ,QAAQ,SAAS,QAAQ,MAAM,QAAQ,QAAQ,GAAG,CAAC,KAAK;AAAA,QAChE,QAAQ,MAAM,OAAO,QAAQ,GAAG,KAAK;AAAA,MACvC,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MACX,SAAS;AAAA,MACT,UAAU;AAAA,MACV,QAAQ,eAAe,QAAQ,QAAQ,KAAK;AAAA,MAC5C,SAAS,GAAG,QAAQ,IAAI,KAAK,QAAQ,KAAK,WAAM,QAAQ,WAAW;AAAA,MACnE,WAAW,OAAO;AAAA,MAClB,YAAY,aAAa,QAAQ,OAAO;AAAA,IAC1C;AACA,YAAQ,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,EACnC;AACF;;;Ad1IA,eAAsB,YACpB,WACA,SACe;AACf,QAAM,MAAMC,SAAQ,aAAa,GAAG;AACpC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,YAAY,KAAK,IAAI;AAG3B,QAAM,SAAS,MAAM,WAAW,GAAG;AACnC,QAAM,SAAS,QAAQ,cAAc,OAAO,MAAM,SAAS,CAAC,CAAC,QAAQ,IAAI;AAEzE,QAAM,WAAW,WAAW;AAW5B,MAAI,OAAuB;AAC3B,MAAI,WAAW;AAEf,MAAI,gBAAgB,GAAG;AACrB,UAAM,QAAQ,MAAM,WAAW;AAC/B,eAAW,MAAM;AACjB,QAAI,MAAM,SAAS,OAAO;AACxB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,MAAM,SAAS;AAGlB,YAAM,eAAgB,MAA4B,SAAS;AAC3D,cAAQ,IAAIC,OAAM,IAAI;AAAA,EAAK,eAAe,YAAY,OAAO,sBAAsB,CAAC;AACpF,cAAQ,IAAIA,OAAM,OAAO,sCAAsC,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AACnG,cAAQ,IAAIA,OAAM,KAAK,UAAU,eAAe,eAAe,UAAU,WAAY,MAA4B,QAAQ,MAAM,IAAI;AAAA,CAAI,CAAC;AACxI,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,QAAI,MAAM,SAAS,UAAU,MAAM,YAAY,KAAK,MAAM,aAAa,KAAK,CAAC,UAAU;AACrF,cAAQ,IAAIA,OAAM,KAAK,KAAK,MAAM,SAAS,aAAa,MAAM,cAAc,IAAI,KAAK,GAAG;AAAA,CAAoB,CAAC;AAAA,IAC/G;AAEA,QAAK,MAA4B,SAAS,WAAW,MAAM,YAAY,KAAK,MAAM,aAAa,MAAM,CAAC,UAAU;AAC9G,cAAQ,IAAIA,OAAM,KAAK,KAAK,MAAM,SAAS,cAAc,MAAM,cAAc,IAAI,KAAK,GAAG;AAAA,CAAyB,CAAC;AAAA,IACrH;AAAA,EACF;AAGA,MAAI,gBAA8B,CAAC;AACnC,MAAI,SAAS,OAAO;AAClB,UAAM,SAAS,mBAAmB;AAClC,QAAI,QAAQ;AACV,sBAAgB;AAAA,IAClB,OAAO;AACL,UAAI,CAAC,SAAU,SAAQ,IAAIA,OAAM,KAAK,4BAA4B,CAAC;AACnE,YAAM,aAAa,MAAM,uBAAuB;AAChD,UAAI,YAAY;AACd,wBAAiB,mBAAmB,KAA6B,CAAC;AAAA,MACpE;AACA,UAAI,cAAc,WAAW,KAAK,CAAC,UAAU;AAC3C,gBAAQ,IAAIA,OAAM,OAAO,iEAA4D,CAAC;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,UAAU,IAAI;AAAA,IAClB,MAAM;AAAA,IACN,OAAO;AAAA,IACP;AAAA,EACF,CAAC,EAAE,MAAM;AAET,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,aAAa,GAAG;AAAA,EAChC,SAAS,OAAO;AACd,YAAQ,KAAK,0BAA0B;AACvC,YAAQ,MAAMA,OAAM,IAAI,UAAU,iBAAiB,QAAQ,MAAM,UAAU,KAAK,EAAE,CAAC;AACnF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,QAAQ,MAAM;AAChB,UAAM,OAAO,OAAO,QAAQ,SAAS,WAAW,QAAQ,OAAO;AAW/D,QAAI,KAAK,WAAW,GAAG,KAAK,CAAC,wBAAwB,KAAK,IAAI,GAAG;AAC/D,cAAQ,KAAK,wBAAwB,IAAI,6BAAwB;AAAA,IACnE,OAAO;AACL,UAAI;AACF,cAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,eAAoB;AACtD,cAAM,EAAE,UAAU,IAAI,MAAM,OAAO,MAAW;AAC9C,cAAM,gBAAgB,UAAUA,SAAQ;AACxC,cAAM,EAAE,OAAO,IAAI,MAAM;AAAA,UACvB;AAAA,UACA,CAAC,QAAQ,eAAe,IAAI;AAAA,UAC5B,EAAE,KAAK,KAAK,UAAU,QAAQ;AAAA,QAChC;AACA,cAAM,eAAe,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAC7D,gBAAQ,MAAM,OAAO,OAAK,aAAa,KAAK,QAAM,EAAE,SAAS,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC;AACnF,YAAI,CAAC,UAAU;AACb,kBAAQ,KAAKD,OAAM,KAAK,yBAAyB,MAAM,MAAM,qBAAqB,IAAI,EAAE,CAAC;AAAA,QAC3F;AAAA,MACF,QAAQ;AACN,gBAAQ,KAAK,kDAA6C;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,KAAK,yCAAyC;AACtD;AAAA,EACF;AAEA,UAAQ,OAAO,SAAS,MAAM,MAAM;AAGpC,QAAM,cAAyB,CAAC;AAGhC,QAAM,0BAA+D,CAAC;AACtE,aAAW,YAAY,OAAO;AAC5B,UAAM,UAAU,iBAAiB,KAAK,QAAQ;AAC9C,QAAI,CAAC,QAAS;AACd,4BAAwB,KAAK,EAAE,MAAM,UAAU,QAAQ,CAAC;AAGxD,UAAM,SAAS,gBAAgB,SAAS,QAAQ;AAGhD,QAAI,OAAO,cAAe;AAG1B,UAAM,WAAW,eAAe,SAAS,UAAU,OAAO,cAAc,MAAM,aAAa;AAG3F,eAAW,KAAK,UAAU;AACxB,UAAI,OAAO,YAAY;AACrB,UAAE,aAAa;AAAA,MACjB,WAAW,OAAO,cAAc;AAC9B,UAAE,aAAa;AAAA,MACjB,OAAO;AACL,UAAE,aAAa;AAAA,MACjB;AAAA,IACF;AAEA,gBAAY,KAAK,GAAG,QAAQ;AAAA,EAC9B;AAEA,QAAM,cAAc,YAAY;AAChC,MAAI,WAAW,cAAc,GAAG;AAC9B,YAAQ,KAAK,sBAAsB,WAAW,SAAS;AAAA,EACzD;AAGA,UAAQ,OAAO;AACf,QAAM,cAAc,iBAAiB,uBAAuB;AAC5D,aAAW,KAAK,aAAa;AAAE,MAAE,aAAa;AAAA,EAAQ;AACtD,cAAY,KAAK,GAAG,WAAW;AAI/B,QAAM,aAAa,oBAAI,IAAY;AACnC,aAAW,KAAK,aAAa;AAE3B,UAAM,QAAQ,EAAE,MAAM,MAAM,QAAQ;AACpC,QAAI,MAAO,YAAW,IAAI,GAAG,MAAM,CAAC,EAAE,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;AAAA,EACjE;AAEA,UAAQ,OAAO;AACf,MAAI;AACF,UAAM,cAAc,MAAM,oBAAoB,yBAAyB,UAAU;AACjF,eAAW,KAAK,aAAa;AAAE,QAAE,aAAa;AAAA,IAAQ;AACtD,gBAAY,KAAK,GAAG,WAAW;AAC/B,QAAI,WAAW,YAAY,SAAS,GAAG;AACrC,cAAQ,KAAK,iBAAiB,YAAY,MAAM,qCAAqC;AAAA,IACvF;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,QAAS,SAAQ,KAAK,2BAA2B,eAAe,QAAQ,IAAI,UAAU,eAAe,EAAE;AAAA,EAC7G;AAEA,MAAI,WAAW,YAAY,SAAS,GAAG;AACrC,YAAQ,KAAK,4BAA4B,YAAY,MAAM,SAAS;AAAA,EACtE;AAGA,UAAQ,OAAO;AACf,QAAM,kBAAkB,YAAY,uBAAuB;AAC3D,aAAW,KAAK,iBAAiB;AAAE,MAAE,aAAa;AAAA,EAAU;AAC5D,cAAY,KAAK,GAAG,eAAe;AACnC,MAAI,WAAW,gBAAgB,SAAS,GAAG;AACzC,YAAQ,KAAK,yBAAyB,gBAAgB,MAAM,oBAAoB;AAAA,EAClF;AAGA,UAAQ,OAAO;AACf,QAAM,iBAAiB,YAAY,uBAAuB;AAC1D,aAAW,KAAK,gBAAgB;AAAE,MAAE,aAAa;AAAA,EAAQ;AACzD,cAAY,KAAK,GAAG,cAAc;AAClC,MAAI,WAAW,eAAe,SAAS,GAAG;AACxC,YAAQ,KAAK,yBAAyB,eAAe,MAAM,SAAS;AAAA,EACtE;AAGA,UAAQ,OAAO;AACf,QAAM,oBAAoB,cAAc,uBAAuB;AAC/D,aAAW,KAAK,mBAAmB;AAAE,MAAE,aAAa;AAAA,EAAU;AAC9D,cAAY,KAAK,GAAG,iBAAiB;AACrC,MAAI,WAAW,kBAAkB,SAAS,GAAG;AAC3C,YAAQ,KAAK,6BAA6B,kBAAkB,MAAM,SAAS;AAAA,EAC7E;AAGA,UAAQ,OAAO;AACf,UAAQ,QAAQ;AAGhB,QAAM,WAAWD,SAAQG,MAAK,YAAY,SAAS,aAAa,CAAC;AACjE,QAAM,mBAAmBH,SAAQG,MAAK,KAAK,UAAU,CAAC;AAEtD,QAAM,CAAC,eAAe,cAAc,IAAI,MAAM,QAAQ,WAAW;AAAA,IAC/D,WAAW,KAAK,QAAQ,EAAE,MAAM,MAAM,WAAW,KAAK,gBAAgB,CAAC,EAAE,MAAM,OAAO,EAAE,UAAU,CAAC,GAAgB,WAAW,MAAM,EAAE;AAAA,IACtI,YAAY,GAAG,EAAE,MAAM,OAAO,EAAE,UAAU,CAAC,GAAgB,WAAW,MAAM,EAAE;AAAA,EAChF,CAAC;AAED,QAAM,UAAU,cAAc,WAAW,cAAc,cAAc,QAAQ,EAAE,UAAU,CAAC,GAAG,WAAW,MAAM;AAC9G,QAAM,WAAW,eAAe,WAAW,cAAc,eAAe,QAAQ,EAAE,UAAU,CAAC,GAAG,WAAW,MAAM;AAEjH,cAAY,KAAK,GAAG,QAAQ,QAAQ;AACpC,cAAY,KAAK,GAAG,SAAS,QAAQ;AAErC,MAAI,SAAS;AACX,QAAI,QAAQ,WAAW;AACrB,cAAQ,KAAK,iBAAiB,QAAQ,SAAS,MAAM,SAAS;AAAA,IAChE,OAAO;AACL,cAAQ,KAAKF,OAAM,KAAK,gEAA2D,CAAC;AAAA,IACtF;AACA,QAAI,SAAS,WAAW;AACtB,cAAQ,KAAK,kBAAkB,SAAS,SAAS,MAAM,SAAS;AAAA,IAClE,OAAO;AACL,cAAQ,KAAKA,OAAM,KAAK,mEAA8D,CAAC;AAAA,IACzF;AAAA,EACF;AAGA,MAAI,CAAC,YAAY,CAAC,SAAS;AACzB,UAAM,UAAoB,CAAC;AAC3B,QAAI,CAAC,QAAQ,UAAW,SAAQ,KAAK,+BAA+B;AACpE,QAAI,CAAC,SAAS,UAAW,SAAQ,KAAK,kCAAkC;AACxE,QAAI,QAAQ,SAAS,GAAG;AACtB,cAAQ,KAAKA,OAAM,KAAK,qBAAqB,QAAQ,KAAK,OAAO,CAAC,sBAAsB,CAAC;AAAA,IAC3F;AAAA,EACF;AAEA,QAAM,cAAc,YAAY;AAChC,UAAQ,OAAO,yBAAyB,WAAW,SAAS,gBAAgB,IAAI,MAAM,EAAE;AAGxF,MAAI,OAAO;AACT,YAAQ,OAAO;AACf,YAAQ,QAAQ;AAEhB,QAAI;AACF,YAAM,gBAAgB,MACnB,IAAI,CAAC,UAAU;AAAA,QACd;AAAA,QACA,SAAS,iBAAiB,KAAK,IAAI,KAAK;AAAA,MAC1C,EAAE,EACD,OAAO,CAAC,MAAM,EAAE,QAAQ,SAAS,KAAK,EAAE,QAAQ,SAAS,GAAM,EAC/D,OAAO,CAAC,MAAM;AACb,cAAM,iBACJ,wEAAwE,KAAK,EAAE,IAAI,KACnF,YAAY,KAAK,CAAC,YAAY,QAAQ,SAAS,EAAE,IAAI,KACrD,mDAAmD,KAAK,EAAE,OAAO;AACnE,eAAO;AAAA,MACT,CAAC,EACA,MAAM,GAAG,EAAE;AAEd,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM,aAAa,MAAM,cAAc,eAAe,WAAW;AACjE,oBAAY,KAAK,GAAG,UAAU;AAE9B,YAAI,WAAW,WAAW,SAAS,GAAG;AACpC,kBAAQ,KAAK,qBAAqB,WAAW,MAAM,oBAAoB;AAAA,QACzE;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,gBAAQ,KAAK,wBAAwB,MAAM,OAAO,EAAE;AAAA,MACtD;AAAA,IACF;AAAA,EACF,WAAW,CAAC,QAAQ,IAAI,qBAAqB,CAAC,UAAU;AACtD,YAAQ;AAAA,MACNA,OAAM,KAAK,+DAA+D;AAAA,IAC5E;AAAA,EACF;AAMA,MAAI,aAAa;AACjB,MAAI,iBAAiB;AACrB,MAAI,gBAAgB;AACpB,MAAI,qBAAkE,CAAC;AACvE,MAAI,QAAQ,IAAI,qBAAqB,YAAY,SAAS,GAAG;AAC3D,YAAQ,OAAO;AACf,YAAQ,QAAQ;AAChB,QAAI;AACF,YAAM,kBAAkB,oBAAI,IAAoB;AAChD,iBAAW,KAAK,aAAa;AAC3B,YAAI,CAAC,gBAAgB,IAAI,EAAE,IAAI,GAAG;AAChC,gBAAM,UAAU,iBAAiB,KAAK,EAAE,IAAI;AAC5C,cAAI,QAAS,iBAAgB,IAAI,EAAE,MAAM,OAAO;AAAA,QAClD;AAAA,MACF;AACA,YAAMG,UAAS,MAAM,qBAAqB,aAAa,eAAe;AACtE,mBAAaA,QAAO;AACpB,uBAAiBA,QAAO;AACxB,sBAAgBA,QAAO;AACvB,2BAAqBA,QAAO;AAC5B,UAAIA,QAAO,eAAe,GAAG;AAC3B,oBAAY,SAAS;AACrB,oBAAY,KAAK,GAAGA,QAAO,QAAQ;AAAA,MACrC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,UAAQ,KAAK;AAGb,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,kBAAkB,YAAY,OAAO,CAAC,MAAM;AAEhD,UAAM,UAAU,EAAE,WAAW,aAAa,UAAU,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,GAAG,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI;AACtG,QAAI,KAAK,IAAI,OAAO,EAAG,QAAO;AAC9B,SAAK,IAAI,OAAO;AAChB,WAAO;AAAA,EACT,CAAC;AAGD,QAAM,SAAqB;AAAA,IACzB,UAAU;AAAA,IACV,cAAc,MAAM;AAAA,IACpB,UAAU,KAAK,IAAI,IAAI;AAAA,IACvB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,WAAW;AAAA,EACb;AAEA,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,uBAAiB,MAAM;AACvB;AAAA,IACF,KAAK;AACH,wBAAkB,MAAM;AACxB;AAAA,IACF,KAAK;AACH,yBAAmB,MAAM;AACzB;AAAA,IACF,KAAK;AACH,0BAAoB,MAAM;AAC1B;AAAA,IACF,KAAK;AACH,0BAAoB,MAAM;AAC1B;AAAA,IACF;AACE,2BAAqB,QAAQ,uBAAuB;AACpD;AAAA,EACJ;AAGA,MAAI,cAAc,CAAC,UAAU;AAC3B,YAAQ,IAAI,EAAE;AACd,QAAI,iBAAiB,GAAG;AACtB,UAAI,gBAAgB,SAAS,GAAG;AAC9B,gBAAQ,IAAIH,OAAM,KAAK,uBAAgB,IAAIA,OAAM,MAAM,GAAG,cAAc,kBAAkB,mBAAmB,IAAI,MAAM,EAAE,gBAAa,IAAIA,OAAM,MAAM,GAAG,gBAAgB,MAAM,cAAc,gBAAgB,WAAW,IAAI,MAAM,EAAE,YAAY,CAAC;AAAA,MACnP,OAAO;AACL,gBAAQ,IAAIA,OAAM,KAAK,uBAAgB,IAAIA,OAAM,MAAM,YAAY,IAAIA,OAAM,KAAK,KAAK,cAAc,kBAAkB,mBAAmB,IAAI,MAAM,EAAE,WAAW,CAAC;AAAA,MACpK;AAAA,IACF,OAAO;AACL,cAAQ,IAAIA,OAAM,KAAK,uBAAgB,IAAIA,OAAM,MAAM,OAAO,gBAAgB,MAAM,WAAW,gBAAgB,WAAW,IAAI,MAAM,EAAE,iBAAiB,CAAC;AAAA,IAC1J;AAEA,QAAI,WAAW,mBAAmB,SAAS,GAAG;AAC5C,cAAQ,IAAIA,OAAM,KAAK;AAAA,+DAAkE,CAAC;AAC1F,iBAAW,EAAE,SAAS,GAAG,OAAO,KAAK,oBAAoB;AACvD,gBAAQ,IAAIA,OAAM,KAAK,OAAO,EAAE,SAAS,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC;AACjG,gBAAQ,IAAIA,OAAM,KAAK,wBAAwB,MAAM,EAAE,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAGA,MAAI,SAAS,UAAU,CAAC,UAAU;AAChC,YAAQ,IAAI,EAAE;AACd,QAAI,aAAa,aAAa;AAC5B,cAAQ,IAAIA,OAAM,KAAK,+BAA+B,IAAIA,OAAM,KAAK,wCAAmC,IAAIA,OAAM,KAAK,wBAAwB,CAAC;AAAA,IAClJ,OAAO;AACL,cAAQ,IAAIA,OAAM,KAAK,0BAA0B,IAAIA,OAAM,KAAK,0CAAqC,IAAIA,OAAM,KAAK,qBAAqB,CAAC;AAAA,IAC5I;AACA,YAAQ,IAAI,EAAE;AAAA,EAChB;AAGA,MAAI,gBAAgB,GAAG;AACrB,UAAM,QAAQ,WAAW;AAAA,MACvB,eAAe;AAAA,MACf,kBAAkB;AAAA,QAChB,WAAW;AAAA,QACX,cAAc,MAAM;AAAA,QACpB,UAAU;AAAA,QACV,UAAU,KAAK,IAAI,IAAI;AAAA,MACzB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAGA,QAAM,cAAc,gBAAgB;AAAA,IAClC,CAAC,MAAM,EAAE,aAAa,cAAc,EAAE,aAAa;AAAA,EACrD;AACA,MAAI,aAAa;AACf,YAAQ,WAAW;AAAA,EACrB;AAGA,MAAI,QAAQ,OAAO;AACjB,UAAM,eAAe,oBAAI,IAAI,CAAC,gBAAgB,QAAQ,QAAQ,SAAS,OAAO,CAAC;AAC/E,UAAM,aAAa;AACnB,QAAI,gBAAsD;AAC1D,QAAI,aAAa;AAEjB,YAAQ,IAAIA,OAAM,KAAK,kDAAkD,CAAC;AAE1E,YAAQ,KAAK,EAAE,WAAW,KAAK,GAAG,CAAC,QAAQ,aAAa;AACtD,UAAI,CAAC,SAAU;AAGf,YAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,UAAI,MAAM,KAAK,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC,EAAG;AAE5C,cAAQ,IAAIA,OAAM,KAAK,iBAAiB,QAAQ,EAAE,CAAC;AAEnD,UAAI,cAAe,cAAa,aAAa;AAE7C,sBAAgB,WAAW,YAAY;AACrC,YAAI,WAAY;AAChB,qBAAa;AAEb,gBAAQ,IAAIA,OAAM,KAAK,oBAAoB,CAAC;AAC5C,YAAI;AACF,gBAAM,YAAY,WAAW;AAAA,YAC3B,GAAG;AAAA,YACH,OAAO;AAAA;AAAA,UACT,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,MAAMA,OAAM,IAAI,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,KAAK,EAAE,CAAC;AAAA,QAC7F,UAAE;AACA,uBAAa;AACb,kBAAQ,IAAIA,OAAM,KAAK,kDAAkD,CAAC;AAAA,QAC5E;AAAA,MACF,GAAG,UAAU;AAAA,IACf,CAAC;AAGD,UAAM,IAAI,QAAQ,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B;AACF;;;Ae7fA,SAAS,oBAAoB;AAC7B,SAAS,WAAW;AACpB,SAAS,kBAAkB;AAC3B,SAAS,YAAAI,iBAAgB;AACzB,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAGhB,IAAM,wBAAwB,QAAQ,IAAI,yBAAyB;AAQnE,IAAM,WAAW,QAAQ,IAAI,uBAAuB;AASpD,eAAsB,eAA8B;AAClD,QAAM,WAAW,eAAe;AAChC,MAAI,UAAU;AACZ,YAAQ,IAAIC,OAAM,OAAO,wBAAwB,SAAS,KAAK,EAAE,CAAC;AAClE,YAAQ,IAAIA,OAAM,KAAK,wDAAwD,CAAC;AAChF;AAAA,EACF;AAEA,QAAM,UAAUC,KAAI,8BAA8B,EAAE,MAAM;AAG1D,QAAM,EAAE,OAAO,OAAO,OAAO,IAAI,MAAM,oBAAoB;AAE3D,UAAQ,OAAO;AAGf,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK;AAAA;AAAA,EAC7C,CAAC;AAGD,QAAM,OAAO,MAAM,SAAS;AAC5B,UAAQ,KAAK;AAEb,UAAQ,IAAID,OAAM,MAAM,gBAAgB,KAAK,EAAE,CAAC;AAChD,MAAI,MAAM;AACR,YAAQ,IAAIA,OAAM,KAAK,SAAS,KAAK,IAAI,EAAE,CAAC;AAAA,EAC9C;AACF;AAEA,eAAsB,gBAA+B;AACnD,QAAM,WAAW,eAAe;AAChC,MAAI,CAAC,UAAU;AACb,YAAQ,IAAIA,OAAM,KAAK,gBAAgB,CAAC;AACxC;AAAA,EACF;AAEA,aAAW;AACX,qBAAmB;AACnB,UAAQ,IAAIA,OAAM,MAAM,0BAA0B,CAAC;AACrD;AAEA,eAAsB,gBAA+B;AACnD,QAAM,QAAQ,eAAe;AAC7B,MAAI,CAAC,OAAO;AACV,YAAQ,IAAIA,OAAM,KAAK,6DAA6D,CAAC;AACrF;AAAA,EACF;AAEA,UAAQ,IAAIA,OAAM,KAAK,UAAU,MAAM,KAAK,EAAE,CAAC;AAC/C,UAAQ,IAAIA,OAAM,KAAK,YAAY,MAAM,MAAM,EAAE,CAAC;AAElD,QAAM,OAAO,MAAM,SAAS;AAC5B,MAAI,MAAM;AACR,UAAM,YAAY,KAAK,SAAS,QAC5BA,OAAM,QAAQ,MAAM,OAAO,IAC3BA,OAAM,OAAO,MAAM,QAAQ;AAC/B,YAAQ,IAAI,SAAS,SAAS,EAAE;AAAA,EAClC;AACF;AAEA,eAAe,sBAIZ;AACD,SAAO,IAAI,QAAQ,CAACE,UAAS,WAAW;AACtC,UAAM,gBAAgB,WAAW;AAEjC,UAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,UAAI,CAAC,IAAI,KAAK;AACZ,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI,aAAa;AACrB;AAAA,MACF;AAEA,YAAM,MAAM,IAAI,IAAI,IAAI,KAAK,kBAAkB;AAE/C,UAAI,IAAI,aAAa,aAAa;AAChC,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAC1C,cAAM,SAAS,IAAI,aAAa,IAAI,SAAS;AAC7C,cAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;AAE1C,YAAI,CAAC,SAAS,UAAU,eAAe;AACrC,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,sDAAiD;AACzD;AAAA,QACF;AAEA,YAAI,SAAS,SAAS,QAAQ;AAC5B,cAAI,UAAU,KAAK,EAAE,gBAAgB,YAAY,CAAC;AAClD,cAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAUP;AAED,iBAAO,MAAM;AACb,UAAAA,SAAQ,EAAE,OAAO,OAAO,OAAO,CAAC;AAAA,QAClC,OAAO;AACL,cAAI,UAAU,GAAG;AACjB,cAAI,IAAI,oBAAoB;AAAA,QAC9B;AAAA,MACF,WAAW,IAAI,aAAa,UAAU;AAcpC,cAAM,OAAQ,OAAO,QAAQ,EAAuB;AACpD,cAAM,cAAc,oBAAoB,IAAI;AAC5C,cAAM,cACJ,GAAG,QAAQ,4BACE,mBAAmB,WAAW,CAAC,UAClC,mBAAmB,aAAa,CAAC;AAE7C,YAAI,UAAU,KAAK,EAAE,UAAU,aAAa,gBAAgB,YAAY,CAAC;AACzE,YAAI,IAAI;AAAA;AAAA;AAAA;AAAA,iDAIiC,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAQtB,WAAW;AAAA;AAAA;AAAA,sCAGX,KAAK,UAAU,WAAW,CAAC;AAAA;AAAA,QAEzD;AAAA,MACF,OAAO;AACL,YAAI,UAAU,KAAK,EAAE,UAAU,SAAS,CAAC;AACzC,YAAI,IAAI;AAAA,MACV;AAAA,IACF,CAAC;AAGD,WAAO,OAAO,GAAG,aAAa,MAAM;AAClC,YAAM,OAAO,OAAO,QAAQ;AAC5B,YAAM,WAAW,oBAAoB,KAAK,IAAI;AAE9C,cAAQ,IAAIF,OAAM,KAAK;AAAA,yCAA4C,CAAC;AACpE,cAAQ,IAAIA,OAAM,KAAK,UAAU,QAAQ,CAAC;AAC1C,cAAQ,IAAI,EAAE;AAGd,YAAM,UAAU,QAAQ,aAAa,WAAW,SAAS,QAAQ,aAAa,UAAU,UAAU;AAClG,MAAAG,UAAS,SAAS,CAAC,QAAQ,GAAG,MAAM;AAAA,MAAC,CAAC;AAAA,IACxC,CAAC;AAGD,eAAW,MAAM;AACf,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,oCAAoC,CAAC;AAAA,IACxD,GAAG,IAAI,KAAK,GAAI;AAAA,EAClB,CAAC;AACH;;;AC5MA,SAAS,gBAAgB;AACzB;AAAA,EACE,cAAAC;AAAA,EACA,gBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,QAAAC,aAAY;AACrB,OAAOC,YAAW;AAElB,IAAM,oBAAoB;AAC1B,IAAM,kBAAkB;AAExB,IAAM,eAAe,GAAG,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYvC,eAAe;AAEjB,SAAS,aAA4B;AACnC,MAAI;AACF,UAAM,OAAO,SAAS,iCAAiC;AAAA,MACrD,UAAU;AAAA,MACV,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC,EAAE,KAAK;AACR,WAAO,QAAQ;AAAA,EACjB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAoB,SAAyB;AACpD,QAAM,WAAW,QAAQ,QAAQ,iBAAiB;AAClD,QAAM,SAAS,QAAQ,QAAQ,eAAe;AAC9C,MAAI,aAAa,MAAM,WAAW,MAAM,SAAS,UAAU;AACzD,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,MAAM,GAAG,QAAQ,EAAE,QAAQ,QAAQ,EAAE;AAC5D,QAAM,QAAQ,QAAQ,MAAM,SAAS,gBAAgB,MAAM,EAAE,QAAQ,QAAQ,EAAE;AAC/E,MAAI,UAAU,MAAO,QAAO,GAAG,MAAM;AAAA;AAAA,EAAO,KAAK;AAAA;AACjD,MAAI,OAAQ,QAAO,GAAG,MAAM;AAAA;AAC5B,MAAI,MAAO,QAAO,GAAG,KAAK;AAAA;AAC1B,SAAO;AACT;AAEA,eAAsB,mBAAmB,SAA6C;AACpF,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,SAAS;AACZ,YAAQ,IAAIA,OAAM,IAAI,8BAAyB,CAAC;AAChD,YAAQ,IAAIA,OAAM,KAAK,4CAA4C,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAWD,MAAK,SAAS,QAAQ,OAAO;AAC9C,QAAM,WAAWA,MAAK,UAAU,YAAY;AAE5C,MAAI,kBAAkB;AACtB,MAAI,cAAc;AAClB,MAAIF,YAAW,QAAQ,GAAG;AACxB,sBAAkBC,cAAa,UAAU,OAAO;AAChD,kBAAc;AAEd,UAAM,mBACJ,gBAAgB,SAAS,iBAAiB,KAAK,gBAAgB,SAAS,eAAe;AAEzF,QAAI,oBAAoB,CAAC,QAAQ,OAAO;AACtC,cAAQ,IAAIE,OAAM,OAAO,+CAA0C,CAAC;AACpE,cAAQ,IAAIA,OAAM,KAAK,0EAA0E,CAAC;AAClG;AAAA,IACF;AAEA,QAAI,oBAAoB,QAAQ,OAAO;AACrC,wBAAkB,oBAAoB,eAAe;AAAA,IACvD;AAAA,EACF;AAGA,MAAI;AACJ,MAAI,eAAe,gBAAgB,KAAK,GAAG;AAEzC,UAAM,UAAU,gBAAgB,QAAQ,QAAQ,EAAE;AAClD,iBAAa,GAAG,OAAO;AAAA;AAAA,EAAO,YAAY;AAAA;AAAA,EAC5C,OAAO;AACL,iBAAa;AAAA;AAAA,EAAgB,YAAY;AAAA;AAAA,EAC3C;AAEA,MAAI;AACF,kBAAc,UAAU,YAAY,OAAO;AAC3C,cAAU,UAAU,GAAK;AAAA,EAC3B,SAAS,KAAK;AACZ,YAAQ,IAAIA,OAAM,IAAI,2CAAuC,IAAc,OAAO,EAAE,CAAC;AACrF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAIA,OAAM,MAAM,8CAAyC,CAAC;AAClE,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAIA,OAAM,KAAK,2DAA2D,CAAC;AACnF,UAAQ,IAAIA,OAAM,KAAK,yBAAyB,IAAIA,OAAM,KAAK,wBAAwB,CAAC;AACxF,UAAQ,IAAIA,OAAM,KAAK,eAAe,IAAIA,OAAM,KAAK,2BAA2B,CAAC;AACnF;AAEA,eAAsB,uBAAsC;AAC1D,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,SAAS;AACZ,YAAQ,IAAIA,OAAM,IAAI,8BAAyB,CAAC;AAChD,YAAQ,IAAIA,OAAM,KAAK,4CAA4C,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,WAAWD,MAAK,SAAS,QAAQ,SAAS,YAAY;AAE5D,MAAI,CAACF,YAAW,QAAQ,GAAG;AACzB,YAAQ,IAAIG,OAAM,OAAO,yDAAoD,CAAC;AAC9E;AAAA,EACF;AAEA,QAAM,UAAUF,cAAa,UAAU,OAAO;AAC9C,MAAI,CAAC,QAAQ,SAAS,iBAAiB,GAAG;AACxC,YAAQ,IAAIE,OAAM,OAAO,uDAAkD,CAAC;AAC5E,YAAQ,IAAIA,OAAM,KAAK,sBAAsB,CAAC;AAC9C;AAAA,EACF;AAEA,QAAM,WAAW,oBAAoB,OAAO,EAAE,KAAK;AACnD,QAAM,cAAc,aAAa,eAAe,aAAa,uBAAuB,aAAa;AAEjG,MAAI;AACF,QAAI,aAAa;AACf,iBAAW,QAAQ;AACnB,cAAQ,IAAIA,OAAM,MAAM,oFAA+E,CAAC;AAAA,IAC1G,OAAO;AACL,oBAAc,UAAU,WAAW,MAAM,OAAO;AAChD,gBAAU,UAAU,GAAK;AACzB,cAAQ,IAAIA,OAAM,MAAM,iCAA4B,CAAC;AACrD,cAAQ,IAAIA,OAAM,KAAK,qCAAqC,CAAC;AAAA,IAC/D;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,IAAIA,OAAM,IAAI,oCAAgC,IAAc,OAAO,EAAE,CAAC;AAC9E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;;;ACrJA,OAAOC,YAAW;AAClB,SAAS,WAAW,cAAAC,aAAY,iBAAAC,gBAAe,gBAAAC,qBAAoB;AACnE,SAAS,QAAAC,OAAM,eAAe;AAC9B,SAAS,qBAAqB;AAe9B,eAAsB,qBAAqB,OAAuB,CAAC,GAAkB;AACnF,QAAM,MAAM,QAAQ,IAAI;AAIxB,QAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AACnD,QAAM,aAAa;AAAA,IACjBA,MAAK,MAAM,WAAW;AAAA,IACtBA,MAAK,MAAM,MAAM,WAAW;AAAA,IAC5BA,MAAK,MAAM,MAAM,OAAO,WAAW;AAAA,EACrC;AACA,QAAM,eAAe,WAAW,KAAK,CAAC,MAAMH,YAAWG,MAAK,GAAG,qBAAqB,CAAC,CAAC;AACtF,MAAI,CAAC,cAAc;AACjB,YAAQ,MAAMJ,OAAM,IAAI,0FAA0F,CAAC;AACnH,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAASG,cAAaC,MAAK,cAAc,qBAAqB,GAAG,OAAO;AAC9E,QAAM,YAAYD,cAAaC,MAAK,cAAc,wBAAwB,GAAG,OAAO;AAGpF,MAAI,CAAC,KAAK,YAAY;AACpB,UAAM,SAASA,MAAK,KAAK,WAAW,OAAO;AAC3C,UAAM,UAAUA,MAAK,QAAQ,yBAAyB;AACtD,QAAIH,YAAW,OAAO,KAAK,CAAC,KAAK,OAAO;AACtC,cAAQ,IAAID,OAAM,OAAO,YAAY,OAAO,6CAA6C,CAAC;AAAA,IAC5F,OAAO;AACL,gBAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACrC,MAAAE,eAAc,SAAS,MAAM;AAC7B,cAAQ,IAAIF,OAAM,MAAM,aAAa,OAAO,EAAE,CAAC;AAAA,IACjD;AAAA,EACF;AAGA,QAAM,aAAaI,MAAK,KAAK,cAAc;AAC3C,MAAIH,YAAW,UAAU,KAAK,CAAC,KAAK,OAAO;AACzC,YAAQ,IAAID,OAAM,OAAO,YAAY,UAAU,6CAA6C,CAAC;AAAA,EAC/F,OAAO;AACL,IAAAE,eAAc,YAAY,SAAS;AACnC,YAAQ,IAAIF,OAAM,MAAM,aAAa,UAAU,EAAE,CAAC;AAAA,EACpD;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAIA,OAAM,KAAK,mFAAmF,CAAC;AAC3G,UAAQ,IAAIA,OAAM,KAAK,2EAA2E,CAAC;AACrG;;;AlBpDA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,YAAY,EACjB;AAAA,EACC;AACF,EACC,QAAQ,OAAsB;AAEjC,QACG,QAAQ,MAAM,EACd,YAAY,+CAA+C,EAC3D,SAAS,eAAe,qBAAqB,GAAG,EAChD,OAAO,WAAW,0BAA0B,EAC5C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,iBAAiB,wBAAwB,KAAK,EACrD,OAAO,iBAAiB,wDAAwD,EAChF,OAAO,eAAe,oDAAoD,KAAK,EAC/E,OAAO,OAAO,WAAmB,SAAqG;AACrI,QAAM,YAAY,WAAW;AAAA,IAC3B;AAAA,IACA,YAAY,KAAK;AAAA,IACjB,QAAQ,KAAK;AAAA,IACb,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,EACd,CAAC;AACH,CAAC;AAGH,IAAM,OAAO,QACV,QAAQ,MAAM,EACd,YAAY,uBAAuB;AAEtC,KACG,QAAQ,OAAO,EACf,YAAY,mCAAmC,EAC/C,OAAO,YAAY;AAEtB,KACG,QAAQ,QAAQ,EAChB,YAAY,oCAAoC,EAChD,OAAO,aAAa;AAEvB,KACG,QAAQ,QAAQ,EAChB,YAAY,6BAA6B,EACzC,OAAO,aAAa;AAGvB,IAAM,OAAO,QACV,QAAQ,MAAM,EACd,YAAY,oDAAoD;AAEnE,KACG,QAAQ,SAAS,EACjB,YAAY,oEAAoE,EAChF,OAAO,eAAe,wDAAwD,KAAK,EACnF,OAAO,OAAO,SAA8B;AAC3C,QAAM,mBAAmB,EAAE,OAAO,KAAK,MAAM,CAAC;AAChD,CAAC;AAEH,KACG,QAAQ,WAAW,EACnB,YAAY,uCAAuC,EACnD,OAAO,oBAAoB;AAG9B,IAAM,SAAS,QACZ,QAAQ,QAAQ,EAChB,YAAY,+CAA+C;AAE9D,OACG,QAAQ,SAAS,EACjB,YAAY,0GAA0G,EACtH,OAAO,eAAe,iCAAiC,KAAK,EAC5D,OAAO,iBAAiB,wEAAwE,KAAK,EACrG,OAAO,OAAO,SAAoD;AACjE,QAAM,qBAAqB,EAAE,OAAO,KAAK,OAAO,YAAY,KAAK,WAAW,CAAC;AAC/E,CAAC;AAGH,QACG,QAAQ,SAAS,EACjB,YAAY,+CAA+C,EAC3D,OAAO,YAAY;AAClB,QAAM,EAAE,gBAAAK,iBAAgB,eAAe,IAAI,MAAM,OAAO,mBAAgB;AACxE,QAAMC,UAAS,MAAM,OAAO,OAAO,GAAG;AAEtC,QAAM,QAAQD,gBAAe;AAC7B,MAAI,CAAC,OAAO;AACV,YAAQ,IAAIC,OAAM,OAAO,4CAA4C,CAAC;AACtE;AAAA,EACF;AAEA,UAAQ,IAAIA,OAAM,KAAK,8BAA8B,CAAC;AACtD,QAAM,MAAM,MAAM,eAAe;AACjC,MAAI,KAAK;AACP,YAAQ,IAAIA,OAAM,MAAM;AAAA,0BAA6B,CAAC;AACtD,YAAQ,IAAIA,OAAM,KAAK,UAAU,GAAG,CAAC;AACrC,UAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,eAAoB;AACtD,UAAM,UAAU,QAAQ,aAAa,WAAW,SAAS,QAAQ,aAAa,UAAU,UAAU;AAClG,IAAAA,UAAS,SAAS,CAAC,GAAG,GAAG,MAAM;AAAA,IAAC,CAAC;AAAA,EACnC,OAAO;AACL,YAAQ,IAAID,OAAM,IAAI,sDAAsD,CAAC;AAAA,EAC/E;AACF,CAAC;AAEH,QAAQ,MAAM;","names":["resolve","join","chalk","readFileSync","join","existsSync","join","resolve","execFile","readFile","mkdtemp","rm","existsSync","join","tmpdir","resolve","execFile","mkdtemp","join","tmpdir","existsSync","readFile","rm","lines","getSnippet","getSnippet","getSnippet","resolve","chalk","execFile","join","result","execFile","chalk","ora","chalk","ora","resolve","execFile","existsSync","readFileSync","join","chalk","chalk","existsSync","writeFileSync","readFileSync","join","getStoredToken","chalk","execFile"]}
|