@vulcn/plugin-report 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/html.ts","../src/json.ts","../src/yaml.ts"],"sourcesContent":["/**\n * @vulcn/plugin-report\n * Report Generation Plugin for Vulcn\n *\n * Generates security reports in HTML, JSON, and YAML formats\n * after a run completes. Features:\n * - Modern dark-themed HTML report with Vulcn branding\n * - Machine-readable JSON for CI/CD integration\n * - Human-readable YAML for documentation\n *\n * Configuration:\n * format: \"html\" | \"json\" | \"yaml\" | \"all\" (default: \"html\")\n * outputDir: directory for reports (default: \".\")\n * filename: base filename (no extension) (default: \"vulcn-report\")\n * open: auto-open HTML in browser (default: false)\n */\n\nimport { z } from \"zod\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport type {\n VulcnPlugin,\n PluginContext,\n PluginRunContext,\n RunResult,\n} from \"@vulcn/engine\";\n\nimport { generateHtml, type HtmlReportData } from \"./html\";\nimport { generateJson, type JsonReport } from \"./json\";\nimport { generateYaml } from \"./yaml\";\n\n/**\n * Plugin configuration schema\n */\nconst configSchema = z.object({\n /**\n * Report format(s) to generate\n * - \"html\": Beautiful dark-themed HTML report\n * - \"json\": Machine-readable structured JSON\n * - \"yaml\": Human-readable YAML\n * - \"all\": Generate all three formats\n * @default \"html\"\n */\n format: z.enum([\"html\", \"json\", \"yaml\", \"all\"]).default(\"html\"),\n\n /**\n * Output directory for report files\n * @default \".\"\n */\n outputDir: z.string().default(\".\"),\n\n /**\n * Base filename (without extension) for the report\n * @default \"vulcn-report\"\n */\n filename: z.string().default(\"vulcn-report\"),\n\n /**\n * Auto-open HTML report in default browser after generation\n * @default false\n */\n open: z.boolean().default(false),\n});\n\nexport type ReportConfig = z.infer<typeof configSchema>;\n\n/**\n * Determine which formats to generate\n */\nfunction getFormats(format: ReportConfig[\"format\"]): string[] {\n if (format === \"all\") return [\"html\", \"json\", \"yaml\"];\n return [format];\n}\n\n/**\n * Report Plugin\n */\nconst plugin: VulcnPlugin = {\n name: \"@vulcn/plugin-report\",\n version: \"0.1.0\",\n apiVersion: 1,\n description:\n \"Report generation plugin — generates beautiful HTML, JSON, and YAML security reports\",\n\n configSchema,\n\n hooks: {\n onInit: async (ctx: PluginContext) => {\n const config = configSchema.parse(ctx.config);\n ctx.logger.info(\n `Report plugin initialized (format: ${config.format}, output: ${config.outputDir}/${config.filename})`,\n );\n },\n\n /**\n * Generate report(s) after run completes\n */\n onRunEnd: async (\n result: RunResult,\n ctx: PluginRunContext,\n ): Promise<RunResult> => {\n const config = configSchema.parse(ctx.config);\n const formats = getFormats(config.format);\n const generatedAt = new Date().toISOString();\n const engineVersion = ctx.engine.version;\n\n // Ensure output directory exists\n const outDir = resolve(config.outputDir);\n await mkdir(outDir, { recursive: true });\n\n const basePath = resolve(outDir, config.filename);\n const writtenFiles: string[] = [];\n\n for (const fmt of formats) {\n try {\n switch (fmt) {\n case \"html\": {\n const htmlData: HtmlReportData = {\n session: ctx.session,\n result,\n generatedAt,\n engineVersion,\n };\n const html = generateHtml(htmlData);\n const htmlPath = `${basePath}.html`;\n await writeFile(htmlPath, html, \"utf-8\");\n writtenFiles.push(htmlPath);\n ctx.logger.info(`📄 HTML report: ${htmlPath}`);\n break;\n }\n\n case \"json\": {\n const jsonReport = generateJson(\n ctx.session,\n result,\n generatedAt,\n engineVersion,\n );\n const jsonPath = `${basePath}.json`;\n await writeFile(\n jsonPath,\n JSON.stringify(jsonReport, null, 2),\n \"utf-8\",\n );\n writtenFiles.push(jsonPath);\n ctx.logger.info(`📄 JSON report: ${jsonPath}`);\n break;\n }\n\n case \"yaml\": {\n const yamlContent = generateYaml(\n ctx.session,\n result,\n generatedAt,\n engineVersion,\n );\n const yamlPath = `${basePath}.yml`;\n await writeFile(yamlPath, yamlContent, \"utf-8\");\n writtenFiles.push(yamlPath);\n ctx.logger.info(`📄 YAML report: ${yamlPath}`);\n break;\n }\n }\n } catch (err) {\n ctx.logger.error(\n `Failed to generate ${fmt} report: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n // Auto-open HTML report if configured\n if (config.open && formats.includes(\"html\")) {\n const htmlPath = `${basePath}.html`;\n try {\n const { exec } = await import(\"node:child_process\");\n const openCmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"start\"\n : \"xdg-open\";\n exec(`${openCmd} \"${htmlPath}\"`);\n } catch {\n // Silently ignore if can't open browser\n }\n }\n\n return result;\n },\n },\n};\n\nexport default plugin;\n\n// Named exports for programmatic usage\nexport { configSchema, generateHtml, generateJson, generateYaml };\nexport type { HtmlReportData, JsonReport };\n","/**\n * HTML Report Generator for Vulcn\n *\n * Generates a modern, dark-themed security report with:\n * - Vulcn branding (shield gradient logo)\n * - Executive summary with severity donut chart\n * - Detailed findings with expandable evidence\n * - Timeline of execution\n * - Responsive design\n */\n\nimport type { Finding, RunResult, Session } from \"@vulcn/engine\";\n\nexport interface HtmlReportData {\n session: Session;\n result: RunResult;\n generatedAt: string;\n engineVersion: string;\n}\n\n// Vulcn brand colors\nconst COLORS = {\n bg: \"#0a0a0f\",\n surface: \"#12121a\",\n surfaceHover: \"#1a1a26\",\n border: \"#1e1e2e\",\n borderActive: \"#2a2a3e\",\n text: \"#e4e4ef\",\n textMuted: \"#8888a0\",\n textDim: \"#555570\",\n accent: \"#fa1b1b\",\n accentGlow: \"rgba(250, 27, 27, 0.15)\",\n accentLight: \"#ff9c9c\",\n critical: \"#ff1744\",\n high: \"#ff5252\",\n medium: \"#ffab40\",\n low: \"#66bb6a\",\n info: \"#42a5f5\",\n success: \"#00e676\",\n};\n\nfunction severityColor(severity: string): string {\n switch (severity) {\n case \"critical\":\n return COLORS.critical;\n case \"high\":\n return COLORS.high;\n case \"medium\":\n return COLORS.medium;\n case \"low\":\n return COLORS.low;\n case \"info\":\n return COLORS.info;\n default:\n return COLORS.textMuted;\n }\n}\n\nfunction severityOrder(severity: string): number {\n switch (severity) {\n case \"critical\":\n return 0;\n case \"high\":\n return 1;\n case \"medium\":\n return 2;\n case \"low\":\n return 3;\n case \"info\":\n return 4;\n default:\n return 5;\n }\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const seconds = (ms / 1000).toFixed(1);\n return `${seconds}s`;\n}\n\nfunction formatDate(iso: string): string {\n const d = new Date(iso);\n return d.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n timeZoneName: \"short\",\n });\n}\n\n// Inline SVG logo matching the vulcn shield branding\nconst VULCN_LOGO_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"32\" height=\"32\">\n <defs>\n <linearGradient id=\"lg1\" x1=\"0\" x2=\"1\" y1=\"0\" y2=\"0\" gradientTransform=\"matrix(7 -13 13 7 7 17)\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#fa1b1b\"/>\n <stop offset=\"1\" stop-color=\"#ff9c9c\"/>\n </linearGradient>\n <linearGradient id=\"lg2\" x1=\"0\" x2=\"1\" y1=\"0\" y2=\"0\" gradientTransform=\"matrix(3 -6 6 3 13 14)\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#ff9c9c\"/>\n <stop offset=\"1\" stop-color=\"#ffffff\"/>\n </linearGradient>\n </defs>\n <path fill=\"url(#lg1)\" d=\"m 11,17 c 0,0.552 -0.448,1 -1,1 -0.552,0 -1,-0.448 -1,-1 0,-0.552 0.448,-1 1,-1 0.552,0 1,0.448 1,1 z M 10,15 C 8,15 7.839,16.622 7.803,16.68 7.51,17.147 6.892,17.288 6.425,16.995 3.592,15.216 2.389,11.366 2.014,9.168 1.977,8.951 1.952,8.743 1.936,8.547 1.936,8.544 1.935,8.541 1.935,8.538 1.844,7.291 2.572,6.13 3.733,5.667 3.736,5.666 3.738,5.665 3.74,5.664 4.948,5.193 5.913,4.705 6.583,3.641 6.586,3.636 6.588,3.632 6.591,3.628 7.235,2.637 8.332,2.035 9.506,2.023 9.817,2.001 10.141,2 10.451,2 c 0,0 0,0 0,0 1.202,0 2.322,0.608 2.977,1.616 0.005,0.008 0.01,0.017 0.015,0.025 0.651,1.07 1.614,1.554 2.817,2.022 0.002,0 0.005,10e-4 0.007,0.002 1.162,0.463 1.89,1.626 1.799,2.873 0,0.006 -10e-4,0.012 -10e-4,0.018 -0.018,0.193 -0.043,0.397 -0.079,0.612 -0.375,2.198 -1.578,6.048 -4.411,7.827 C 13.108,17.288 12.49,17.147 12.197,16.68 12.161,16.622 12,15 10,15 Z\"/>\n <path fill=\"#dc2626\" d=\"m 13.0058,9.89 c -0.164,1.484 -0.749,2.568 -1.659,3.353 -0.418,0.36 -0.465,0.992 -0.104,1.41 0.36,0.418 0.992,0.465 1.41,0.104 1.266,-1.092 2.112,-2.583 2.341,-4.647 0.061,-0.548 -0.335,-1.043 -0.884,-1.104 -0.548,-0.061 -1.043,0.335 -1.104,0.884 z\"/>\n <path fill=\"url(#lg2)\" d=\"m 14.0058,8.89 c -0.164,1.484 -0.749,2.568 -1.659,3.353 -0.418,0.36 -0.465,0.992 -0.104,1.41 0.36,0.418 0.992,0.465 1.41,0.104 1.266,-1.092 2.112,-2.583 2.341,-4.647 0.061,-0.548 -0.335,-1.043 -0.884,-1.104 -0.548,-0.061 -1.043,0.335 -1.104,0.884 z\"/>\n</svg>`;\n\nexport function generateHtml(data: HtmlReportData): string {\n const { session, result, generatedAt, engineVersion } = data;\n const findings = [...result.findings].sort(\n (a, b) => severityOrder(a.severity) - severityOrder(b.severity),\n );\n\n // Severity counts for donut chart\n const counts: Record<string, number> = {\n critical: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n for (const f of findings) {\n counts[f.severity] = (counts[f.severity] || 0) + 1;\n }\n\n const totalFindings = findings.length;\n const hasFindings = totalFindings > 0;\n\n // Overall risk score\n const riskScore =\n counts.critical * 10 + counts.high * 7 + counts.medium * 4 + counts.low * 1;\n const maxRisk = totalFindings * 10 || 1;\n const riskPercent = Math.min(100, Math.round((riskScore / maxRisk) * 100));\n const riskLabel =\n riskPercent >= 80\n ? \"Critical\"\n : riskPercent >= 50\n ? \"High\"\n : riskPercent >= 25\n ? \"Medium\"\n : riskPercent > 0\n ? \"Low\"\n : \"Clear\";\n const riskColor =\n riskPercent >= 80\n ? COLORS.critical\n : riskPercent >= 50\n ? COLORS.high\n : riskPercent >= 25\n ? COLORS.medium\n : riskPercent > 0\n ? COLORS.low\n : COLORS.success;\n\n // Donut chart SVG segments\n const donutSvg = generateDonut(counts, totalFindings);\n\n // Unique URLs affected\n const affectedUrls = [...new Set(findings.map((f) => f.url))];\n\n // Unique vuln types\n const vulnTypes = [...new Set(findings.map((f) => f.type))];\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Vulcn Security Report — ${escapeHtml(session.name)}</title>\n <style>\n @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap');\n\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n :root {\n --bg: ${COLORS.bg};\n --surface: ${COLORS.surface};\n --surface-hover: ${COLORS.surfaceHover};\n --border: ${COLORS.border};\n --border-active: ${COLORS.borderActive};\n --text: ${COLORS.text};\n --text-muted: ${COLORS.textMuted};\n --text-dim: ${COLORS.textDim};\n --accent: ${COLORS.accent};\n --accent-glow: ${COLORS.accentGlow};\n --accent-light: ${COLORS.accentLight};\n --radius: 12px;\n --radius-sm: 8px;\n --radius-xs: 6px;\n }\n\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: var(--bg);\n color: var(--text);\n line-height: 1.6;\n min-height: 100vh;\n }\n\n /* Ambient gradient background */\n body::before {\n content: '';\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 600px;\n background: radial-gradient(ellipse 80% 50% at 50% -20%, ${COLORS.accentGlow} 0%, transparent 100%);\n pointer-events: none;\n z-index: 0;\n }\n\n .container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 40px 24px;\n position: relative;\n z-index: 1;\n }\n\n /* Header */\n .header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 48px;\n padding-bottom: 24px;\n border-bottom: 1px solid var(--border);\n }\n\n .header-brand {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-brand svg {\n filter: drop-shadow(0 0 8px rgba(250, 27, 27, 0.3));\n }\n\n .header-brand h1 {\n font-size: 20px;\n font-weight: 700;\n letter-spacing: -0.02em;\n background: linear-gradient(135deg, #fa1b1b, #ff9c9c);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n }\n\n .header-brand span {\n font-size: 11px;\n font-weight: 500;\n color: var(--text-dim);\n text-transform: uppercase;\n letter-spacing: 0.1em;\n }\n\n .header-meta {\n text-align: right;\n font-size: 12px;\n color: var(--text-dim);\n line-height: 1.8;\n }\n\n /* Session info */\n .session-info {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 24px;\n margin-bottom: 32px;\n }\n\n .session-info h2 {\n font-size: 22px;\n font-weight: 700;\n margin-bottom: 16px;\n letter-spacing: -0.02em;\n }\n\n .session-meta {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n }\n\n .meta-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .meta-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--text-dim);\n }\n\n .meta-value {\n font-size: 14px;\n font-weight: 500;\n color: var(--text);\n font-family: 'JetBrains Mono', monospace;\n font-size: 13px;\n }\n\n /* Stats grid */\n .stats-grid {\n display: grid;\n grid-template-columns: 1fr 1.5fr;\n gap: 24px;\n margin-bottom: 32px;\n }\n\n @media (max-width: 768px) {\n .stats-grid { grid-template-columns: 1fr; }\n }\n\n /* Risk gauge */\n .risk-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 32px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 20px;\n }\n\n .risk-card h3 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--text-dim);\n width: 100%;\n }\n\n .risk-gauge {\n position: relative;\n width: 160px;\n height: 160px;\n }\n\n .risk-gauge svg {\n transform: rotate(-90deg);\n }\n\n .risk-gauge-label {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n }\n\n .risk-gauge-label .score {\n font-size: 36px;\n font-weight: 800;\n letter-spacing: -0.03em;\n }\n\n .risk-gauge-label .label {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--text-muted);\n }\n\n /* Summary card */\n .summary-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 32px;\n }\n\n .summary-card h3 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--text-dim);\n margin-bottom: 20px;\n }\n\n .summary-stats {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 20px;\n }\n\n .stat-box {\n padding: 16px;\n background: rgba(255,255,255,0.02);\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n transition: border-color 0.2s;\n }\n\n .stat-box:hover { border-color: var(--border-active); }\n\n .stat-number {\n font-size: 28px;\n font-weight: 800;\n letter-spacing: -0.03em;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .stat-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--text-muted);\n }\n\n /* Severity breakdown */\n .severity-breakdown {\n margin-bottom: 32px;\n }\n\n .severity-section-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n }\n\n .severity-section-header h3 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--text-dim);\n }\n\n .severity-bars {\n display: flex;\n gap: 8px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 20px 24px;\n }\n\n .severity-bar-item {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n }\n\n .severity-bar-track {\n width: 100%;\n height: 6px;\n background: rgba(255,255,255,0.04);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .severity-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.5s ease;\n }\n\n .severity-bar-label {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--text-dim);\n }\n\n .severity-bar-count {\n font-size: 18px;\n font-weight: 700;\n font-family: 'JetBrains Mono', monospace;\n }\n\n /* Findings section */\n .findings-section {\n margin-bottom: 32px;\n }\n\n .findings-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n }\n\n .findings-header h3 {\n font-size: 18px;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n .findings-count {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-dim);\n padding: 4px 12px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: 100px;\n }\n\n /* Finding card */\n .finding-card {\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 12px;\n overflow: hidden;\n transition: border-color 0.2s;\n }\n\n .finding-card:hover { border-color: var(--border-active); }\n\n .finding-header {\n padding: 20px 24px;\n display: flex;\n align-items: flex-start;\n gap: 16px;\n cursor: pointer;\n user-select: none;\n }\n\n .finding-severity-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n margin-top: 6px;\n box-shadow: 0 0 8px currentColor;\n }\n\n .finding-info {\n flex: 1;\n min-width: 0;\n }\n\n .finding-title {\n font-size: 15px;\n font-weight: 600;\n margin-bottom: 4px;\n letter-spacing: -0.01em;\n }\n\n .finding-subtitle {\n font-size: 12px;\n color: var(--text-muted);\n display: flex;\n gap: 16px;\n flex-wrap: wrap;\n }\n\n .finding-tag {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n }\n\n .finding-expand-icon {\n font-size: 18px;\n color: var(--text-dim);\n transition: transform 0.2s;\n flex-shrink: 0;\n margin-top: 2px;\n }\n\n .finding-card.open .finding-expand-icon {\n transform: rotate(180deg);\n }\n\n .finding-details {\n display: none;\n padding: 0 24px 20px;\n border-top: 1px solid var(--border);\n }\n\n .finding-card.open .finding-details {\n display: block;\n padding-top: 20px;\n }\n\n .detail-row {\n display: grid;\n grid-template-columns: 120px 1fr;\n gap: 8px;\n margin-bottom: 12px;\n align-items: baseline;\n }\n\n .detail-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--text-dim);\n }\n\n .detail-value {\n font-size: 13px;\n color: var(--text);\n word-break: break-all;\n }\n\n .evidence-box {\n background: rgba(255,255,255,0.02);\n border: 1px solid var(--border);\n border-radius: var(--radius-xs);\n padding: 12px 16px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n color: var(--text-muted);\n line-height: 1.5;\n overflow-x: auto;\n white-space: pre-wrap;\n }\n\n .payload-box {\n background: rgba(250, 27, 27, 0.06);\n border: 1px solid rgba(250, 27, 27, 0.15);\n border-radius: var(--radius-xs);\n padding: 8px 12px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n color: var(--accent-light);\n word-break: break-all;\n }\n\n /* No findings */\n .no-findings {\n text-align: center;\n padding: 60px 24px;\n background: var(--surface);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n\n .no-findings .icon { font-size: 48px; margin-bottom: 16px; }\n .no-findings h3 { font-size: 20px; font-weight: 700; color: ${COLORS.success}; margin-bottom: 8px; }\n .no-findings p { font-size: 14px; color: var(--text-muted); }\n\n /* Errors section */\n .errors-section {\n margin-bottom: 32px;\n }\n\n .errors-section h3 {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-muted);\n margin-bottom: 12px;\n }\n\n .error-item {\n padding: 10px 16px;\n background: rgba(255, 171, 64, 0.04);\n border: 1px solid rgba(255, 171, 64, 0.1);\n border-radius: var(--radius-xs);\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n color: ${COLORS.medium};\n margin-bottom: 6px;\n }\n\n /* Footer */\n .footer {\n text-align: center;\n padding: 32px 0;\n border-top: 1px solid var(--border);\n margin-top: 48px;\n color: var(--text-dim);\n font-size: 12px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n }\n\n .footer a {\n color: var(--accent-light);\n text-decoration: none;\n }\n\n .footer a:hover { text-decoration: underline; }\n\n /* Animations */\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(12px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n .animate-in {\n animation: fadeIn 0.4s ease-out;\n }\n\n .animate-in-delay { animation: fadeIn 0.4s ease-out 0.1s both; }\n .animate-in-delay-2 { animation: fadeIn 0.4s ease-out 0.2s both; }\n .animate-in-delay-3 { animation: fadeIn 0.4s ease-out 0.3s both; }\n\n /* Print styles */\n @media print {\n body { background: white; color: #111; }\n body::before { display: none; }\n .finding-details { display: block !important; padding-top: 12px !important; }\n .finding-card { page-break-inside: avoid; }\n }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <!-- Header -->\n <div class=\"header animate-in\">\n <div class=\"header-brand\">\n ${VULCN_LOGO_SVG}\n <div>\n <h1>vulcn</h1>\n <span>Security Report</span>\n </div>\n </div>\n <div class=\"header-meta\">\n <div>${formatDate(generatedAt)}</div>\n <div>Engine v${escapeHtml(engineVersion)}</div>\n </div>\n </div>\n\n <!-- Session info -->\n <div class=\"session-info animate-in-delay\">\n <h2>${escapeHtml(session.name)}</h2>\n <div class=\"session-meta\">\n <div class=\"meta-item\">\n <span class=\"meta-label\">Driver</span>\n <span class=\"meta-value\">${escapeHtml(session.driver)}</span>\n </div>\n ${session.driverConfig?.startUrl ? `<div class=\"meta-item\"><span class=\"meta-label\">Target URL</span><span class=\"meta-value\">${escapeHtml(String(session.driverConfig.startUrl))}</span></div>` : \"\"}\n <div class=\"meta-item\">\n <span class=\"meta-label\">Duration</span>\n <span class=\"meta-value\">${formatDuration(result.duration)}</span>\n </div>\n <div class=\"meta-item\">\n <span class=\"meta-label\">Generated</span>\n <span class=\"meta-value\">${formatDate(generatedAt)}</span>\n </div>\n </div>\n </div>\n\n <!-- Stats grid: Risk + Summary -->\n <div class=\"stats-grid animate-in-delay-2\">\n <div class=\"risk-card\">\n <h3>Risk Level</h3>\n <div class=\"risk-gauge\">\n <svg viewBox=\"0 0 160 160\" width=\"160\" height=\"160\">\n <circle cx=\"80\" cy=\"80\" r=\"68\" fill=\"none\" stroke=\"rgba(255,255,255,0.04)\" stroke-width=\"10\"/>\n <circle cx=\"80\" cy=\"80\" r=\"68\" fill=\"none\" stroke=\"${riskColor}\" stroke-width=\"10\"\n stroke-dasharray=\"${(riskPercent / 100) * 427} 427\"\n stroke-linecap=\"round\"\n style=\"filter: drop-shadow(0 0 6px ${riskColor});\"/>\n </svg>\n <div class=\"risk-gauge-label\">\n <div class=\"score\" style=\"color: ${riskColor}\">${hasFindings ? riskPercent : 0}</div>\n <div class=\"label\">${riskLabel}</div>\n </div>\n </div>\n </div>\n\n <div class=\"summary-card\">\n <h3>Execution Summary</h3>\n <div class=\"summary-stats\">\n <div class=\"stat-box\">\n <div class=\"stat-number\" style=\"color: ${hasFindings ? COLORS.high : COLORS.success}\">${totalFindings}</div>\n <div class=\"stat-label\">Findings</div>\n </div>\n <div class=\"stat-box\">\n <div class=\"stat-number\">${result.payloadsTested}</div>\n <div class=\"stat-label\">Payloads Tested</div>\n </div>\n <div class=\"stat-box\">\n <div class=\"stat-number\">${result.stepsExecuted}</div>\n <div class=\"stat-label\">Steps Executed</div>\n </div>\n <div class=\"stat-box\">\n <div class=\"stat-number\">${affectedUrls.length}</div>\n <div class=\"stat-label\">URLs Affected</div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Severity breakdown -->\n <div class=\"severity-breakdown animate-in-delay-2\">\n <div class=\"severity-bars\">\n ${[\"critical\", \"high\", \"medium\", \"low\", \"info\"]\n .map(\n (sev) => `\n <div class=\"severity-bar-item\">\n <div class=\"severity-bar-count\" style=\"color: ${severityColor(sev)}\">${counts[sev]}</div>\n <div class=\"severity-bar-track\">\n <div class=\"severity-bar-fill\" style=\"width: ${totalFindings ? (counts[sev] / totalFindings) * 100 : 0}%; background: ${severityColor(sev)};\"></div>\n </div>\n <div class=\"severity-bar-label\">${sev}</div>\n </div>\n `,\n )\n .join(\"\")}\n </div>\n </div>\n\n <!-- Findings -->\n <div class=\"findings-section animate-in-delay-3\">\n <div class=\"findings-header\">\n <h3>Findings</h3>\n <span class=\"findings-count\">${totalFindings} total</span>\n </div>\n\n ${\n hasFindings\n ? findings\n .map(\n (f, i) => `\n <div class=\"finding-card\" onclick=\"this.classList.toggle('open')\">\n <div class=\"finding-header\">\n <div class=\"finding-severity-dot\" style=\"color: ${severityColor(f.severity)}; background: ${severityColor(f.severity)};\"></div>\n <div class=\"finding-info\">\n <div class=\"finding-title\">${escapeHtml(f.title)}</div>\n <div class=\"finding-subtitle\">\n <span class=\"finding-tag\" style=\"color: ${severityColor(f.severity)}\">${f.severity.toUpperCase()}</span>\n <span class=\"finding-tag\">${escapeHtml(f.type)}</span>\n <span class=\"finding-tag\">${escapeHtml(f.stepId)}</span>\n </div>\n </div>\n <span class=\"finding-expand-icon\">▾</span>\n </div>\n <div class=\"finding-details\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Description</span>\n <span class=\"detail-value\">${escapeHtml(f.description)}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">URL</span>\n <span class=\"detail-value\">${escapeHtml(f.url)}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Payload</span>\n <div class=\"payload-box\">${escapeHtml(f.payload)}</div>\n </div>\n ${\n f.evidence\n ? `\n <div class=\"detail-row\">\n <span class=\"detail-label\">Evidence</span>\n <div class=\"evidence-box\">${escapeHtml(f.evidence)}</div>\n </div>\n `\n : \"\"\n }\n ${\n f.metadata\n ? `\n <div class=\"detail-row\">\n <span class=\"detail-label\">Metadata</span>\n <div class=\"evidence-box\">${escapeHtml(JSON.stringify(f.metadata, null, 2))}</div>\n </div>\n `\n : \"\"\n }\n </div>\n </div>\n `,\n )\n .join(\"\")\n : `\n <div class=\"no-findings\">\n <div class=\"icon\">🛡️</div>\n <h3>No Vulnerabilities Detected</h3>\n <p>${result.payloadsTested} payloads were tested across ${result.stepsExecuted} steps with no findings.</p>\n </div>\n `\n }\n </div>\n\n ${\n result.errors.length > 0\n ? `\n <div class=\"errors-section\">\n <h3>⚠️ Errors During Execution (${result.errors.length})</h3>\n ${result.errors.map((e) => `<div class=\"error-item\">${escapeHtml(e)}</div>`).join(\"\")}\n </div>\n `\n : \"\"\n }\n\n <!-- Footer -->\n <div class=\"footer\">\n <div>Generated by ${VULCN_LOGO_SVG.replace(/width=\"32\"/g, 'width=\"16\"').replace(/height=\"32\"/g, 'height=\"16\"')} <strong>Vulcn</strong> — Security Testing Engine</div>\n <div><a href=\"https://docs.vulcn.dev\">docs.vulcn.dev</a></div>\n </div>\n </div>\n</body>\n</html>`;\n}\n\n/**\n * Generate SVG donut chart segments (unused in current layout but available)\n */\nfunction generateDonut(counts: Record<string, number>, total: number): string {\n if (total === 0) return \"\";\n const radius = 60;\n const circumference = 2 * Math.PI * radius;\n let offset = 0;\n\n const segments = [\"critical\", \"high\", \"medium\", \"low\", \"info\"]\n .filter((sev) => counts[sev] > 0)\n .map((sev) => {\n const pct = counts[sev] / total;\n const dash = pct * circumference;\n const seg = `<circle cx=\"80\" cy=\"80\" r=\"${radius}\" fill=\"none\" stroke=\"${severityColor(sev)}\" stroke-width=\"14\"\n stroke-dasharray=\"${dash} ${circumference - dash}\"\n stroke-dashoffset=\"${-offset}\"\n opacity=\"0.9\"/>`;\n offset += dash;\n return seg;\n });\n\n return `<svg viewBox=\"0 0 160 160\" width=\"120\" height=\"120\" style=\"transform:rotate(-90deg)\">\n <circle cx=\"80\" cy=\"80\" r=\"${radius}\" fill=\"none\" stroke=\"rgba(255,255,255,0.04)\" stroke-width=\"14\"/>\n ${segments.join(\"\\n \")}\n </svg>`;\n}\n","/**\n * JSON Report Generator for Vulcn\n *\n * Produces a structured, machine-readable JSON report.\n */\n\nimport type { Finding, RunResult, Session } from \"@vulcn/engine\";\n\nexport interface JsonReport {\n vulcn: {\n version: string;\n reportVersion: string;\n generatedAt: string;\n };\n session: {\n name: string;\n driver: string;\n driverConfig: Record<string, unknown>;\n stepsCount: number;\n metadata?: Record<string, unknown>;\n };\n execution: {\n stepsExecuted: number;\n payloadsTested: number;\n durationMs: number;\n durationFormatted: string;\n errors: string[];\n };\n summary: {\n totalFindings: number;\n riskScore: number;\n severityCounts: Record<string, number>;\n vulnerabilityTypes: string[];\n affectedUrls: string[];\n };\n findings: Finding[];\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function generateJson(\n session: Session,\n result: RunResult,\n generatedAt: string,\n engineVersion: string,\n): JsonReport {\n const counts: Record<string, number> = {\n critical: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n for (const f of result.findings) {\n counts[f.severity] = (counts[f.severity] || 0) + 1;\n }\n\n const riskScore =\n counts.critical * 10 + counts.high * 7 + counts.medium * 4 + counts.low * 1;\n\n return {\n vulcn: {\n version: engineVersion,\n reportVersion: \"1.0\",\n generatedAt,\n },\n session: {\n name: session.name,\n driver: session.driver,\n driverConfig: session.driverConfig,\n stepsCount: session.steps.length,\n metadata: session.metadata,\n },\n execution: {\n stepsExecuted: result.stepsExecuted,\n payloadsTested: result.payloadsTested,\n durationMs: result.duration,\n durationFormatted: formatDuration(result.duration),\n errors: result.errors,\n },\n summary: {\n totalFindings: result.findings.length,\n riskScore,\n severityCounts: counts,\n vulnerabilityTypes: [...new Set(result.findings.map((f) => f.type))],\n affectedUrls: [...new Set(result.findings.map((f) => f.url))],\n },\n findings: result.findings,\n };\n}\n","/**\n * YAML Report Generator for Vulcn\n *\n * Produces a human-readable YAML report.\n */\n\nimport { stringify } from \"yaml\";\nimport type { RunResult, Session } from \"@vulcn/engine\";\nimport { generateJson } from \"./json\";\n\nexport function generateYaml(\n session: Session,\n result: RunResult,\n generatedAt: string,\n engineVersion: string,\n): string {\n const report = generateJson(session, result, generatedAt, engineVersion);\n\n // YAML with header comment\n const header = [\n \"# ──────────────────────────────────────────────\",\n \"# Vulcn Security Report\",\n `# Generated: ${generatedAt}`,\n `# Session: ${session.name}`,\n `# Findings: ${result.findings.length}`,\n \"# ──────────────────────────────────────────────\",\n \"\",\n ].join(\"\\n\");\n\n return header + stringify(report, { indent: 2 });\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["formatDuration"]}
@@ -0,0 +1,130 @@
1
+ import { z } from 'zod';
2
+ import { Session, RunResult, Finding, VulcnPlugin } from '@vulcn/engine';
3
+
4
+ /**
5
+ * HTML Report Generator for Vulcn
6
+ *
7
+ * Generates a modern, dark-themed security report with:
8
+ * - Vulcn branding (shield gradient logo)
9
+ * - Executive summary with severity donut chart
10
+ * - Detailed findings with expandable evidence
11
+ * - Timeline of execution
12
+ * - Responsive design
13
+ */
14
+
15
+ interface HtmlReportData {
16
+ session: Session;
17
+ result: RunResult;
18
+ generatedAt: string;
19
+ engineVersion: string;
20
+ }
21
+ declare function generateHtml(data: HtmlReportData): string;
22
+
23
+ /**
24
+ * JSON Report Generator for Vulcn
25
+ *
26
+ * Produces a structured, machine-readable JSON report.
27
+ */
28
+
29
+ interface JsonReport {
30
+ vulcn: {
31
+ version: string;
32
+ reportVersion: string;
33
+ generatedAt: string;
34
+ };
35
+ session: {
36
+ name: string;
37
+ driver: string;
38
+ driverConfig: Record<string, unknown>;
39
+ stepsCount: number;
40
+ metadata?: Record<string, unknown>;
41
+ };
42
+ execution: {
43
+ stepsExecuted: number;
44
+ payloadsTested: number;
45
+ durationMs: number;
46
+ durationFormatted: string;
47
+ errors: string[];
48
+ };
49
+ summary: {
50
+ totalFindings: number;
51
+ riskScore: number;
52
+ severityCounts: Record<string, number>;
53
+ vulnerabilityTypes: string[];
54
+ affectedUrls: string[];
55
+ };
56
+ findings: Finding[];
57
+ }
58
+ declare function generateJson(session: Session, result: RunResult, generatedAt: string, engineVersion: string): JsonReport;
59
+
60
+ /**
61
+ * YAML Report Generator for Vulcn
62
+ *
63
+ * Produces a human-readable YAML report.
64
+ */
65
+
66
+ declare function generateYaml(session: Session, result: RunResult, generatedAt: string, engineVersion: string): string;
67
+
68
+ /**
69
+ * @vulcn/plugin-report
70
+ * Report Generation Plugin for Vulcn
71
+ *
72
+ * Generates security reports in HTML, JSON, and YAML formats
73
+ * after a run completes. Features:
74
+ * - Modern dark-themed HTML report with Vulcn branding
75
+ * - Machine-readable JSON for CI/CD integration
76
+ * - Human-readable YAML for documentation
77
+ *
78
+ * Configuration:
79
+ * format: "html" | "json" | "yaml" | "all" (default: "html")
80
+ * outputDir: directory for reports (default: ".")
81
+ * filename: base filename (no extension) (default: "vulcn-report")
82
+ * open: auto-open HTML in browser (default: false)
83
+ */
84
+
85
+ /**
86
+ * Plugin configuration schema
87
+ */
88
+ declare const configSchema: z.ZodObject<{
89
+ /**
90
+ * Report format(s) to generate
91
+ * - "html": Beautiful dark-themed HTML report
92
+ * - "json": Machine-readable structured JSON
93
+ * - "yaml": Human-readable YAML
94
+ * - "all": Generate all three formats
95
+ * @default "html"
96
+ */
97
+ format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "all"]>>;
98
+ /**
99
+ * Output directory for report files
100
+ * @default "."
101
+ */
102
+ outputDir: z.ZodDefault<z.ZodString>;
103
+ /**
104
+ * Base filename (without extension) for the report
105
+ * @default "vulcn-report"
106
+ */
107
+ filename: z.ZodDefault<z.ZodString>;
108
+ /**
109
+ * Auto-open HTML report in default browser after generation
110
+ * @default false
111
+ */
112
+ open: z.ZodDefault<z.ZodBoolean>;
113
+ }, "strip", z.ZodTypeAny, {
114
+ format: "html" | "json" | "yaml" | "all";
115
+ outputDir: string;
116
+ filename: string;
117
+ open: boolean;
118
+ }, {
119
+ format?: "html" | "json" | "yaml" | "all" | undefined;
120
+ outputDir?: string | undefined;
121
+ filename?: string | undefined;
122
+ open?: boolean | undefined;
123
+ }>;
124
+ type ReportConfig = z.infer<typeof configSchema>;
125
+ /**
126
+ * Report Plugin
127
+ */
128
+ declare const plugin: VulcnPlugin;
129
+
130
+ export { type HtmlReportData, type JsonReport, type ReportConfig, configSchema, plugin as default, generateHtml, generateJson, generateYaml };
@@ -0,0 +1,130 @@
1
+ import { z } from 'zod';
2
+ import { Session, RunResult, Finding, VulcnPlugin } from '@vulcn/engine';
3
+
4
+ /**
5
+ * HTML Report Generator for Vulcn
6
+ *
7
+ * Generates a modern, dark-themed security report with:
8
+ * - Vulcn branding (shield gradient logo)
9
+ * - Executive summary with severity donut chart
10
+ * - Detailed findings with expandable evidence
11
+ * - Timeline of execution
12
+ * - Responsive design
13
+ */
14
+
15
+ interface HtmlReportData {
16
+ session: Session;
17
+ result: RunResult;
18
+ generatedAt: string;
19
+ engineVersion: string;
20
+ }
21
+ declare function generateHtml(data: HtmlReportData): string;
22
+
23
+ /**
24
+ * JSON Report Generator for Vulcn
25
+ *
26
+ * Produces a structured, machine-readable JSON report.
27
+ */
28
+
29
+ interface JsonReport {
30
+ vulcn: {
31
+ version: string;
32
+ reportVersion: string;
33
+ generatedAt: string;
34
+ };
35
+ session: {
36
+ name: string;
37
+ driver: string;
38
+ driverConfig: Record<string, unknown>;
39
+ stepsCount: number;
40
+ metadata?: Record<string, unknown>;
41
+ };
42
+ execution: {
43
+ stepsExecuted: number;
44
+ payloadsTested: number;
45
+ durationMs: number;
46
+ durationFormatted: string;
47
+ errors: string[];
48
+ };
49
+ summary: {
50
+ totalFindings: number;
51
+ riskScore: number;
52
+ severityCounts: Record<string, number>;
53
+ vulnerabilityTypes: string[];
54
+ affectedUrls: string[];
55
+ };
56
+ findings: Finding[];
57
+ }
58
+ declare function generateJson(session: Session, result: RunResult, generatedAt: string, engineVersion: string): JsonReport;
59
+
60
+ /**
61
+ * YAML Report Generator for Vulcn
62
+ *
63
+ * Produces a human-readable YAML report.
64
+ */
65
+
66
+ declare function generateYaml(session: Session, result: RunResult, generatedAt: string, engineVersion: string): string;
67
+
68
+ /**
69
+ * @vulcn/plugin-report
70
+ * Report Generation Plugin for Vulcn
71
+ *
72
+ * Generates security reports in HTML, JSON, and YAML formats
73
+ * after a run completes. Features:
74
+ * - Modern dark-themed HTML report with Vulcn branding
75
+ * - Machine-readable JSON for CI/CD integration
76
+ * - Human-readable YAML for documentation
77
+ *
78
+ * Configuration:
79
+ * format: "html" | "json" | "yaml" | "all" (default: "html")
80
+ * outputDir: directory for reports (default: ".")
81
+ * filename: base filename (no extension) (default: "vulcn-report")
82
+ * open: auto-open HTML in browser (default: false)
83
+ */
84
+
85
+ /**
86
+ * Plugin configuration schema
87
+ */
88
+ declare const configSchema: z.ZodObject<{
89
+ /**
90
+ * Report format(s) to generate
91
+ * - "html": Beautiful dark-themed HTML report
92
+ * - "json": Machine-readable structured JSON
93
+ * - "yaml": Human-readable YAML
94
+ * - "all": Generate all three formats
95
+ * @default "html"
96
+ */
97
+ format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "all"]>>;
98
+ /**
99
+ * Output directory for report files
100
+ * @default "."
101
+ */
102
+ outputDir: z.ZodDefault<z.ZodString>;
103
+ /**
104
+ * Base filename (without extension) for the report
105
+ * @default "vulcn-report"
106
+ */
107
+ filename: z.ZodDefault<z.ZodString>;
108
+ /**
109
+ * Auto-open HTML report in default browser after generation
110
+ * @default false
111
+ */
112
+ open: z.ZodDefault<z.ZodBoolean>;
113
+ }, "strip", z.ZodTypeAny, {
114
+ format: "html" | "json" | "yaml" | "all";
115
+ outputDir: string;
116
+ filename: string;
117
+ open: boolean;
118
+ }, {
119
+ format?: "html" | "json" | "yaml" | "all" | undefined;
120
+ outputDir?: string | undefined;
121
+ filename?: string | undefined;
122
+ open?: boolean | undefined;
123
+ }>;
124
+ type ReportConfig = z.infer<typeof configSchema>;
125
+ /**
126
+ * Report Plugin
127
+ */
128
+ declare const plugin: VulcnPlugin;
129
+
130
+ export { type HtmlReportData, type JsonReport, type ReportConfig, configSchema, plugin as default, generateHtml, generateJson, generateYaml };