@vulcn/engine 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/driver-manager.ts","../src/driver-types.ts","../src/plugin-manager.ts","../src/plugin-types.ts","../src/session.ts","../src/browser.ts","../src/recorder.ts","../src/runner.ts"],"sourcesContent":["/**\n * @vulcn/engine - Core security testing engine\n *\n * v0.3.0: Driver-based architecture\n *\n * The engine now provides:\n * - Driver system for different recording targets (browser, api, cli)\n * - Plugin system for payloads and detection\n * - Generic session format\n *\n * Drivers handle:\n * - Recording interactions (RecorderDriver)\n * - Replaying with payload injection (RunnerDriver)\n *\n * Plugins handle:\n * - Payload loading (builtin, payloadbox, custom files)\n * - Vulnerability detection (reflection, execution, etc.)\n * - Reporting (JSON, SARIF, HTML)\n */\n\n// ============================================================================\n// Driver System (NEW in v0.3.0)\n// ============================================================================\n\nexport { DriverManager, driverManager } from \"./driver-manager\";\nexport { DRIVER_API_VERSION } from \"./driver-types\";\nexport type {\n VulcnDriver,\n RecorderDriver,\n RunnerDriver,\n RecordingHandle,\n RecordOptions,\n RunOptions,\n RunResult,\n RunContext,\n Session,\n Step,\n DriverLogger,\n LoadedDriver,\n DriverSource,\n} from \"./driver-types\";\n\n// ============================================================================\n// Plugin System\n// ============================================================================\n\nexport { PluginManager, pluginManager } from \"./plugin-manager\";\nexport { PLUGIN_API_VERSION } from \"./plugin-types\";\nexport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n PluginHooks,\n PluginContext,\n RecordContext,\n DetectContext,\n LoadedPlugin as LoadedPluginInfo,\n PluginLogger,\n EngineInfo,\n PluginSource,\n} from \"./plugin-types\";\n\n// ============================================================================\n// Payload Types\n// ============================================================================\n\nexport type {\n PayloadCategory,\n PayloadSource,\n RuntimePayload,\n CustomPayload,\n CustomPayloadFile,\n} from \"./payload-types\";\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\nexport type {\n BrowserType,\n RecorderOptions,\n RunnerOptions,\n Finding,\n} from \"./types\";\n\n// ============================================================================\n// Legacy Exports (Deprecated - will be removed in v1.0)\n// These are browser-specific and should be imported from @vulcn/driver-browser\n// ============================================================================\n\n/** @deprecated Import from @vulcn/driver-browser instead */\nexport { Recorder, type RecordingSession } from \"./recorder\";\n\n/** @deprecated Import from @vulcn/driver-browser instead */\nexport { Runner } from \"./runner\";\n\n/** @deprecated Import from @vulcn/driver-browser instead */\nexport {\n createSession,\n parseSession,\n serializeSession,\n SessionSchema,\n StepSchema,\n} from \"./session\";\nexport type { Session as LegacySession, Step as LegacyStep } from \"./session\";\n\n/** @deprecated Import from @vulcn/driver-browser instead */\nexport {\n launchBrowser,\n installBrowsers,\n checkBrowsers,\n BrowserNotFoundError,\n type LaunchOptions,\n type BrowserLaunchResult,\n} from \"./browser\";\n","/**\n * Vulcn Driver Manager\n *\n * Handles driver loading, registration, and lifecycle.\n * Drivers are loaded from npm packages or local files.\n */\n\nimport { isAbsolute, resolve } from \"node:path\";\nimport type {\n VulcnDriver,\n LoadedDriver,\n DriverSource,\n Session,\n RunContext,\n RunResult,\n RunOptions,\n RecordOptions,\n RecordingHandle,\n DriverLogger,\n DRIVER_API_VERSION,\n} from \"./driver-types\";\nimport type { PluginManager } from \"./plugin-manager\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\n\n/**\n * Driver Manager - loads and manages recording/running drivers\n */\nexport class DriverManager {\n private drivers: Map<string, LoadedDriver> = new Map();\n private defaultDriver: string | null = null;\n\n /**\n * Register a driver\n */\n register(driver: VulcnDriver, source: DriverSource = \"builtin\"): void {\n this.validateDriver(driver);\n this.drivers.set(driver.name, { driver, source });\n\n // First registered driver becomes default\n if (this.drivers.size === 1) {\n this.defaultDriver = driver.name;\n }\n }\n\n /**\n * Load a driver from npm or local path\n */\n async load(nameOrPath: string): Promise<void> {\n let driver: VulcnDriver;\n let source: DriverSource;\n\n if (\n nameOrPath.startsWith(\"./\") ||\n nameOrPath.startsWith(\"../\") ||\n isAbsolute(nameOrPath)\n ) {\n // Local file\n const resolved = isAbsolute(nameOrPath)\n ? nameOrPath\n : resolve(process.cwd(), nameOrPath);\n const module = await import(resolved);\n driver = module.default || module;\n source = \"local\";\n } else {\n // npm package\n const module = await import(nameOrPath);\n driver = module.default || module;\n source = \"npm\";\n }\n\n this.register(driver, source);\n }\n\n /**\n * Get a loaded driver by name\n */\n get(name: string): VulcnDriver | undefined {\n return this.drivers.get(name)?.driver;\n }\n\n /**\n * Get the default driver\n */\n getDefault(): VulcnDriver | undefined {\n if (!this.defaultDriver) return undefined;\n return this.get(this.defaultDriver);\n }\n\n /**\n * Set the default driver\n */\n setDefault(name: string): void {\n if (!this.drivers.has(name)) {\n throw new Error(`Driver \"${name}\" is not registered`);\n }\n this.defaultDriver = name;\n }\n\n /**\n * Check if a driver is registered\n */\n has(name: string): boolean {\n return this.drivers.has(name);\n }\n\n /**\n * Get all registered drivers\n */\n list(): LoadedDriver[] {\n return Array.from(this.drivers.values());\n }\n\n /**\n * Get driver for a session\n */\n getForSession(session: Session): VulcnDriver {\n const driverName = session.driver;\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(\n `Driver \"${driverName}\" not found. Install @vulcn/driver-${driverName} or load it manually.`,\n );\n }\n\n return driver;\n }\n\n /**\n * Start recording with a driver\n */\n async startRecording(\n driverName: string,\n config: Record<string, unknown>,\n options: RecordOptions = {},\n ): Promise<RecordingHandle> {\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(`Driver \"${driverName}\" not found`);\n }\n\n return driver.recorder.start(config, options);\n }\n\n /**\n * Execute a session\n */\n async execute(\n session: Session,\n pluginManager: PluginManager,\n options: RunOptions = {},\n ): Promise<RunResult> {\n const driver = this.getForSession(session);\n const findings: Finding[] = [];\n const logger = this.createLogger(driver.name);\n\n const ctx: RunContext = {\n session,\n pluginManager,\n payloads: pluginManager.getPayloads(),\n findings,\n addFinding: (finding) => {\n findings.push(finding);\n pluginManager.addFinding(finding);\n options.onFinding?.(finding);\n },\n logger,\n options,\n };\n\n return driver.runner.execute(session, ctx);\n }\n\n /**\n * Validate driver structure\n */\n private validateDriver(driver: unknown): asserts driver is VulcnDriver {\n if (!driver || typeof driver !== \"object\") {\n throw new Error(\"Driver must be an object\");\n }\n\n const d = driver as Record<string, unknown>;\n\n if (typeof d.name !== \"string\" || !d.name) {\n throw new Error(\"Driver must have a name\");\n }\n\n if (typeof d.version !== \"string\" || !d.version) {\n throw new Error(\"Driver must have a version\");\n }\n\n if (!Array.isArray(d.stepTypes) || d.stepTypes.length === 0) {\n throw new Error(\"Driver must define stepTypes\");\n }\n\n if (!d.recorder || typeof d.recorder !== \"object\") {\n throw new Error(\"Driver must have a recorder\");\n }\n\n if (!d.runner || typeof d.runner !== \"object\") {\n throw new Error(\"Driver must have a runner\");\n }\n }\n\n /**\n * Create a scoped logger for a driver\n */\n private createLogger(name: string): DriverLogger {\n const prefix = `[driver:${name}]`;\n return {\n debug: (msg, ...args) => console.debug(prefix, msg, ...args),\n info: (msg, ...args) => console.info(prefix, msg, ...args),\n warn: (msg, ...args) => console.warn(prefix, msg, ...args),\n error: (msg, ...args) => console.error(prefix, msg, ...args),\n };\n }\n}\n\n/**\n * Default driver manager instance\n */\nexport const driverManager = new DriverManager();\n","/**\n * Vulcn Driver System\n *\n * Drivers handle recording and running sessions for different targets:\n * - browser: Web applications (Playwright)\n * - api: REST/HTTP APIs\n * - cli: Command-line tools\n *\n * Each driver implements RecorderDriver and RunnerDriver interfaces.\n */\n\nimport type { z } from \"zod\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\nimport type { PluginManager } from \"./plugin-manager\";\n\n/**\n * Current driver API version\n */\nexport const DRIVER_API_VERSION = 1;\n\n/**\n * Generic step - drivers define their own step types\n */\nexport interface Step {\n /** Unique step ID */\n id: string;\n\n /** Step type (namespaced, e.g., \"browser.click\", \"api.request\") */\n type: string;\n\n /** Timestamp when step was recorded */\n timestamp: number;\n\n /** Step-specific data */\n [key: string]: unknown;\n}\n\n/**\n * Generic session format\n */\nexport interface Session {\n /** Session name */\n name: string;\n\n /** Driver that recorded this session */\n driver: string;\n\n /** Driver-specific configuration */\n driverConfig: Record<string, unknown>;\n\n /** Recorded steps */\n steps: Step[];\n\n /** Session metadata */\n metadata?: {\n recordedAt?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Recording context passed to drivers\n */\nexport interface RecordContext {\n /** Session being built */\n session: Partial<Session>;\n\n /** Add a step to the session */\n addStep(step: Omit<Step, \"id\" | \"timestamp\">): void;\n\n /** Logger */\n logger: DriverLogger;\n}\n\n/**\n * Running context passed to drivers\n */\nexport interface RunContext {\n /** Session being executed */\n session: Session;\n\n /** Plugin manager for calling hooks */\n pluginManager: PluginManager;\n\n /** Available payloads */\n payloads: RuntimePayload[];\n\n /** Collected findings */\n findings: Finding[];\n\n /** Add a finding */\n addFinding(finding: Finding): void;\n\n /** Logger */\n logger: DriverLogger;\n\n /** Running options */\n options: RunOptions;\n}\n\n/**\n * Options for recording\n */\nexport interface RecordOptions {\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Options for running\n */\nexport interface RunOptions {\n /** Run headless (for visual drivers) */\n headless?: boolean;\n\n /** Callback for findings */\n onFinding?: (finding: Finding) => void;\n\n /** Callback for step completion */\n onStepComplete?: (stepId: string, payloadCount: number) => void;\n\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Run result\n */\nexport interface RunResult {\n /** All findings */\n findings: Finding[];\n\n /** Steps executed */\n stepsExecuted: number;\n\n /** Payloads tested */\n payloadsTested: number;\n\n /** Duration in milliseconds */\n duration: number;\n\n /** Errors encountered */\n errors: string[];\n}\n\n/**\n * Driver logger\n */\nexport interface DriverLogger {\n debug(msg: string, ...args: unknown[]): void;\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n}\n\n/**\n * Recorder Driver Interface\n *\n * Implement this to add recording support for a target type.\n */\nexport interface RecorderDriver {\n /** Start recording and return control handle */\n start(\n config: Record<string, unknown>,\n options: RecordOptions,\n ): Promise<RecordingHandle>;\n}\n\n/**\n * Handle returned by RecorderDriver.start()\n */\nexport interface RecordingHandle {\n /** Stop recording and return the session */\n stop(): Promise<Session>;\n\n /** Abort recording without saving */\n abort(): Promise<void>;\n\n /** Get current steps (during recording) */\n getSteps(): Step[];\n\n /** Manually add a step */\n addStep(step: Omit<Step, \"id\" | \"timestamp\">): void;\n}\n\n/**\n * Runner Driver Interface\n *\n * Implement this to add running/replay support for a target type.\n */\nexport interface RunnerDriver {\n /** Execute a session with payloads */\n execute(session: Session, ctx: RunContext): Promise<RunResult>;\n}\n\n/**\n * Complete driver definition\n */\nexport interface VulcnDriver {\n /** Unique driver name (e.g., \"browser\", \"api\", \"cli\") */\n name: string;\n\n /** Driver version */\n version: string;\n\n /** Driver API version */\n apiVersion?: number;\n\n /** Human-readable description */\n description?: string;\n\n /** Configuration schema (Zod) */\n configSchema?: z.ZodSchema;\n\n /** Step types this driver handles */\n stepTypes: string[];\n\n /** Recorder implementation */\n recorder: RecorderDriver;\n\n /** Runner implementation */\n runner: RunnerDriver;\n}\n\n/**\n * Driver source for loading\n */\nexport type DriverSource = \"npm\" | \"local\" | \"builtin\";\n\n/**\n * Loaded driver with metadata\n */\nexport interface LoadedDriver {\n driver: VulcnDriver;\n source: DriverSource;\n}\n","/**\n * Vulcn Plugin Manager\n * Handles plugin loading, lifecycle, and hook execution\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { resolve, isAbsolute } from \"node:path\";\nimport YAML from \"yaml\";\nimport { z } from \"zod\";\nimport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n LoadedPlugin,\n PluginContext,\n PluginSource,\n PluginLogger,\n EngineInfo,\n PluginHooks,\n} from \"./plugin-types\";\nimport { PLUGIN_API_VERSION } from \"./plugin-types\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\n\n// Package version (injected at build time or read from package.json)\nconst ENGINE_VERSION = \"0.2.0\";\n\n/**\n * Config file schema\n */\nconst VulcnConfigSchema = z.object({\n version: z.string().default(\"1\"),\n plugins: z\n .array(\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n enabled: z.boolean().default(true),\n }),\n )\n .optional(),\n settings: z\n .object({\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).optional(),\n headless: z.boolean().optional(),\n timeout: z.number().optional(),\n })\n .optional(),\n});\n\n/**\n * Plugin Manager - loads, configures, and orchestrates plugins\n */\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private config: VulcnConfig | null = null;\n private initialized = false;\n\n /**\n * Shared context passed to all plugins\n */\n private sharedPayloads: RuntimePayload[] = [];\n private sharedFindings: Finding[] = [];\n\n /**\n * Load configuration from vulcn.config.yml\n */\n async loadConfig(configPath?: string): Promise<VulcnConfig> {\n const paths = configPath\n ? [configPath]\n : [\n \"vulcn.config.yml\",\n \"vulcn.config.yaml\",\n \"vulcn.config.json\",\n \".vulcnrc.yml\",\n \".vulcnrc.yaml\",\n \".vulcnrc.json\",\n ];\n\n for (const path of paths) {\n const resolved = isAbsolute(path) ? path : resolve(process.cwd(), path);\n if (existsSync(resolved)) {\n const content = await readFile(resolved, \"utf-8\");\n const parsed = path.endsWith(\".json\")\n ? JSON.parse(content)\n : YAML.parse(content);\n this.config = VulcnConfigSchema.parse(parsed);\n return this.config;\n }\n }\n\n // No config file - use defaults\n this.config = { version: \"1\", plugins: [], settings: {} };\n return this.config;\n }\n\n /**\n * Load all plugins from config\n */\n async loadPlugins(): Promise<void> {\n if (!this.config) {\n await this.loadConfig();\n }\n\n const pluginConfigs = this.config?.plugins || [];\n\n for (const pluginConfig of pluginConfigs) {\n if (pluginConfig.enabled === false) continue;\n\n try {\n const loaded = await this.loadPlugin(pluginConfig);\n this.plugins.push(loaded);\n } catch (err) {\n console.error(\n `Failed to load plugin ${pluginConfig.name}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n /**\n * Load a single plugin\n */\n private async loadPlugin(config: PluginConfig): Promise<LoadedPlugin> {\n const { name, config: pluginConfig = {} } = config;\n let plugin: VulcnPlugin;\n let source: PluginSource;\n\n // Determine plugin source and load\n if (name.startsWith(\"./\") || name.startsWith(\"../\") || isAbsolute(name)) {\n // Local file plugin\n const resolved = isAbsolute(name) ? name : resolve(process.cwd(), name);\n const module = await import(resolved);\n plugin = module.default || module;\n source = \"local\";\n } else if (name.startsWith(\"@vulcn/\")) {\n // Official plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n } else {\n // Community plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n }\n\n // Validate plugin structure\n this.validatePlugin(plugin);\n\n // Validate plugin config if schema provided\n let resolvedConfig = pluginConfig;\n if (plugin.configSchema) {\n try {\n resolvedConfig = plugin.configSchema.parse(pluginConfig);\n } catch (err) {\n throw new Error(\n `Invalid config for plugin ${name}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n return {\n plugin,\n config: resolvedConfig,\n source,\n enabled: true,\n };\n }\n\n /**\n * Validate plugin structure\n */\n private validatePlugin(plugin: unknown): asserts plugin is VulcnPlugin {\n if (!plugin || typeof plugin !== \"object\") {\n throw new Error(\"Plugin must be an object\");\n }\n\n const p = plugin as Record<string, unknown>;\n if (typeof p.name !== \"string\" || !p.name) {\n throw new Error(\"Plugin must have a name\");\n }\n if (typeof p.version !== \"string\" || !p.version) {\n throw new Error(\"Plugin must have a version\");\n }\n\n // Check API version compatibility\n const apiVersion = (p.apiVersion as number) || 1;\n if (apiVersion > PLUGIN_API_VERSION) {\n throw new Error(\n `Plugin requires API version ${apiVersion}, but engine supports ${PLUGIN_API_VERSION}`,\n );\n }\n }\n\n /**\n * Add a plugin programmatically (for testing or dynamic loading)\n */\n addPlugin(plugin: VulcnPlugin, config: Record<string, unknown> = {}): void {\n this.validatePlugin(plugin);\n this.plugins.push({\n plugin,\n config,\n source: \"custom\",\n enabled: true,\n });\n }\n\n /**\n * Initialize all plugins (call onInit hooks)\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Load payloads from plugins that provide them\n for (const loaded of this.plugins) {\n if (loaded.plugin.payloads) {\n const payloads =\n typeof loaded.plugin.payloads === \"function\"\n ? await loaded.plugin.payloads()\n : loaded.plugin.payloads;\n this.sharedPayloads.push(...payloads);\n }\n }\n\n // Call onInit hooks\n await this.callHook(\"onInit\", (hook, ctx) => hook(ctx));\n\n this.initialized = true;\n }\n\n /**\n * Destroy all plugins (call onDestroy hooks)\n */\n async destroy(): Promise<void> {\n await this.callHook(\"onDestroy\", (hook, ctx) => hook(ctx));\n this.plugins = [];\n this.sharedPayloads = [];\n this.sharedFindings = [];\n this.initialized = false;\n }\n\n /**\n * Get all loaded payloads\n */\n getPayloads(): RuntimePayload[] {\n return this.sharedPayloads;\n }\n\n /**\n * Get all collected findings\n */\n getFindings(): Finding[] {\n return this.sharedFindings;\n }\n\n /**\n * Add a finding (used by detectors)\n */\n addFinding(finding: Finding): void {\n this.sharedFindings.push(finding);\n }\n\n /**\n * Add payloads (used by loaders)\n */\n addPayloads(payloads: RuntimePayload[]): void {\n this.sharedPayloads.push(...payloads);\n }\n\n /**\n * Clear findings (for new run)\n */\n clearFindings(): void {\n this.sharedFindings = [];\n }\n\n /**\n * Get loaded plugins\n */\n getPlugins(): LoadedPlugin[] {\n return this.plugins;\n }\n\n /**\n * Check if a plugin is loaded by name\n */\n hasPlugin(name: string): boolean {\n return this.plugins.some((p) => p.plugin.name === name);\n }\n\n /**\n * Create base context for plugins\n */\n createContext(pluginConfig: Record<string, unknown>): PluginContext {\n const engineInfo: EngineInfo = {\n version: ENGINE_VERSION,\n pluginApiVersion: PLUGIN_API_VERSION,\n };\n\n return {\n config: pluginConfig,\n engine: engineInfo,\n payloads: this.sharedPayloads,\n findings: this.sharedFindings,\n logger: this.createLogger(\"plugin\"),\n fetch: globalThis.fetch,\n };\n }\n\n /**\n * Create scoped logger for a plugin\n */\n private createLogger(name: string): PluginLogger {\n const prefix = `[${name}]`;\n return {\n debug: (msg, ...args) => console.debug(prefix, msg, ...args),\n info: (msg, ...args) => console.info(prefix, msg, ...args),\n warn: (msg, ...args) => console.warn(prefix, msg, ...args),\n error: (msg, ...args) => console.error(prefix, msg, ...args),\n };\n }\n\n /**\n * Call a hook on all plugins sequentially\n */\n async callHook<K extends keyof PluginHooks>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<unknown>,\n ): Promise<void> {\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n await executor(hook as NonNullable<PluginHooks[K]>, ctx);\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n }\n\n /**\n * Call a hook and collect results\n */\n async callHookCollect<K extends keyof PluginHooks, R>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<R | R[] | null>,\n ): Promise<R[]> {\n const results: R[] = [];\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n const result = await executor(\n hook as NonNullable<PluginHooks[K]>,\n ctx,\n );\n if (result !== null && result !== undefined) {\n if (Array.isArray(result)) {\n results.push(...result);\n } else {\n results.push(result);\n }\n }\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return results;\n }\n\n /**\n * Call a hook that transforms a value through the pipeline\n */\n async callHookPipe<T>(\n hookName: keyof PluginHooks,\n initial: T,\n executor: (\n hook: NonNullable<PluginHooks[typeof hookName]>,\n value: T,\n ctx: PluginContext,\n ) => Promise<T>,\n ): Promise<T> {\n let value = initial;\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n value = await executor(\n hook as NonNullable<PluginHooks[typeof hookName]>,\n value,\n ctx,\n );\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return value;\n }\n}\n\n/**\n * Default shared plugin manager instance\n */\nexport const pluginManager = new PluginManager();\n","/**\n * Vulcn Plugin System Types\n * @module @vulcn/engine/plugin\n */\n\nimport type {\n Page,\n Dialog,\n ConsoleMessage,\n Request,\n Response,\n} from \"playwright\";\nimport type { z } from \"zod\";\nimport type { Session, Step } from \"./session\";\nimport type { Finding, RunResult, BrowserType } from \"./types\";\nimport type { RuntimePayload, PayloadCategory } from \"./payload-types\";\n\n// Re-export for plugin authors\nexport type {\n Session,\n Step,\n Finding,\n RunResult,\n RuntimePayload,\n PayloadCategory,\n};\n\n/**\n * Plugin API version - plugins declare compatibility\n */\nexport const PLUGIN_API_VERSION = 1;\n\n/**\n * Plugin source types for identification\n */\nexport type PluginSource = \"builtin\" | \"npm\" | \"local\" | \"custom\";\n\n/**\n * Main plugin interface\n */\nexport interface VulcnPlugin {\n /** Unique plugin name (e.g., \"@vulcn/plugin-payloads\") */\n name: string;\n\n /** Plugin version (semver) */\n version: string;\n\n /** Plugin API version this plugin targets */\n apiVersion?: number;\n\n /** Human-readable description */\n description?: string;\n\n /** Lifecycle hooks */\n hooks?: PluginHooks;\n\n /**\n * Payloads provided by this plugin (Loaders)\n * Can be static array or async function for lazy loading\n */\n payloads?: RuntimePayload[] | (() => Promise<RuntimePayload[]>);\n\n /**\n * Zod schema for plugin configuration validation\n */\n configSchema?: z.ZodSchema;\n}\n\n/**\n * Plugin lifecycle hooks\n */\nexport interface PluginHooks {\n // ─────────────────────────────────────────────────────────────────\n // Initialization\n // ─────────────────────────────────────────────────────────────────\n\n /**\n * Called when plugin is loaded, before any operation\n * Use for setup, loading payloads, etc.\n */\n onInit?: (ctx: PluginContext) => Promise<void>;\n\n /**\n * Called when plugin is unloaded/cleanup\n */\n onDestroy?: (ctx: PluginContext) => Promise<void>;\n\n // ─────────────────────────────────────────────────────────────────\n // Recording Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when recording starts */\n onRecordStart?: (ctx: RecordContext) => Promise<void>;\n\n /** Called for each recorded step, can transform */\n onRecordStep?: (step: Step, ctx: RecordContext) => Promise<Step>;\n\n /** Called when recording ends, can transform session */\n onRecordEnd?: (session: Session, ctx: RecordContext) => Promise<Session>;\n\n // ─────────────────────────────────────────────────────────────────\n // Running Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when run starts */\n onRunStart?: (ctx: RunContext) => Promise<void>;\n\n /** Called before each payload is injected, can transform payload */\n onBeforePayload?: (\n payload: string,\n step: Step,\n ctx: RunContext,\n ) => Promise<string>;\n\n /** Called after payload injection, for detection */\n onAfterPayload?: (ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called when run ends, can transform results */\n onRunEnd?: (result: RunResult, ctx: RunContext) => Promise<RunResult>;\n\n // ─────────────────────────────────────────────────────────────────\n // Browser Event Hooks (Detection)\n // ─────────────────────────────────────────────────────────────────\n\n /** Called on page load/navigation */\n onPageLoad?: (page: Page, ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called when JavaScript alert/confirm/prompt appears */\n onDialog?: (dialog: Dialog, ctx: DetectContext) => Promise<Finding | null>;\n\n /** Called on console.log/warn/error */\n onConsoleMessage?: (\n msg: ConsoleMessage,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on network request */\n onNetworkRequest?: (\n request: Request,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on network response */\n onNetworkResponse?: (\n response: Response,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n}\n\n/**\n * Logger interface for plugins\n */\nexport interface PluginLogger {\n debug: (msg: string, ...args: unknown[]) => void;\n info: (msg: string, ...args: unknown[]) => void;\n warn: (msg: string, ...args: unknown[]) => void;\n error: (msg: string, ...args: unknown[]) => void;\n}\n\n/**\n * Engine information exposed to plugins\n */\nexport interface EngineInfo {\n version: string;\n pluginApiVersion: number;\n}\n\n/**\n * Base context available to all plugin hooks\n */\nexport interface PluginContext {\n /** Plugin-specific config from vulcn.config.yml */\n config: Record<string, unknown>;\n\n /** Engine information */\n engine: EngineInfo;\n\n /** Shared payload registry - loaders add payloads here */\n payloads: RuntimePayload[];\n\n /** Shared findings collection - detectors add findings here */\n findings: Finding[];\n\n /** Scoped logger */\n logger: PluginLogger;\n\n /** Fetch API for network requests */\n fetch: typeof fetch;\n}\n\n/**\n * Context for recording phase hooks\n */\nexport interface RecordContext extends PluginContext {\n /** Starting URL */\n startUrl: string;\n\n /** Browser type being used */\n browser: BrowserType;\n\n /** Playwright page instance */\n page: Page;\n}\n\n/**\n * Context for running phase hooks\n */\nexport interface RunContext extends PluginContext {\n /** Session being executed */\n session: Session;\n\n /** Playwright page instance */\n page: Page;\n\n /** Browser type being used */\n browser: BrowserType;\n\n /** Whether running headless */\n headless: boolean;\n}\n\n/**\n * Context for detection hooks\n */\nexport interface DetectContext extends RunContext {\n /** Current step being tested */\n step: Step;\n\n /** Current payload set being tested */\n payloadSet: RuntimePayload;\n\n /** Actual payload value injected */\n payloadValue: string;\n\n /** Step ID for reporting */\n stepId: string;\n}\n\n/**\n * Plugin configuration in vulcn.config.yml\n */\nexport interface PluginConfig {\n /** Plugin name/path */\n name: string;\n\n /** Plugin-specific configuration */\n config?: Record<string, unknown>;\n\n /** Whether plugin is enabled (default: true) */\n enabled?: boolean;\n}\n\n/**\n * Vulcn configuration file schema\n */\nexport interface VulcnConfig {\n /** Config version */\n version: string;\n\n /** Plugins to load */\n plugins?: PluginConfig[];\n\n /** Global settings */\n settings?: {\n browser?: BrowserType;\n headless?: boolean;\n timeout?: number;\n };\n}\n\n/**\n * Loaded plugin instance with resolved config\n */\nexport interface LoadedPlugin {\n /** Plugin definition */\n plugin: VulcnPlugin;\n\n /** Resolved configuration */\n config: Record<string, unknown>;\n\n /** Source of the plugin */\n source: PluginSource;\n\n /** Whether plugin is enabled */\n enabled: boolean;\n}\n","import { z } from \"zod\";\nimport { parse, stringify } from \"yaml\";\n\n// Step types\nexport const StepSchema = z.discriminatedUnion(\"type\", [\n z.object({\n id: z.string(),\n type: z.literal(\"navigate\"),\n url: z.string(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"click\"),\n selector: z.string(),\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"input\"),\n selector: z.string(),\n value: z.string(),\n injectable: z.boolean().optional().default(true),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"keypress\"),\n key: z.string(),\n modifiers: z.array(z.string()).optional(),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"scroll\"),\n selector: z.string().optional(),\n position: z.object({ x: z.number(), y: z.number() }),\n timestamp: z.number(),\n }),\n z.object({\n id: z.string(),\n type: z.literal(\"wait\"),\n duration: z.number(),\n timestamp: z.number(),\n }),\n]);\n\nexport type Step = z.infer<typeof StepSchema>;\n\n// Session schema\nexport const SessionSchema = z.object({\n version: z.string().default(\"1\"),\n name: z.string(),\n recordedAt: z.string(),\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).default(\"chromium\"),\n viewport: z.object({\n width: z.number(),\n height: z.number(),\n }),\n startUrl: z.string(),\n steps: z.array(StepSchema),\n});\n\nexport type Session = z.infer<typeof SessionSchema>;\n\n/**\n * Create a new session object\n */\nexport function createSession(options: {\n name: string;\n startUrl: string;\n browser?: \"chromium\" | \"firefox\" | \"webkit\";\n viewport?: { width: number; height: number };\n}): Session {\n return {\n version: \"1\",\n name: options.name,\n recordedAt: new Date().toISOString(),\n browser: options.browser ?? \"chromium\",\n viewport: options.viewport ?? { width: 1280, height: 720 },\n startUrl: options.startUrl,\n steps: [],\n };\n}\n\n/**\n * Parse a session from YAML string\n */\nexport function parseSession(yaml: string): Session {\n const data = parse(yaml);\n return SessionSchema.parse(data);\n}\n\n/**\n * Serialize a session to YAML string\n */\nexport function serializeSession(session: Session): string {\n return stringify(session, { lineWidth: 0 });\n}\n","import { chromium, firefox, webkit, type Browser } from \"playwright\";\nimport { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport type { BrowserType } from \"./types\";\n\nconst execAsync = promisify(exec);\n\nexport interface LaunchOptions {\n browser?: BrowserType;\n headless?: boolean;\n}\n\nexport interface BrowserLaunchResult {\n browser: Browser;\n channel?: string;\n}\n\nexport class BrowserNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"BrowserNotFoundError\";\n }\n}\n\n/**\n * Launch a browser with smart fallback:\n * 1. Try system Chrome/Edge first (zero-install experience)\n * 2. Fall back to Playwright's bundled browsers\n */\nexport async function launchBrowser(\n options: LaunchOptions = {},\n): Promise<BrowserLaunchResult> {\n const browserType = options.browser ?? \"chromium\";\n const headless = options.headless ?? false;\n\n // For Chromium, try system browsers first\n if (browserType === \"chromium\") {\n // Try system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless,\n });\n return { browser, channel: \"chrome\" };\n } catch {\n // Chrome not available\n }\n\n // Try system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless,\n });\n return { browser, channel: \"msedge\" };\n } catch {\n // Edge not available\n }\n\n // Fall back to Playwright's bundled Chromium\n try {\n const browser = await chromium.launch({ headless });\n return { browser, channel: \"chromium\" };\n } catch {\n throw new BrowserNotFoundError(\n \"No Chromium browser found. Install Chrome or run: vulcn install chromium\",\n );\n }\n }\n\n // Firefox\n if (browserType === \"firefox\") {\n try {\n const browser = await firefox.launch({ headless });\n return { browser, channel: \"firefox\" };\n } catch {\n throw new BrowserNotFoundError(\n \"Firefox not found. Run: vulcn install firefox\",\n );\n }\n }\n\n // WebKit\n if (browserType === \"webkit\") {\n try {\n const browser = await webkit.launch({ headless });\n return { browser, channel: \"webkit\" };\n } catch {\n throw new BrowserNotFoundError(\n \"WebKit not found. Run: vulcn install webkit\",\n );\n }\n }\n\n throw new BrowserNotFoundError(`Unknown browser type: ${browserType}`);\n}\n\n/**\n * Install Playwright browsers\n */\nexport async function installBrowsers(\n browsers: BrowserType[] = [\"chromium\"],\n): Promise<void> {\n const browserArg = browsers.join(\" \");\n await execAsync(`npx playwright install ${browserArg}`);\n}\n\n/**\n * Check which browsers are available\n */\nexport async function checkBrowsers(): Promise<{\n systemChrome: boolean;\n systemEdge: boolean;\n playwrightChromium: boolean;\n playwrightFirefox: boolean;\n playwrightWebkit: boolean;\n}> {\n const results = {\n systemChrome: false,\n systemEdge: false,\n playwrightChromium: false,\n playwrightFirefox: false,\n playwrightWebkit: false,\n };\n\n // Check system Chrome\n try {\n const browser = await chromium.launch({\n channel: \"chrome\",\n headless: true,\n });\n await browser.close();\n results.systemChrome = true;\n } catch {\n // Not available\n }\n\n // Check system Edge\n try {\n const browser = await chromium.launch({\n channel: \"msedge\",\n headless: true,\n });\n await browser.close();\n results.systemEdge = true;\n } catch {\n // Not available\n }\n\n // Check Playwright Chromium\n try {\n const browser = await chromium.launch({ headless: true });\n await browser.close();\n results.playwrightChromium = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright Firefox\n try {\n const browser = await firefox.launch({ headless: true });\n await browser.close();\n results.playwrightFirefox = true;\n } catch {\n // Not installed\n }\n\n // Check Playwright WebKit\n try {\n const browser = await webkit.launch({ headless: true });\n await browser.close();\n results.playwrightWebkit = true;\n } catch {\n // Not installed\n }\n\n return results;\n}\n","/**\n * Recorder - captures browser interactions as a replayable session\n * v0.2.0: Plugin hooks for recording customization\n */\n\nimport { Page } from \"playwright\";\nimport { createSession, type Session, type Step } from \"./session\";\nimport { launchBrowser } from \"./browser\";\nimport type { RecorderOptions } from \"./types\";\nimport { PluginManager, pluginManager } from \"./plugin-manager\";\nimport type { RecordContext } from \"./plugin-types\";\n\n/**\n * Configuration for the recorder\n */\nexport interface RecorderConfig {\n /** Plugin manager to use (defaults to shared instance) */\n pluginManager?: PluginManager;\n}\n\n/**\n * Active recording session handle\n */\nexport interface RecordingSession {\n /** Stop recording and return the session */\n stop(): Promise<Session>;\n /** Get current recorded steps */\n getSteps(): Step[];\n /** Get the Playwright page (for advanced use) */\n getPage(): Page;\n}\n\n/**\n * Recorder - captures browser interactions as a replayable session\n *\n * Uses plugin hooks for:\n * - onRecordStart: Called when recording starts\n * - onRecordStep: Called for each step, can transform\n * - onRecordEnd: Called when recording ends, can transform session\n */\nexport class Recorder {\n /**\n * Start a new recording session\n * Opens a browser window for the user to interact with\n */\n static async start(\n startUrl: string,\n options: RecorderOptions = {},\n config: RecorderConfig = {},\n ): Promise<RecordingSession> {\n const manager = config.pluginManager ?? pluginManager;\n const browserType = options.browser ?? \"chromium\";\n const viewport = options.viewport ?? { width: 1280, height: 720 };\n const headless = options.headless ?? false;\n\n // Initialize plugins if not already done\n await manager.initialize();\n\n // Launch browser with smart fallback (system Chrome first)\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport });\n const page = await context.newPage();\n\n // Navigate to start URL\n await page.goto(startUrl);\n\n // Create session\n const session = createSession({\n name: `Recording ${new Date().toISOString()}`,\n startUrl,\n browser: browserType,\n viewport,\n });\n\n // Track recording start time\n const startTime = Date.now();\n const steps: Step[] = [];\n let stepCounter = 0;\n\n const generateStepId = () => {\n stepCounter++;\n return `step_${String(stepCounter).padStart(3, \"0\")}`;\n };\n\n // Create base record context\n const baseRecordContext: Omit<RecordContext, \"config\"> = {\n startUrl,\n browser: browserType,\n page,\n engine: { version: \"0.2.0\", pluginApiVersion: 1 },\n payloads: manager.getPayloads(),\n findings: manager.getFindings(),\n logger: {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n },\n fetch: globalThis.fetch,\n };\n\n // Call onRecordStart hooks\n await manager.callHook(\"onRecordStart\", async (hook, ctx) => {\n const recordCtx: RecordContext = { ...baseRecordContext, ...ctx };\n await hook(recordCtx);\n });\n\n // Add initial navigation step\n const initialStep: Step = {\n id: generateStepId(),\n type: \"navigate\",\n url: startUrl,\n timestamp: 0,\n };\n\n // Transform through plugins\n const transformedInitialStep = await Recorder.transformStep(\n initialStep,\n manager,\n baseRecordContext,\n );\n if (transformedInitialStep) {\n steps.push(transformedInitialStep);\n }\n\n // Attach event listeners with step transformation\n Recorder.attachListeners(\n page,\n steps,\n startTime,\n generateStepId,\n manager,\n baseRecordContext,\n );\n\n return {\n async stop() {\n // Call onRecordEnd hooks to transform session\n session.steps = steps;\n let finalSession = session;\n\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRecordEnd;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const recordCtx: RecordContext = { ...baseRecordContext, ...ctx };\n finalSession = await hook(finalSession, recordCtx);\n }\n }\n\n await browser.close();\n return finalSession;\n },\n getSteps() {\n return [...steps];\n },\n getPage() {\n return page;\n },\n };\n }\n\n /**\n * Transform a step through plugin hooks\n * Returns null if the step should be filtered out\n */\n private static async transformStep(\n step: Step,\n manager: PluginManager,\n baseContext: Omit<RecordContext, \"config\">,\n ): Promise<Step | null> {\n let transformedStep = step;\n\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRecordStep;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const recordCtx: RecordContext = { ...baseContext, ...ctx };\n transformedStep = await hook(transformedStep, recordCtx);\n }\n }\n\n return transformedStep;\n }\n\n private static attachListeners(\n page: Page,\n steps: Step[],\n startTime: number,\n generateStepId: () => string,\n manager: PluginManager,\n baseContext: Omit<RecordContext, \"config\">,\n ) {\n const getTimestamp = () => Date.now() - startTime;\n\n // Helper to add step with plugin transformation\n const addStep = async (step: Step) => {\n const transformed = await Recorder.transformStep(\n step,\n manager,\n baseContext,\n );\n if (transformed) {\n steps.push(transformed);\n }\n };\n\n // Track navigation\n page.on(\"framenavigated\", (frame) => {\n if (frame === page.mainFrame()) {\n const url = frame.url();\n // Avoid duplicate nav steps for initial load\n const lastStep = steps[steps.length - 1];\n if (\n steps.length > 0 &&\n lastStep.type === \"navigate\" &&\n lastStep.url === url\n ) {\n return;\n }\n addStep({\n id: generateStepId(),\n type: \"navigate\",\n url,\n timestamp: getTimestamp(),\n });\n }\n });\n\n // Expose recording function to browser\n page.exposeFunction(\n \"__vulcn_record\",\n async (event: { type: string; data: Record<string, unknown> }) => {\n const timestamp = getTimestamp();\n\n switch (event.type) {\n case \"click\": {\n const data = event.data as {\n selector: string;\n x: number;\n y: number;\n };\n await addStep({\n id: generateStepId(),\n type: \"click\",\n selector: data.selector,\n position: { x: data.x, y: data.y },\n timestamp,\n });\n break;\n }\n case \"input\": {\n const data = event.data as {\n selector: string;\n value: string;\n inputType: string | null;\n injectable: boolean;\n };\n await addStep({\n id: generateStepId(),\n type: \"input\",\n selector: data.selector,\n value: data.value,\n injectable: data.injectable,\n timestamp,\n });\n break;\n }\n case \"keypress\": {\n const data = event.data as { key: string; modifiers?: string[] };\n await addStep({\n id: generateStepId(),\n type: \"keypress\",\n key: data.key,\n modifiers: data.modifiers,\n timestamp,\n });\n break;\n }\n }\n },\n );\n\n // Inject recording script into every frame\n page.on(\"load\", async () => {\n await Recorder.injectRecordingScript(page);\n });\n\n // Inject into initial page\n Recorder.injectRecordingScript(page);\n }\n\n private static async injectRecordingScript(page: Page) {\n await page.evaluate(`\n (function() {\n if (window.__vulcn_injected) return;\n window.__vulcn_injected = true;\n\n var textInputTypes = ['text', 'password', 'email', 'search', 'url', 'tel', 'number'];\n\n function getSelector(el) {\n if (el.id) {\n return '#' + CSS.escape(el.id);\n }\n if (el.name) {\n var tag = el.tagName.toLowerCase();\n var nameSelector = tag + '[name=\"' + el.name + '\"]';\n if (document.querySelectorAll(nameSelector).length === 1) {\n return nameSelector;\n }\n }\n if (el.dataset && el.dataset.testid) {\n return '[data-testid=\"' + el.dataset.testid + '\"]';\n }\n if (el.tagName === 'INPUT' && el.type && el.name) {\n var inputSelector = 'input[type=\"' + el.type + '\"][name=\"' + el.name + '\"]';\n if (document.querySelectorAll(inputSelector).length === 1) {\n return inputSelector;\n }\n }\n if (el.className && typeof el.className === 'string') {\n var classes = el.className.trim().split(/\\\\s+/).filter(function(c) { return c.length > 0; });\n if (classes.length > 0) {\n var classSelector = el.tagName.toLowerCase() + '.' + classes.map(function(c) { return CSS.escape(c); }).join('.');\n if (document.querySelectorAll(classSelector).length === 1) {\n return classSelector;\n }\n }\n }\n var path = [];\n var current = el;\n while (current && current !== document.body) {\n var tag = current.tagName.toLowerCase();\n var parent = current.parentElement;\n if (parent) {\n var siblings = Array.from(parent.children).filter(function(c) { return c.tagName === current.tagName; });\n if (siblings.length > 1) {\n var index = siblings.indexOf(current) + 1;\n tag = tag + ':nth-of-type(' + index + ')';\n }\n }\n path.unshift(tag);\n current = parent;\n }\n return path.join(' > ');\n }\n\n function getInputType(el) {\n if (el.tagName === 'INPUT') return el.type || 'text';\n if (el.tagName === 'TEXTAREA') return 'textarea';\n if (el.tagName === 'SELECT') return 'select';\n return null;\n }\n\n function isTextInjectable(el) {\n var inputType = getInputType(el);\n if (!inputType) return false;\n if (inputType === 'textarea') return true;\n if (inputType === 'select') return false;\n return textInputTypes.indexOf(inputType) !== -1;\n }\n\n document.addEventListener('click', function(e) {\n var target = e.target;\n window.__vulcn_record({\n type: 'click',\n data: {\n selector: getSelector(target),\n x: e.clientX,\n y: e.clientY\n }\n });\n }, true);\n\n document.addEventListener('change', function(e) {\n var target = e.target;\n if ('value' in target) {\n var inputType = getInputType(target);\n window.__vulcn_record({\n type: 'input',\n data: {\n selector: getSelector(target),\n value: target.value,\n inputType: inputType,\n injectable: isTextInjectable(target)\n }\n });\n }\n }, true);\n\n document.addEventListener('keydown', function(e) {\n if (e.ctrlKey || e.metaKey || e.altKey) {\n var modifiers = [];\n if (e.ctrlKey) modifiers.push('ctrl');\n if (e.metaKey) modifiers.push('meta');\n if (e.altKey) modifiers.push('alt');\n if (e.shiftKey) modifiers.push('shift');\n\n window.__vulcn_record({\n type: 'keypress',\n data: {\n key: e.key,\n modifiers: modifiers\n }\n });\n }\n }, true);\n })();\n `);\n }\n}\n","/**\n * Runner - replays sessions with security payloads\n * v0.2.0: Plugin-based architecture for extensibility\n */\n\nimport type { Page, Dialog, ConsoleMessage } from \"playwright\";\nimport { launchBrowser } from \"./browser\";\nimport type { RuntimePayload, PayloadCategory } from \"./payload-types\";\nimport type { Session, Step } from \"./session\";\nimport type { Finding, RunResult, RunnerOptions } from \"./types\";\nimport { PluginManager, pluginManager } from \"./plugin-manager\";\nimport type { DetectContext, RunContext } from \"./plugin-types\";\n\nexport interface RunnerConfig {\n /** Plugin manager to use (defaults to shared instance) */\n pluginManager?: PluginManager;\n}\n\n/**\n * Runner - replays sessions with security payloads\n *\n * Uses plugin hooks for:\n * - Payload loading (onInit)\n * - Payload transformation (onBeforePayload)\n * - Vulnerability detection (onAfterPayload, onDialog, onConsoleMessage, etc.)\n * - Results processing (onRunEnd)\n */\nexport class Runner {\n /**\n * Execute a session with security payloads from plugins\n *\n * @param session - The recorded session to replay\n * @param options - Runner configuration\n * @param config - Plugin manager configuration\n */\n static async execute(\n session: Session,\n options: RunnerOptions = {},\n config: RunnerConfig = {},\n ): Promise<RunResult> {\n const manager = config.pluginManager ?? pluginManager;\n const browserType = options.browser ?? session.browser ?? \"chromium\";\n const headless = options.headless ?? true;\n const startTime = Date.now();\n\n const errors: string[] = [];\n let payloadsTested = 0;\n\n // Initialize plugins and load payloads\n await manager.initialize();\n manager.clearFindings();\n\n const payloads = manager.getPayloads();\n if (payloads.length === 0) {\n return {\n findings: [],\n stepsExecuted: session.steps.length,\n payloadsTested: 0,\n duration: Date.now() - startTime,\n errors: [\n \"No payloads loaded. Add a payload plugin or configure payloads.\",\n ],\n };\n }\n\n // Launch browser\n const { browser } = await launchBrowser({\n browser: browserType,\n headless,\n });\n const context = await browser.newContext({ viewport: session.viewport });\n const page = await context.newPage();\n\n // Create base run context\n const baseRunContext: Omit<RunContext, \"config\"> = {\n session,\n page,\n browser: browserType,\n headless,\n engine: { version: \"0.2.0\", pluginApiVersion: 1 },\n payloads: manager.getPayloads(),\n findings: manager.getFindings(),\n logger: {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n },\n fetch: globalThis.fetch,\n };\n\n // Call onRunStart hooks\n await manager.callHook(\"onRunStart\", async (hook, ctx) => {\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n await hook(runCtx);\n });\n\n // Set up browser event listeners for detection\n const eventFindings: Finding[] = [];\n let currentDetectContext: DetectContext | null = null;\n\n // Dialog handler (for alert-based XSS detection)\n const dialogHandler = async (dialog: Dialog) => {\n if (currentDetectContext) {\n const findings = await manager.callHookCollect<\"onDialog\", Finding>(\n \"onDialog\",\n async (hook, ctx) => {\n const detectCtx: DetectContext = {\n ...currentDetectContext!,\n ...ctx,\n };\n return hook(dialog, detectCtx);\n },\n );\n eventFindings.push(...findings);\n }\n // Always dismiss dialogs to prevent blocking\n try {\n await dialog.dismiss();\n } catch {\n // Dialog may have already been handled\n }\n };\n\n // Console message handler (for console-based XSS detection)\n const consoleHandler = async (msg: ConsoleMessage) => {\n if (currentDetectContext) {\n const findings = await manager.callHookCollect<\n \"onConsoleMessage\",\n Finding\n >(\"onConsoleMessage\", async (hook, ctx) => {\n const detectCtx: DetectContext = { ...currentDetectContext!, ...ctx };\n return hook(msg, detectCtx);\n });\n eventFindings.push(...findings);\n }\n };\n\n page.on(\"dialog\", dialogHandler);\n page.on(\"console\", consoleHandler);\n\n try {\n // Find injectable steps\n const injectableSteps = session.steps.filter(\n (step): step is Step & { type: \"input\" } =>\n step.type === \"input\" && step.injectable !== false,\n );\n\n // Build flat list of all individual payloads to test\n const allPayloads: { payloadSet: RuntimePayload; value: string }[] = [];\n for (const payloadSet of payloads) {\n for (const value of payloadSet.payloads) {\n allPayloads.push({ payloadSet, value });\n }\n }\n\n // For each injectable step, test with each payload\n for (const injectableStep of injectableSteps) {\n for (const { payloadSet, value: originalValue } of allPayloads) {\n try {\n // Transform payload through plugins\n let transformedPayload = originalValue;\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onBeforePayload;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n transformedPayload = await hook(\n transformedPayload,\n injectableStep,\n runCtx,\n );\n }\n }\n\n // Create detect context for this payload\n currentDetectContext = {\n ...baseRunContext,\n config: {},\n step: injectableStep,\n payloadSet,\n payloadValue: transformedPayload,\n stepId: injectableStep.id,\n };\n\n // Replay session with payload\n await Runner.replayWithPayload(\n page,\n session,\n injectableStep,\n transformedPayload,\n );\n\n // Call onAfterPayload hooks for detection\n const afterFindings = await manager.callHookCollect<\n \"onAfterPayload\",\n Finding\n >(\"onAfterPayload\", async (hook, ctx) => {\n const detectCtx: DetectContext = {\n ...currentDetectContext!,\n ...ctx,\n };\n return hook(detectCtx);\n });\n\n // Also do basic reflection check (built-in fallback)\n const reflectionFinding = await Runner.checkReflection(\n page,\n injectableStep,\n payloadSet,\n transformedPayload,\n );\n\n // Collect all findings\n const allFindings = [...afterFindings, ...eventFindings];\n if (reflectionFinding) {\n allFindings.push(reflectionFinding);\n }\n\n // Add unique findings\n for (const finding of allFindings) {\n manager.addFinding(finding);\n options.onFinding?.(finding);\n }\n\n // Clear event findings for next iteration\n eventFindings.length = 0;\n payloadsTested++;\n } catch (err) {\n errors.push(`${injectableStep.id}: ${String(err)}`);\n }\n }\n }\n } finally {\n // Remove listeners\n page.off(\"dialog\", dialogHandler);\n page.off(\"console\", consoleHandler);\n\n currentDetectContext = null;\n await browser.close();\n }\n\n // Create result\n let result: RunResult = {\n findings: manager.getFindings(),\n stepsExecuted: session.steps.length,\n payloadsTested,\n duration: Date.now() - startTime,\n errors,\n };\n\n // Transform result through plugins\n for (const loaded of manager.getPlugins()) {\n const hook = loaded.plugin.hooks?.onRunEnd;\n if (hook) {\n const ctx = manager.createContext(loaded.config);\n const runCtx: RunContext = { ...baseRunContext, ...ctx };\n result = await hook(result, runCtx);\n }\n }\n\n return result;\n }\n\n /**\n * Execute with explicit payloads (legacy API, for backwards compatibility)\n */\n static async executeWithPayloads(\n session: Session,\n payloads: RuntimePayload[],\n options: RunnerOptions = {},\n ): Promise<RunResult> {\n // Create a temporary plugin manager with the provided payloads\n const manager = new PluginManager();\n manager.addPayloads(payloads);\n\n return Runner.execute(session, options, { pluginManager: manager });\n }\n\n /**\n * Replay session steps with payload injected at target step\n */\n private static async replayWithPayload(\n page: Page,\n session: Session,\n targetStep: Step & { type: \"input\" },\n payloadValue: string,\n ): Promise<void> {\n // Navigate to start\n await page.goto(session.startUrl, { waitUntil: \"domcontentloaded\" });\n\n // Replay steps\n for (const step of session.steps) {\n try {\n if (step.type === \"navigate\") {\n await page.goto(step.url, { waitUntil: \"domcontentloaded\" });\n } else if (step.type === \"click\") {\n await page.click(step.selector, { timeout: 5000 });\n } else if (step.type === \"input\") {\n // Inject payload for target step\n const value = step.id === targetStep.id ? payloadValue : step.value;\n await page.fill(step.selector, value, { timeout: 5000 });\n } else if (step.type === \"keypress\") {\n const modifiers = step.modifiers ?? [];\n for (const mod of modifiers) {\n await page.keyboard.down(\n mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\",\n );\n }\n await page.keyboard.press(step.key);\n for (const mod of modifiers.reverse()) {\n await page.keyboard.up(mod as \"Control\" | \"Shift\" | \"Alt\" | \"Meta\");\n }\n }\n } catch {\n // Step failed, continue to next\n }\n\n // Stop after target step is injected (we can check sooner)\n if (step.id === targetStep.id) {\n // Wait a bit for any scripts to execute\n await page.waitForTimeout(100);\n break;\n }\n }\n }\n\n /**\n * Basic reflection check - fallback when no detection plugin is loaded\n */\n private static async checkReflection(\n page: Page,\n step: Step & { type: \"input\" },\n payloadSet: RuntimePayload,\n payloadValue: string,\n ): Promise<Finding | undefined> {\n // Get page content\n const content = await page.content();\n\n // Check for reflection patterns\n for (const pattern of payloadSet.detectPatterns) {\n if (pattern.test(content)) {\n return {\n type: payloadSet.category,\n severity: Runner.getSeverity(payloadSet.category),\n title: `${payloadSet.category.toUpperCase()} vulnerability detected`,\n description: `Payload pattern was reflected in page content`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n evidence: content.match(pattern)?.[0]?.slice(0, 200),\n };\n }\n }\n\n // Check if payload appears verbatim (potential XSS)\n if (content.includes(payloadValue)) {\n return {\n type: payloadSet.category,\n severity: \"medium\",\n title: `Potential ${payloadSet.category.toUpperCase()} - payload reflection`,\n description: `Payload was reflected in page without encoding`,\n stepId: step.id,\n payload: payloadValue,\n url: page.url(),\n };\n }\n\n return undefined;\n }\n\n /**\n * Determine severity based on vulnerability category\n */\n private static getSeverity(\n category: PayloadCategory,\n ): \"critical\" | \"high\" | \"medium\" | \"low\" | \"info\" {\n switch (category) {\n case \"sqli\":\n case \"command-injection\":\n case \"xxe\":\n return \"critical\";\n case \"xss\":\n case \"ssrf\":\n case \"path-traversal\":\n return \"high\";\n case \"open-redirect\":\n return \"medium\";\n default:\n return \"medium\";\n }\n }\n}\n"],"mappings":"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","names":["module","pluginManager","import_node_path","YAML","module","import_zod","import_yaml"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/driver-manager.ts","../src/driver-types.ts","../src/plugin-manager.ts","../src/plugin-types.ts"],"sourcesContent":["/**\n * @vulcn/engine - Core security testing engine\n *\n * v0.3.0: Driver-based architecture\n *\n * The engine now provides:\n * - Driver system for different recording targets (browser, api, cli)\n * - Plugin system for payloads and detection\n * - Generic session format\n *\n * Drivers handle:\n * - Recording interactions (RecorderDriver)\n * - Replaying with payload injection (RunnerDriver)\n *\n * Plugins handle:\n * - Payload loading (builtin, payloadbox, custom files)\n * - Vulnerability detection (reflection, execution, etc.)\n * - Reporting (JSON, SARIF, HTML)\n */\n\n// ============================================================================\n// Driver System\n// ============================================================================\n\nexport { DriverManager, driverManager } from \"./driver-manager\";\nexport { DRIVER_API_VERSION } from \"./driver-types\";\nexport type {\n VulcnDriver,\n RecorderDriver,\n RunnerDriver,\n RecordingHandle,\n RecordOptions,\n RunOptions,\n RunResult,\n RunContext,\n Session,\n Step,\n DriverLogger,\n LoadedDriver,\n DriverSource,\n} from \"./driver-types\";\n\n// ============================================================================\n// Plugin System\n// ============================================================================\n\nexport { PluginManager, pluginManager } from \"./plugin-manager\";\nexport { PLUGIN_API_VERSION } from \"./plugin-types\";\nexport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n PluginHooks,\n PluginContext,\n RecordContext,\n RunContext as PluginRunContext,\n DetectContext,\n LoadedPlugin as LoadedPluginInfo,\n PluginLogger,\n EngineInfo,\n PluginSource,\n} from \"./plugin-types\";\n\n// ============================================================================\n// Payload Types\n// ============================================================================\n\nexport type {\n PayloadCategory,\n PayloadSource,\n RuntimePayload,\n CustomPayload,\n CustomPayloadFile,\n} from \"./payload-types\";\n\n// ============================================================================\n// Core Types\n// ============================================================================\n\nexport type { Finding } from \"./types\";\n","/**\n * Vulcn Driver Manager\n *\n * Handles driver loading, registration, and lifecycle.\n * Drivers are loaded from npm packages or local files.\n */\n\nimport { isAbsolute, resolve } from \"node:path\";\nimport { parse, stringify } from \"yaml\";\nimport type {\n VulcnDriver,\n LoadedDriver,\n DriverSource,\n Session,\n Step,\n RunContext,\n RunResult,\n RunOptions,\n RecordOptions,\n RecordingHandle,\n DriverLogger,\n DRIVER_API_VERSION,\n} from \"./driver-types\";\nimport type { PluginManager } from \"./plugin-manager\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\n\n/**\n * Driver Manager - loads and manages recording/running drivers\n */\nexport class DriverManager {\n private drivers: Map<string, LoadedDriver> = new Map();\n private defaultDriver: string | null = null;\n\n /**\n * Register a driver\n */\n register(driver: VulcnDriver, source: DriverSource = \"builtin\"): void {\n this.validateDriver(driver);\n this.drivers.set(driver.name, { driver, source });\n\n // First registered driver becomes default\n if (this.drivers.size === 1) {\n this.defaultDriver = driver.name;\n }\n }\n\n /**\n * Load a driver from npm or local path\n */\n async load(nameOrPath: string): Promise<void> {\n let driver: VulcnDriver;\n let source: DriverSource;\n\n if (\n nameOrPath.startsWith(\"./\") ||\n nameOrPath.startsWith(\"../\") ||\n isAbsolute(nameOrPath)\n ) {\n // Local file\n const resolved = isAbsolute(nameOrPath)\n ? nameOrPath\n : resolve(process.cwd(), nameOrPath);\n const module = await import(resolved);\n driver = module.default || module;\n source = \"local\";\n } else {\n // npm package\n const module = await import(nameOrPath);\n driver = module.default || module;\n source = \"npm\";\n }\n\n this.register(driver, source);\n }\n\n /**\n * Get a loaded driver by name\n */\n get(name: string): VulcnDriver | undefined {\n return this.drivers.get(name)?.driver;\n }\n\n /**\n * Get the default driver\n */\n getDefault(): VulcnDriver | undefined {\n if (!this.defaultDriver) return undefined;\n return this.get(this.defaultDriver);\n }\n\n /**\n * Set the default driver\n */\n setDefault(name: string): void {\n if (!this.drivers.has(name)) {\n throw new Error(`Driver \"${name}\" is not registered`);\n }\n this.defaultDriver = name;\n }\n\n /**\n * Check if a driver is registered\n */\n has(name: string): boolean {\n return this.drivers.has(name);\n }\n\n /**\n * Get all registered drivers\n */\n list(): LoadedDriver[] {\n return Array.from(this.drivers.values());\n }\n\n /**\n * Get driver for a session\n */\n getForSession(session: Session): VulcnDriver {\n const driverName = session.driver;\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(\n `Driver \"${driverName}\" not found. Install @vulcn/driver-${driverName} or load it manually.`,\n );\n }\n\n return driver;\n }\n\n /**\n * Parse a YAML session string into a Session object.\n *\n * Handles both new driver-format sessions and legacy v1 sessions.\n * Legacy sessions (those with non-namespaced step types like \"click\",\n * \"input\", \"navigate\") are automatically converted to the driver format\n * (e.g., \"browser.click\", \"browser.input\", \"browser.navigate\").\n *\n * @param yaml - Raw YAML string\n * @param defaultDriver - Driver to assign for legacy sessions (default: \"browser\")\n */\n parseSession(yaml: string, defaultDriver = \"browser\"): Session {\n const data = parse(yaml) as Record<string, unknown>;\n\n // Already in driver format — has a `driver` field\n if (data.driver && typeof data.driver === \"string\") {\n return data as unknown as Session;\n }\n\n // Legacy format — convert to driver session\n const steps = (data.steps as Array<Record<string, unknown>>) ?? [];\n const convertedSteps: Step[] = steps.map((step) => {\n const type = step.type as string;\n\n // If step type is already namespaced (e.g. \"browser.click\"), keep it\n if (type.includes(\".\")) {\n return step as unknown as Step;\n }\n\n // Convert legacy type → namespaced type\n return {\n ...step,\n type: `${defaultDriver}.${type}`,\n } as unknown as Step;\n });\n\n return {\n name: (data.name as string) ?? \"Untitled Session\",\n driver: defaultDriver,\n driverConfig: {\n browser: data.browser ?? \"chromium\",\n viewport: data.viewport ?? { width: 1280, height: 720 },\n startUrl: data.startUrl as string,\n },\n steps: convertedSteps,\n metadata: {\n recordedAt: data.recordedAt as string,\n version: (data.version as string) ?? \"1\",\n },\n };\n }\n\n /**\n * Start recording with a driver\n */\n async startRecording(\n driverName: string,\n config: Record<string, unknown>,\n options: RecordOptions = {},\n ): Promise<RecordingHandle> {\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(`Driver \"${driverName}\" not found`);\n }\n\n return driver.recorder.start(config, options);\n }\n\n /**\n * Execute a session\n * Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.\n */\n async execute(\n session: Session,\n pluginManager: PluginManager,\n options: RunOptions = {},\n ): Promise<RunResult> {\n const driver = this.getForSession(session);\n const findings: Finding[] = [];\n const logger = this.createLogger(driver.name);\n\n const ctx: RunContext = {\n session,\n pluginManager,\n payloads: pluginManager.getPayloads(),\n findings,\n addFinding: (finding) => {\n findings.push(finding);\n pluginManager.addFinding(finding);\n options.onFinding?.(finding);\n },\n logger,\n options,\n };\n\n // Build a plugin context for hooks\n const pluginCtx = {\n session,\n page: null as unknown,\n headless: !!(options as Record<string, unknown>).headless,\n config: {} as Record<string, unknown>,\n engine: { version: \"0.3.0\", pluginApiVersion: 1 },\n payloads: pluginManager.getPayloads(),\n findings,\n logger,\n fetch: globalThis.fetch,\n };\n\n // Call onRunStart hooks\n for (const loaded of pluginManager.getPlugins()) {\n if (loaded.enabled && loaded.plugin.hooks?.onRunStart) {\n try {\n await loaded.plugin.hooks.onRunStart({\n ...pluginCtx,\n config: loaded.config,\n });\n } catch (err) {\n logger.warn(`Plugin ${loaded.plugin.name} onRunStart failed: ${err}`);\n }\n }\n }\n\n // Execute via driver runner\n let result = await driver.runner.execute(session, ctx);\n\n // Call onRunEnd hooks (e.g., report generation)\n for (const loaded of pluginManager.getPlugins()) {\n if (loaded.enabled && loaded.plugin.hooks?.onRunEnd) {\n try {\n result = await loaded.plugin.hooks.onRunEnd(result, {\n ...pluginCtx,\n config: loaded.config,\n findings: result.findings,\n });\n } catch (err) {\n logger.warn(`Plugin ${loaded.plugin.name} onRunEnd failed: ${err}`);\n }\n }\n }\n\n return result;\n }\n\n /**\n * Validate driver structure\n */\n private validateDriver(driver: unknown): asserts driver is VulcnDriver {\n if (!driver || typeof driver !== \"object\") {\n throw new Error(\"Driver must be an object\");\n }\n\n const d = driver as Record<string, unknown>;\n\n if (typeof d.name !== \"string\" || !d.name) {\n throw new Error(\"Driver must have a name\");\n }\n\n if (typeof d.version !== \"string\" || !d.version) {\n throw new Error(\"Driver must have a version\");\n }\n\n if (!Array.isArray(d.stepTypes) || d.stepTypes.length === 0) {\n throw new Error(\"Driver must define stepTypes\");\n }\n\n if (!d.recorder || typeof d.recorder !== \"object\") {\n throw new Error(\"Driver must have a recorder\");\n }\n\n if (!d.runner || typeof d.runner !== \"object\") {\n throw new Error(\"Driver must have a runner\");\n }\n }\n\n /**\n * Create a scoped logger for a driver\n */\n private createLogger(name: string): DriverLogger {\n const prefix = `[driver:${name}]`;\n return {\n debug: (msg, ...args) => console.debug(prefix, msg, ...args),\n info: (msg, ...args) => console.info(prefix, msg, ...args),\n warn: (msg, ...args) => console.warn(prefix, msg, ...args),\n error: (msg, ...args) => console.error(prefix, msg, ...args),\n };\n }\n}\n\n/**\n * Default driver manager instance\n */\nexport const driverManager = new DriverManager();\n","/**\n * Vulcn Driver System\n *\n * Drivers handle recording and running sessions for different targets:\n * - browser: Web applications (Playwright)\n * - api: REST/HTTP APIs\n * - cli: Command-line tools\n *\n * Each driver implements RecorderDriver and RunnerDriver interfaces.\n */\n\nimport type { z } from \"zod\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\nimport type { PluginManager } from \"./plugin-manager\";\n\n/**\n * Current driver API version\n */\nexport const DRIVER_API_VERSION = 1;\n\n/**\n * Generic step - drivers define their own step types\n */\nexport interface Step {\n /** Unique step ID */\n id: string;\n\n /** Step type (namespaced, e.g., \"browser.click\", \"api.request\") */\n type: string;\n\n /** Timestamp when step was recorded */\n timestamp: number;\n\n /** Step-specific data */\n [key: string]: unknown;\n}\n\n/**\n * Generic session format\n */\nexport interface Session {\n /** Session name */\n name: string;\n\n /** Driver that recorded this session */\n driver: string;\n\n /** Driver-specific configuration */\n driverConfig: Record<string, unknown>;\n\n /** Recorded steps */\n steps: Step[];\n\n /** Session metadata */\n metadata?: {\n recordedAt?: string;\n version?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Recording context passed to drivers\n */\nexport interface RecordContext {\n /** Session being built */\n session: Partial<Session>;\n\n /** Add a step to the session */\n addStep(step: Omit<Step, \"id\" | \"timestamp\">): void;\n\n /** Logger */\n logger: DriverLogger;\n}\n\n/**\n * Running context passed to drivers\n */\nexport interface RunContext {\n /** Session being executed */\n session: Session;\n\n /** Plugin manager for calling hooks */\n pluginManager: PluginManager;\n\n /** Available payloads */\n payloads: RuntimePayload[];\n\n /** Collected findings */\n findings: Finding[];\n\n /** Add a finding */\n addFinding(finding: Finding): void;\n\n /** Logger */\n logger: DriverLogger;\n\n /** Running options */\n options: RunOptions;\n}\n\n/**\n * Options for recording\n */\nexport interface RecordOptions {\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Options for running\n */\nexport interface RunOptions {\n /** Run headless (for visual drivers) */\n headless?: boolean;\n\n /** Callback for findings */\n onFinding?: (finding: Finding) => void;\n\n /** Callback for step completion */\n onStepComplete?: (stepId: string, payloadCount: number) => void;\n\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Run result\n */\nexport interface RunResult {\n /** All findings */\n findings: Finding[];\n\n /** Steps executed */\n stepsExecuted: number;\n\n /** Payloads tested */\n payloadsTested: number;\n\n /** Duration in milliseconds */\n duration: number;\n\n /** Errors encountered */\n errors: string[];\n}\n\n/**\n * Driver logger\n */\nexport interface DriverLogger {\n debug(msg: string, ...args: unknown[]): void;\n info(msg: string, ...args: unknown[]): void;\n warn(msg: string, ...args: unknown[]): void;\n error(msg: string, ...args: unknown[]): void;\n}\n\n/**\n * Recorder Driver Interface\n *\n * Implement this to add recording support for a target type.\n */\nexport interface RecorderDriver {\n /** Start recording and return control handle */\n start(\n config: Record<string, unknown>,\n options: RecordOptions,\n ): Promise<RecordingHandle>;\n}\n\n/**\n * Handle returned by RecorderDriver.start()\n */\nexport interface RecordingHandle {\n /** Stop recording and return the session */\n stop(): Promise<Session>;\n\n /** Abort recording without saving */\n abort(): Promise<void>;\n\n /** Get current steps (during recording) */\n getSteps(): Step[];\n\n /** Manually add a step */\n addStep(step: Omit<Step, \"id\" | \"timestamp\">): void;\n}\n\n/**\n * Runner Driver Interface\n *\n * Implement this to add running/replay support for a target type.\n */\nexport interface RunnerDriver {\n /** Execute a session with payloads */\n execute(session: Session, ctx: RunContext): Promise<RunResult>;\n}\n\n/**\n * Complete driver definition\n */\nexport interface VulcnDriver {\n /** Unique driver name (e.g., \"browser\", \"api\", \"cli\") */\n name: string;\n\n /** Driver version */\n version: string;\n\n /** Driver API version */\n apiVersion?: number;\n\n /** Human-readable description */\n description?: string;\n\n /** Configuration schema (Zod) */\n configSchema?: z.ZodSchema;\n\n /** Step types this driver handles */\n stepTypes: string[];\n\n /** Recorder implementation */\n recorder: RecorderDriver;\n\n /** Runner implementation */\n runner: RunnerDriver;\n}\n\n/**\n * Driver source for loading\n */\nexport type DriverSource = \"npm\" | \"local\" | \"builtin\";\n\n/**\n * Loaded driver with metadata\n */\nexport interface LoadedDriver {\n driver: VulcnDriver;\n source: DriverSource;\n}\n","/**\n * Vulcn Plugin Manager\n * Handles plugin loading, lifecycle, and hook execution\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { resolve, isAbsolute } from \"node:path\";\nimport YAML from \"yaml\";\nimport { z } from \"zod\";\nimport type {\n VulcnPlugin,\n VulcnConfig,\n PluginConfig,\n LoadedPlugin,\n PluginContext,\n PluginSource,\n PluginLogger,\n EngineInfo,\n PluginHooks,\n} from \"./plugin-types\";\nimport { PLUGIN_API_VERSION } from \"./plugin-types\";\nimport type { Finding } from \"./types\";\nimport type { RuntimePayload } from \"./payload-types\";\n\n// Package version (injected at build time or read from package.json)\nconst ENGINE_VERSION = \"0.2.0\";\n\n/**\n * Config file schema\n */\nconst VulcnConfigSchema = z.object({\n version: z.string().default(\"1\"),\n plugins: z\n .array(\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n enabled: z.boolean().default(true),\n }),\n )\n .optional(),\n settings: z\n .object({\n browser: z.enum([\"chromium\", \"firefox\", \"webkit\"]).optional(),\n headless: z.boolean().optional(),\n timeout: z.number().optional(),\n })\n .optional(),\n});\n\n/**\n * Plugin Manager - loads, configures, and orchestrates plugins\n */\nexport class PluginManager {\n private plugins: LoadedPlugin[] = [];\n private config: VulcnConfig | null = null;\n private initialized = false;\n\n /**\n * Shared context passed to all plugins\n */\n private sharedPayloads: RuntimePayload[] = [];\n private sharedFindings: Finding[] = [];\n\n /**\n * Load configuration from vulcn.config.yml\n */\n async loadConfig(configPath?: string): Promise<VulcnConfig> {\n const paths = configPath\n ? [configPath]\n : [\n \"vulcn.config.yml\",\n \"vulcn.config.yaml\",\n \"vulcn.config.json\",\n \".vulcnrc.yml\",\n \".vulcnrc.yaml\",\n \".vulcnrc.json\",\n ];\n\n for (const path of paths) {\n const resolved = isAbsolute(path) ? path : resolve(process.cwd(), path);\n if (existsSync(resolved)) {\n const content = await readFile(resolved, \"utf-8\");\n const parsed = path.endsWith(\".json\")\n ? JSON.parse(content)\n : YAML.parse(content);\n this.config = VulcnConfigSchema.parse(parsed);\n return this.config;\n }\n }\n\n // No config file - use defaults\n this.config = { version: \"1\", plugins: [], settings: {} };\n return this.config;\n }\n\n /**\n * Load all plugins from config\n */\n async loadPlugins(): Promise<void> {\n if (!this.config) {\n await this.loadConfig();\n }\n\n const pluginConfigs = this.config?.plugins || [];\n\n for (const pluginConfig of pluginConfigs) {\n if (pluginConfig.enabled === false) continue;\n\n try {\n const loaded = await this.loadPlugin(pluginConfig);\n this.plugins.push(loaded);\n } catch (err) {\n console.error(\n `Failed to load plugin ${pluginConfig.name}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n /**\n * Load a single plugin\n */\n private async loadPlugin(config: PluginConfig): Promise<LoadedPlugin> {\n const { name, config: pluginConfig = {} } = config;\n let plugin: VulcnPlugin;\n let source: PluginSource;\n\n // Determine plugin source and load\n if (name.startsWith(\"./\") || name.startsWith(\"../\") || isAbsolute(name)) {\n // Local file plugin\n const resolved = isAbsolute(name) ? name : resolve(process.cwd(), name);\n const module = await import(resolved);\n plugin = module.default || module;\n source = \"local\";\n } else if (name.startsWith(\"@vulcn/\")) {\n // Official plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n } else {\n // Community plugin (npm package)\n const module = await import(name);\n plugin = module.default || module;\n source = \"npm\";\n }\n\n // Validate plugin structure\n this.validatePlugin(plugin);\n\n // Validate plugin config if schema provided\n let resolvedConfig = pluginConfig;\n if (plugin.configSchema) {\n try {\n resolvedConfig = plugin.configSchema.parse(pluginConfig);\n } catch (err) {\n throw new Error(\n `Invalid config for plugin ${name}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n return {\n plugin,\n config: resolvedConfig,\n source,\n enabled: true,\n };\n }\n\n /**\n * Validate plugin structure\n */\n private validatePlugin(plugin: unknown): asserts plugin is VulcnPlugin {\n if (!plugin || typeof plugin !== \"object\") {\n throw new Error(\"Plugin must be an object\");\n }\n\n const p = plugin as Record<string, unknown>;\n if (typeof p.name !== \"string\" || !p.name) {\n throw new Error(\"Plugin must have a name\");\n }\n if (typeof p.version !== \"string\" || !p.version) {\n throw new Error(\"Plugin must have a version\");\n }\n\n // Check API version compatibility\n const apiVersion = (p.apiVersion as number) || 1;\n if (apiVersion > PLUGIN_API_VERSION) {\n throw new Error(\n `Plugin requires API version ${apiVersion}, but engine supports ${PLUGIN_API_VERSION}`,\n );\n }\n }\n\n /**\n * Add a plugin programmatically (for testing or dynamic loading)\n */\n addPlugin(plugin: VulcnPlugin, config: Record<string, unknown> = {}): void {\n this.validatePlugin(plugin);\n this.plugins.push({\n plugin,\n config,\n source: \"custom\",\n enabled: true,\n });\n }\n\n /**\n * Initialize all plugins (call onInit hooks)\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Load payloads from plugins that provide them\n for (const loaded of this.plugins) {\n if (loaded.plugin.payloads) {\n const payloads =\n typeof loaded.plugin.payloads === \"function\"\n ? await loaded.plugin.payloads()\n : loaded.plugin.payloads;\n this.sharedPayloads.push(...payloads);\n }\n }\n\n // Call onInit hooks\n await this.callHook(\"onInit\", (hook, ctx) => hook(ctx));\n\n this.initialized = true;\n }\n\n /**\n * Destroy all plugins (call onDestroy hooks)\n */\n async destroy(): Promise<void> {\n await this.callHook(\"onDestroy\", (hook, ctx) => hook(ctx));\n this.plugins = [];\n this.sharedPayloads = [];\n this.sharedFindings = [];\n this.initialized = false;\n }\n\n /**\n * Get all loaded payloads\n */\n getPayloads(): RuntimePayload[] {\n return this.sharedPayloads;\n }\n\n /**\n * Get all collected findings\n */\n getFindings(): Finding[] {\n return this.sharedFindings;\n }\n\n /**\n * Add a finding (used by detectors)\n */\n addFinding(finding: Finding): void {\n this.sharedFindings.push(finding);\n }\n\n /**\n * Add payloads (used by loaders)\n */\n addPayloads(payloads: RuntimePayload[]): void {\n this.sharedPayloads.push(...payloads);\n }\n\n /**\n * Clear findings (for new run)\n */\n clearFindings(): void {\n this.sharedFindings = [];\n }\n\n /**\n * Get loaded plugins\n */\n getPlugins(): LoadedPlugin[] {\n return this.plugins;\n }\n\n /**\n * Check if a plugin is loaded by name\n */\n hasPlugin(name: string): boolean {\n return this.plugins.some((p) => p.plugin.name === name);\n }\n\n /**\n * Create base context for plugins\n */\n createContext(pluginConfig: Record<string, unknown>): PluginContext {\n const engineInfo: EngineInfo = {\n version: ENGINE_VERSION,\n pluginApiVersion: PLUGIN_API_VERSION,\n };\n\n return {\n config: pluginConfig,\n engine: engineInfo,\n payloads: this.sharedPayloads,\n findings: this.sharedFindings,\n logger: this.createLogger(\"plugin\"),\n fetch: globalThis.fetch,\n };\n }\n\n /**\n * Create scoped logger for a plugin\n */\n private createLogger(name: string): PluginLogger {\n const prefix = `[${name}]`;\n return {\n debug: (msg, ...args) => console.debug(prefix, msg, ...args),\n info: (msg, ...args) => console.info(prefix, msg, ...args),\n warn: (msg, ...args) => console.warn(prefix, msg, ...args),\n error: (msg, ...args) => console.error(prefix, msg, ...args),\n };\n }\n\n /**\n * Call a hook on all plugins sequentially\n */\n async callHook<K extends keyof PluginHooks>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<unknown>,\n ): Promise<void> {\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n await executor(hook as NonNullable<PluginHooks[K]>, ctx);\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n }\n\n /**\n * Call a hook and collect results\n */\n async callHookCollect<K extends keyof PluginHooks, R>(\n hookName: K,\n executor: (\n hook: NonNullable<PluginHooks[K]>,\n ctx: PluginContext,\n ) => Promise<R | R[] | null>,\n ): Promise<R[]> {\n const results: R[] = [];\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n const result = await executor(\n hook as NonNullable<PluginHooks[K]>,\n ctx,\n );\n if (result !== null && result !== undefined) {\n if (Array.isArray(result)) {\n results.push(...result);\n } else {\n results.push(result);\n }\n }\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return results;\n }\n\n /**\n * Call a hook that transforms a value through the pipeline\n */\n async callHookPipe<T>(\n hookName: keyof PluginHooks,\n initial: T,\n executor: (\n hook: NonNullable<PluginHooks[typeof hookName]>,\n value: T,\n ctx: PluginContext,\n ) => Promise<T>,\n ): Promise<T> {\n let value = initial;\n\n for (const loaded of this.plugins) {\n const hook = loaded.plugin.hooks?.[hookName];\n if (hook) {\n const ctx = this.createContext(loaded.config);\n ctx.logger = this.createLogger(loaded.plugin.name);\n try {\n value = await executor(\n hook as NonNullable<PluginHooks[typeof hookName]>,\n value,\n ctx,\n );\n } catch (err) {\n console.error(\n `Error in plugin ${loaded.plugin.name}.${hookName}:`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n }\n\n return value;\n }\n}\n\n/**\n * Default shared plugin manager instance\n */\nexport const pluginManager = new PluginManager();\n","/**\n * Vulcn Plugin System Types\n * @module @vulcn/engine/plugin\n *\n * The plugin system is driver-agnostic. Detection plugins receive\n * a generic page interface rather than Playwright types directly.\n * This allows the same plugin to work across different driver types.\n */\n\nimport type { z } from \"zod\";\nimport type { Session, Step } from \"./driver-types\";\nimport type { Finding } from \"./types\";\nimport type { RunResult } from \"./driver-types\";\nimport type { RuntimePayload, PayloadCategory } from \"./payload-types\";\n\n// Re-export for plugin authors\nexport type {\n Session,\n Step,\n Finding,\n RunResult,\n RuntimePayload,\n PayloadCategory,\n};\n\n/**\n * Plugin API version - plugins declare compatibility\n */\nexport const PLUGIN_API_VERSION = 1;\n\n/**\n * Plugin source types for identification\n */\nexport type PluginSource = \"builtin\" | \"npm\" | \"local\" | \"custom\";\n\n/**\n * Main plugin interface\n */\nexport interface VulcnPlugin {\n /** Unique plugin name (e.g., \"@vulcn/plugin-payloads\") */\n name: string;\n\n /** Plugin version (semver) */\n version: string;\n\n /** Plugin API version this plugin targets */\n apiVersion?: number;\n\n /** Human-readable description */\n description?: string;\n\n /** Lifecycle hooks */\n hooks?: PluginHooks;\n\n /**\n * Payloads provided by this plugin (Loaders)\n * Can be static array or async function for lazy loading\n */\n payloads?: RuntimePayload[] | (() => Promise<RuntimePayload[]>);\n\n /**\n * Zod schema for plugin configuration validation\n */\n configSchema?: z.ZodSchema;\n}\n\n/**\n * Plugin lifecycle hooks\n *\n * Detection hooks (onDialog, onConsoleMessage, etc.) receive\n * Playwright types from the driver. Plugins that use these\n * should declare playwright as a peer/dev dependency.\n */\nexport interface PluginHooks {\n // ─────────────────────────────────────────────────────────────────\n // Initialization\n // ─────────────────────────────────────────────────────────────────\n\n /**\n * Called when plugin is loaded, before any operation\n * Use for setup, loading payloads, etc.\n */\n onInit?: (ctx: PluginContext) => Promise<void>;\n\n /**\n * Called when plugin is unloaded/cleanup\n */\n onDestroy?: (ctx: PluginContext) => Promise<void>;\n\n // ─────────────────────────────────────────────────────────────────\n // Recording Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when recording starts */\n onRecordStart?: (ctx: RecordContext) => Promise<void>;\n\n /** Called for each recorded step, can transform */\n onRecordStep?: (step: Step, ctx: RecordContext) => Promise<Step>;\n\n /** Called when recording ends, can transform session */\n onRecordEnd?: (session: Session, ctx: RecordContext) => Promise<Session>;\n\n // ─────────────────────────────────────────────────────────────────\n // Running Phase\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when run starts */\n onRunStart?: (ctx: RunContext) => Promise<void>;\n\n /** Called before each payload is injected, can transform payload */\n onBeforePayload?: (\n payload: string,\n step: Step,\n ctx: RunContext,\n ) => Promise<string>;\n\n /** Called after payload injection, for detection */\n onAfterPayload?: (ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called when run ends, can transform results */\n onRunEnd?: (result: RunResult, ctx: RunContext) => Promise<RunResult>;\n\n // ─────────────────────────────────────────────────────────────────\n // Browser Event Hooks (Detection)\n // These receive driver-specific types (e.g. Playwright's Dialog)\n // ─────────────────────────────────────────────────────────────────\n\n /** Called when JavaScript alert/confirm/prompt appears */\n onDialog?: (dialog: unknown, ctx: DetectContext) => Promise<Finding | null>;\n\n /** Called on console.log/warn/error */\n onConsoleMessage?: (\n msg: unknown,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on page load/navigation */\n onPageLoad?: (page: unknown, ctx: DetectContext) => Promise<Finding[]>;\n\n /** Called on network request */\n onNetworkRequest?: (\n request: unknown,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n\n /** Called on network response */\n onNetworkResponse?: (\n response: unknown,\n ctx: DetectContext,\n ) => Promise<Finding | null>;\n}\n\n/**\n * Logger interface for plugins\n */\nexport interface PluginLogger {\n debug: (msg: string, ...args: unknown[]) => void;\n info: (msg: string, ...args: unknown[]) => void;\n warn: (msg: string, ...args: unknown[]) => void;\n error: (msg: string, ...args: unknown[]) => void;\n}\n\n/**\n * Engine information exposed to plugins\n */\nexport interface EngineInfo {\n version: string;\n pluginApiVersion: number;\n}\n\n/**\n * Base context available to all plugin hooks\n */\nexport interface PluginContext {\n /** Plugin-specific config from vulcn.config.yml */\n config: Record<string, unknown>;\n\n /** Engine information */\n engine: EngineInfo;\n\n /** Shared payload registry - loaders add payloads here */\n payloads: RuntimePayload[];\n\n /** Shared findings collection - detectors add findings here */\n findings: Finding[];\n\n /** Scoped logger */\n logger: PluginLogger;\n\n /** Fetch API for network requests */\n fetch: typeof fetch;\n}\n\n/**\n * Context for recording phase hooks\n */\nexport interface RecordContext extends PluginContext {\n /** Page interface (driver-specific, e.g. Playwright Page) */\n page: unknown;\n}\n\n/**\n * Context for running phase hooks\n */\nexport interface RunContext extends PluginContext {\n /** Session being executed */\n session: Session;\n\n /** Page interface (driver-specific, e.g. Playwright Page) */\n page: unknown;\n\n /** Whether running headless */\n headless: boolean;\n}\n\n/**\n * Context for detection hooks\n */\nexport interface DetectContext extends RunContext {\n /** Current step being tested */\n step: Step;\n\n /** Current payload set being tested */\n payloadSet: RuntimePayload;\n\n /** Actual payload value injected */\n payloadValue: string;\n\n /** Step ID for reporting */\n stepId: string;\n}\n\n/**\n * Plugin configuration in vulcn.config.yml\n */\nexport interface PluginConfig {\n /** Plugin name/path */\n name: string;\n\n /** Plugin-specific configuration */\n config?: Record<string, unknown>;\n\n /** Whether plugin is enabled (default: true) */\n enabled?: boolean;\n}\n\n/**\n * Vulcn configuration file schema\n */\nexport interface VulcnConfig {\n /** Config version */\n version: string;\n\n /** Plugins to load */\n plugins?: PluginConfig[];\n\n /** Global settings */\n settings?: {\n headless?: boolean;\n timeout?: number;\n };\n}\n\n/**\n * Loaded plugin instance with resolved config\n */\nexport interface LoadedPlugin {\n /** Plugin definition */\n plugin: VulcnPlugin;\n\n /** Resolved configuration */\n config: Record<string, unknown>;\n\n /** Source of the plugin */\n source: PluginSource;\n\n /** Whether plugin is enabled */\n enabled: boolean;\n}\n"],"mappings":"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","names":["module","pluginManager","import_node_path","import_yaml","YAML","module"]}