@vulcn/engine 0.3.1 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d4fd4df: ### Breaking: Remove built-in payloads, PayloadBox is now the default
8
+
9
+ All hardcoded built-in payloads have been removed. Payloads are now fetched on demand from [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings), the largest community-curated security payload collection.
10
+
11
+ **`@vulcn/engine`**
12
+ - Removed `"builtin"` from `PayloadSource` type — valid sources are now `"custom" | "payloadbox" | "plugin"`
13
+
14
+ **`@vulcn/plugin-payloads`**
15
+ - Removed all built-in payload sets and the `builtin`, `include`, `exclude`, `payloadbox` config options
16
+ - New config: `types` (short aliases), `limit`, `files`
17
+ - Short aliases for payload types: `xss`, `sqli`, `xxe`, `cmd`, `redirect`, `traversal`
18
+ - Removed legacy `payloadbox:` prefix — use short aliases directly
19
+
20
+ **`vulcn` (CLI)**
21
+ - Default payload changed from `xss-basic` to `xss` (PayloadBox)
22
+ - `vulcn payloads` now lists PayloadBox types with short aliases
23
+ - `vulcn run` help updated with payload type reference
24
+ - Auto-loads `@vulcn/plugin-detect-sqli` when `sqli` payloads are used
25
+
26
+ **`@vulcn/plugin-detect-sqli`**
27
+ - SQL injection detection plugin with error-based, response diffing, and timing-based strategies
28
+ - Auto-loaded by CLI when SQLi payloads are selected
29
+
30
+ ## 0.3.2
31
+
32
+ ### Patch Changes
33
+
34
+ - 51d69b7: ### Auto-Crawl: Automated Form Discovery & Session Generation
35
+
36
+ Adds a new **auto-crawl** capability to the browser driver — automatically discovers injectable forms, inputs, and submit buttons on a target URL, then generates ready-to-run `Session[]` objects. This replaces the need to manually record sessions for basic form testing.
37
+
38
+ #### `@vulcn/engine`
39
+ - **`CrawlOptions` type** — new interface for crawl configuration (`maxDepth`, `maxPages`, `pageTimeout`, `sameOrigin`, `onPageCrawled` callback)
40
+ - **`RecorderDriver.crawl()`** — optional method on the recorder interface, so only drivers that support auto-discovery need to implement it
41
+ - **`DriverManager.crawl()`** — new top-level method that dispatches to the driver's crawl implementation, with clear errors when a driver doesn't support it
42
+ - **Test coverage** — 4 new tests for the crawl flow (success, arg passthrough, missing driver, unsupported driver), coverage at 62.88%
43
+
44
+ #### `@vulcn/driver-browser`
45
+ - **`BrowserCrawler`** — new module (`crawler.ts`) that performs BFS-based crawling using Playwright:
46
+ - Discovers explicit `<form>` elements with their inputs and submit buttons
47
+ - Discovers standalone inputs not inside a `<form>` (common in SPAs)
48
+ - Identifies injectable text-like input types (text, search, url, email, tel, password, textarea)
49
+ - Finds submit triggers (submit buttons, untyped buttons, or falls back to Enter keypress)
50
+ - Follows same-origin links with configurable depth control
51
+ - Generates proper `navigate → input → submit` step sequences per form
52
+ - **`recorder.crawl()`** — wired into the browser driver's recorder interface
53
+ - **Exported** — `crawlAndBuildSessions` available for direct programmatic use
54
+
55
+ #### Architecture
56
+ - Removed standalone `@vulcn/crawler` package — crawler is now a core part of `@vulcn/driver-browser`, consistent with the driver-based architecture
57
+ - Cleaned up `pnpm-workspace.yaml` to remove the deleted crawler entry
58
+
3
59
  ## 0.3.1
4
60
 
5
61
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -172,6 +172,28 @@ var DriverManager = class {
172
172
  }
173
173
  return driver.recorder.start(config, options);
174
174
  }
175
+ /**
176
+ * Auto-crawl a URL using a driver.
177
+ *
178
+ * Uses the driver's optional crawl() method to automatically
179
+ * discover forms and injection points, returning Session[] that
180
+ * can be passed to execute().
181
+ *
182
+ * Not all drivers support this — only browser has crawl capability.
183
+ * CLI and API drivers will throw.
184
+ */
185
+ async crawl(driverName, config, options = {}) {
186
+ const driver = this.get(driverName);
187
+ if (!driver) {
188
+ throw new Error(`Driver "${driverName}" not found`);
189
+ }
190
+ if (!driver.recorder.crawl) {
191
+ throw new Error(
192
+ `Driver "${driverName}" does not support auto-crawl. Use manual recording instead.`
193
+ );
194
+ }
195
+ return driver.recorder.crawl(config, options);
196
+ }
175
197
  /**
176
198
  * Execute a session
177
199
  * Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.
@@ -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"],"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"]}
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 CrawlOptions,\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 CrawlOptions,\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 * Auto-crawl a URL using a driver.\n *\n * Uses the driver's optional crawl() method to automatically\n * discover forms and injection points, returning Session[] that\n * can be passed to execute().\n *\n * Not all drivers support this — only browser has crawl capability.\n * CLI and API drivers will throw.\n */\n async crawl(\n driverName: string,\n config: Record<string, unknown>,\n options: CrawlOptions = {},\n ): Promise<Session[]> {\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(`Driver \"${driverName}\" not found`);\n }\n\n if (!driver.recorder.crawl) {\n throw new Error(\n `Driver \"${driverName}\" does not support auto-crawl. Use manual recording instead.`,\n );\n }\n\n return driver.recorder.crawl(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 /** Enable auto-crawl mode (driver discovers forms automatically) */\n auto?: boolean;\n\n /** Crawl options (only used when auto=true) */\n crawlOptions?: CrawlOptions;\n\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Options for auto-crawl mode\n *\n * When a driver supports crawling, these options control how\n * the automated discovery works. Not all drivers support crawling —\n * it's optional and primarily used by the browser driver.\n */\nexport interface CrawlOptions {\n /** Maximum crawl depth (0 = only the given URL, default: 2) */\n maxDepth?: number;\n\n /** Maximum number of pages to visit (default: 20) */\n maxPages?: number;\n\n /** Timeout per page navigation in ms (default: 10000) */\n pageTimeout?: number;\n\n /** Only crawl pages under the same origin (default: true) */\n sameOrigin?: boolean;\n\n /** Callback when a page is crawled */\n onPageCrawled?: (url: string, formsFound: number) => void;\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 * Auto-crawl a URL and generate sessions.\n *\n * Optional — only drivers that support automated discovery\n * (e.g., browser) implement this. CLI and API drivers do not.\n *\n * When options.auto=true is passed to startRecording, the engine\n * calls this instead of start().\n */\n crawl?(\n config: Record<string, unknown>,\n options: CrawlOptions,\n ): Promise<Session[]>;\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"]}
package/dist/index.d.cts CHANGED
@@ -11,7 +11,7 @@ type PayloadCategory = "xss" | "sqli" | "ssrf" | "xxe" | "command-injection" | "
11
11
  /**
12
12
  * Payload source types
13
13
  */
14
- type PayloadSource = "builtin" | "custom" | "payloadbox" | "plugin";
14
+ type PayloadSource = "custom" | "payloadbox" | "plugin";
15
15
  /**
16
16
  * Runtime payload structure - used by plugins and the runner
17
17
  */
@@ -415,9 +415,32 @@ interface RunContext {
415
415
  * Options for recording
416
416
  */
417
417
  interface RecordOptions {
418
+ /** Enable auto-crawl mode (driver discovers forms automatically) */
419
+ auto?: boolean;
420
+ /** Crawl options (only used when auto=true) */
421
+ crawlOptions?: CrawlOptions;
418
422
  /** Driver-specific options */
419
423
  [key: string]: unknown;
420
424
  }
425
+ /**
426
+ * Options for auto-crawl mode
427
+ *
428
+ * When a driver supports crawling, these options control how
429
+ * the automated discovery works. Not all drivers support crawling —
430
+ * it's optional and primarily used by the browser driver.
431
+ */
432
+ interface CrawlOptions {
433
+ /** Maximum crawl depth (0 = only the given URL, default: 2) */
434
+ maxDepth?: number;
435
+ /** Maximum number of pages to visit (default: 20) */
436
+ maxPages?: number;
437
+ /** Timeout per page navigation in ms (default: 10000) */
438
+ pageTimeout?: number;
439
+ /** Only crawl pages under the same origin (default: true) */
440
+ sameOrigin?: boolean;
441
+ /** Callback when a page is crawled */
442
+ onPageCrawled?: (url: string, formsFound: number) => void;
443
+ }
421
444
  /**
422
445
  * Options for running
423
446
  */
@@ -463,6 +486,16 @@ interface DriverLogger {
463
486
  interface RecorderDriver {
464
487
  /** Start recording and return control handle */
465
488
  start(config: Record<string, unknown>, options: RecordOptions): Promise<RecordingHandle>;
489
+ /**
490
+ * Auto-crawl a URL and generate sessions.
491
+ *
492
+ * Optional — only drivers that support automated discovery
493
+ * (e.g., browser) implement this. CLI and API drivers do not.
494
+ *
495
+ * When options.auto=true is passed to startRecording, the engine
496
+ * calls this instead of start().
497
+ */
498
+ crawl?(config: Record<string, unknown>, options: CrawlOptions): Promise<Session[]>;
466
499
  }
467
500
  /**
468
501
  * Handle returned by RecorderDriver.start()
@@ -580,6 +613,17 @@ declare class DriverManager {
580
613
  * Start recording with a driver
581
614
  */
582
615
  startRecording(driverName: string, config: Record<string, unknown>, options?: RecordOptions): Promise<RecordingHandle>;
616
+ /**
617
+ * Auto-crawl a URL using a driver.
618
+ *
619
+ * Uses the driver's optional crawl() method to automatically
620
+ * discover forms and injection points, returning Session[] that
621
+ * can be passed to execute().
622
+ *
623
+ * Not all drivers support this — only browser has crawl capability.
624
+ * CLI and API drivers will throw.
625
+ */
626
+ crawl(driverName: string, config: Record<string, unknown>, options?: CrawlOptions): Promise<Session[]>;
583
627
  /**
584
628
  * Execute a session
585
629
  * Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.
@@ -599,4 +643,4 @@ declare class DriverManager {
599
643
  */
600
644
  declare const driverManager: DriverManager;
601
645
 
602
- export { type CustomPayload, type CustomPayloadFile, DRIVER_API_VERSION, type DetectContext, type DriverLogger, DriverManager, type DriverSource, type EngineInfo, type Finding, type LoadedDriver, type LoadedPlugin as LoadedPluginInfo, PLUGIN_API_VERSION, type PayloadCategory, type PayloadSource, type PluginConfig, type PluginContext, type PluginHooks, type PluginLogger, PluginManager, type RunContext$1 as PluginRunContext, type PluginSource, type RecordContext, type RecordOptions, type RecorderDriver, type RecordingHandle, type RunContext, type RunOptions, type RunResult, type RunnerDriver, type RuntimePayload, type Session, type Step, type VulcnConfig, type VulcnDriver, type VulcnPlugin, driverManager, pluginManager };
646
+ export { type CrawlOptions, type CustomPayload, type CustomPayloadFile, DRIVER_API_VERSION, type DetectContext, type DriverLogger, DriverManager, type DriverSource, type EngineInfo, type Finding, type LoadedDriver, type LoadedPlugin as LoadedPluginInfo, PLUGIN_API_VERSION, type PayloadCategory, type PayloadSource, type PluginConfig, type PluginContext, type PluginHooks, type PluginLogger, PluginManager, type RunContext$1 as PluginRunContext, type PluginSource, type RecordContext, type RecordOptions, type RecorderDriver, type RecordingHandle, type RunContext, type RunOptions, type RunResult, type RunnerDriver, type RuntimePayload, type Session, type Step, type VulcnConfig, type VulcnDriver, type VulcnPlugin, driverManager, pluginManager };
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ type PayloadCategory = "xss" | "sqli" | "ssrf" | "xxe" | "command-injection" | "
11
11
  /**
12
12
  * Payload source types
13
13
  */
14
- type PayloadSource = "builtin" | "custom" | "payloadbox" | "plugin";
14
+ type PayloadSource = "custom" | "payloadbox" | "plugin";
15
15
  /**
16
16
  * Runtime payload structure - used by plugins and the runner
17
17
  */
@@ -415,9 +415,32 @@ interface RunContext {
415
415
  * Options for recording
416
416
  */
417
417
  interface RecordOptions {
418
+ /** Enable auto-crawl mode (driver discovers forms automatically) */
419
+ auto?: boolean;
420
+ /** Crawl options (only used when auto=true) */
421
+ crawlOptions?: CrawlOptions;
418
422
  /** Driver-specific options */
419
423
  [key: string]: unknown;
420
424
  }
425
+ /**
426
+ * Options for auto-crawl mode
427
+ *
428
+ * When a driver supports crawling, these options control how
429
+ * the automated discovery works. Not all drivers support crawling —
430
+ * it's optional and primarily used by the browser driver.
431
+ */
432
+ interface CrawlOptions {
433
+ /** Maximum crawl depth (0 = only the given URL, default: 2) */
434
+ maxDepth?: number;
435
+ /** Maximum number of pages to visit (default: 20) */
436
+ maxPages?: number;
437
+ /** Timeout per page navigation in ms (default: 10000) */
438
+ pageTimeout?: number;
439
+ /** Only crawl pages under the same origin (default: true) */
440
+ sameOrigin?: boolean;
441
+ /** Callback when a page is crawled */
442
+ onPageCrawled?: (url: string, formsFound: number) => void;
443
+ }
421
444
  /**
422
445
  * Options for running
423
446
  */
@@ -463,6 +486,16 @@ interface DriverLogger {
463
486
  interface RecorderDriver {
464
487
  /** Start recording and return control handle */
465
488
  start(config: Record<string, unknown>, options: RecordOptions): Promise<RecordingHandle>;
489
+ /**
490
+ * Auto-crawl a URL and generate sessions.
491
+ *
492
+ * Optional — only drivers that support automated discovery
493
+ * (e.g., browser) implement this. CLI and API drivers do not.
494
+ *
495
+ * When options.auto=true is passed to startRecording, the engine
496
+ * calls this instead of start().
497
+ */
498
+ crawl?(config: Record<string, unknown>, options: CrawlOptions): Promise<Session[]>;
466
499
  }
467
500
  /**
468
501
  * Handle returned by RecorderDriver.start()
@@ -580,6 +613,17 @@ declare class DriverManager {
580
613
  * Start recording with a driver
581
614
  */
582
615
  startRecording(driverName: string, config: Record<string, unknown>, options?: RecordOptions): Promise<RecordingHandle>;
616
+ /**
617
+ * Auto-crawl a URL using a driver.
618
+ *
619
+ * Uses the driver's optional crawl() method to automatically
620
+ * discover forms and injection points, returning Session[] that
621
+ * can be passed to execute().
622
+ *
623
+ * Not all drivers support this — only browser has crawl capability.
624
+ * CLI and API drivers will throw.
625
+ */
626
+ crawl(driverName: string, config: Record<string, unknown>, options?: CrawlOptions): Promise<Session[]>;
583
627
  /**
584
628
  * Execute a session
585
629
  * Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.
@@ -599,4 +643,4 @@ declare class DriverManager {
599
643
  */
600
644
  declare const driverManager: DriverManager;
601
645
 
602
- export { type CustomPayload, type CustomPayloadFile, DRIVER_API_VERSION, type DetectContext, type DriverLogger, DriverManager, type DriverSource, type EngineInfo, type Finding, type LoadedDriver, type LoadedPlugin as LoadedPluginInfo, PLUGIN_API_VERSION, type PayloadCategory, type PayloadSource, type PluginConfig, type PluginContext, type PluginHooks, type PluginLogger, PluginManager, type RunContext$1 as PluginRunContext, type PluginSource, type RecordContext, type RecordOptions, type RecorderDriver, type RecordingHandle, type RunContext, type RunOptions, type RunResult, type RunnerDriver, type RuntimePayload, type Session, type Step, type VulcnConfig, type VulcnDriver, type VulcnPlugin, driverManager, pluginManager };
646
+ export { type CrawlOptions, type CustomPayload, type CustomPayloadFile, DRIVER_API_VERSION, type DetectContext, type DriverLogger, DriverManager, type DriverSource, type EngineInfo, type Finding, type LoadedDriver, type LoadedPlugin as LoadedPluginInfo, PLUGIN_API_VERSION, type PayloadCategory, type PayloadSource, type PluginConfig, type PluginContext, type PluginHooks, type PluginLogger, PluginManager, type RunContext$1 as PluginRunContext, type PluginSource, type RecordContext, type RecordOptions, type RecorderDriver, type RecordingHandle, type RunContext, type RunOptions, type RunResult, type RunnerDriver, type RuntimePayload, type Session, type Step, type VulcnConfig, type VulcnDriver, type VulcnPlugin, driverManager, pluginManager };
package/dist/index.js CHANGED
@@ -131,6 +131,28 @@ var DriverManager = class {
131
131
  }
132
132
  return driver.recorder.start(config, options);
133
133
  }
134
+ /**
135
+ * Auto-crawl a URL using a driver.
136
+ *
137
+ * Uses the driver's optional crawl() method to automatically
138
+ * discover forms and injection points, returning Session[] that
139
+ * can be passed to execute().
140
+ *
141
+ * Not all drivers support this — only browser has crawl capability.
142
+ * CLI and API drivers will throw.
143
+ */
144
+ async crawl(driverName, config, options = {}) {
145
+ const driver = this.get(driverName);
146
+ if (!driver) {
147
+ throw new Error(`Driver "${driverName}" not found`);
148
+ }
149
+ if (!driver.recorder.crawl) {
150
+ throw new Error(
151
+ `Driver "${driverName}" does not support auto-crawl. Use manual recording instead.`
152
+ );
153
+ }
154
+ return driver.recorder.crawl(config, options);
155
+ }
134
156
  /**
135
157
  * Execute a session
136
158
  * Invokes plugin hooks (onRunStart, onRunEnd) around the driver runner.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/driver-manager.ts","../src/driver-types.ts","../src/plugin-manager.ts","../src/plugin-types.ts"],"sourcesContent":["/**\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":["pluginManager","resolve","isAbsolute","isAbsolute","resolve"]}
1
+ {"version":3,"sources":["../src/driver-manager.ts","../src/driver-types.ts","../src/plugin-manager.ts","../src/plugin-types.ts"],"sourcesContent":["/**\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 CrawlOptions,\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 * Auto-crawl a URL using a driver.\n *\n * Uses the driver's optional crawl() method to automatically\n * discover forms and injection points, returning Session[] that\n * can be passed to execute().\n *\n * Not all drivers support this — only browser has crawl capability.\n * CLI and API drivers will throw.\n */\n async crawl(\n driverName: string,\n config: Record<string, unknown>,\n options: CrawlOptions = {},\n ): Promise<Session[]> {\n const driver = this.get(driverName);\n\n if (!driver) {\n throw new Error(`Driver \"${driverName}\" not found`);\n }\n\n if (!driver.recorder.crawl) {\n throw new Error(\n `Driver \"${driverName}\" does not support auto-crawl. Use manual recording instead.`,\n );\n }\n\n return driver.recorder.crawl(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 /** Enable auto-crawl mode (driver discovers forms automatically) */\n auto?: boolean;\n\n /** Crawl options (only used when auto=true) */\n crawlOptions?: CrawlOptions;\n\n /** Driver-specific options */\n [key: string]: unknown;\n}\n\n/**\n * Options for auto-crawl mode\n *\n * When a driver supports crawling, these options control how\n * the automated discovery works. Not all drivers support crawling —\n * it's optional and primarily used by the browser driver.\n */\nexport interface CrawlOptions {\n /** Maximum crawl depth (0 = only the given URL, default: 2) */\n maxDepth?: number;\n\n /** Maximum number of pages to visit (default: 20) */\n maxPages?: number;\n\n /** Timeout per page navigation in ms (default: 10000) */\n pageTimeout?: number;\n\n /** Only crawl pages under the same origin (default: true) */\n sameOrigin?: boolean;\n\n /** Callback when a page is crawled */\n onPageCrawled?: (url: string, formsFound: number) => void;\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 * Auto-crawl a URL and generate sessions.\n *\n * Optional — only drivers that support automated discovery\n * (e.g., browser) implement this. CLI and API drivers do not.\n *\n * When options.auto=true is passed to startRecording, the engine\n * calls this instead of start().\n */\n crawl?(\n config: Record<string, unknown>,\n options: CrawlOptions,\n ): Promise<Session[]>;\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":";AAOA,SAAS,YAAY,eAAe;AACpC,SAAS,aAAwB;AAuB1B,IAAM,gBAAN,MAAoB;AAAA,EACjB,UAAqC,oBAAI,IAAI;AAAA,EAC7C,gBAA+B;AAAA;AAAA;AAAA;AAAA,EAKvC,SAAS,QAAqB,SAAuB,WAAiB;AACpE,SAAK,eAAe,MAAM;AAC1B,SAAK,QAAQ,IAAI,OAAO,MAAM,EAAE,QAAQ,OAAO,CAAC;AAGhD,QAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,WAAK,gBAAgB,OAAO;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,YAAmC;AAC5C,QAAI;AACJ,QAAI;AAEJ,QACE,WAAW,WAAW,IAAI,KAC1B,WAAW,WAAW,KAAK,KAC3B,WAAW,UAAU,GACrB;AAEA,YAAM,WAAW,WAAW,UAAU,IAClC,aACA,QAAQ,QAAQ,IAAI,GAAG,UAAU;AACrC,YAAM,SAAS,MAAM,OAAO;AAC5B,eAAS,OAAO,WAAW;AAC3B,eAAS;AAAA,IACX,OAAO;AAEL,YAAM,SAAS,MAAM,OAAO;AAC5B,eAAS,OAAO,WAAW;AAC3B,eAAS;AAAA,IACX;AAEA,SAAK,SAAS,QAAQ,MAAM;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAuC;AACzC,WAAO,KAAK,QAAQ,IAAI,IAAI,GAAG;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAsC;AACpC,QAAI,CAAC,KAAK,cAAe,QAAO;AAChC,WAAO,KAAK,IAAI,KAAK,aAAa;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,MAAoB;AAC7B,QAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,GAAG;AAC3B,YAAM,IAAI,MAAM,WAAW,IAAI,qBAAqB;AAAA,IACtD;AACA,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,MAAuB;AACzB,WAAO,KAAK,QAAQ,IAAI,IAAI;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAuB;AACrB,WAAO,MAAM,KAAK,KAAK,QAAQ,OAAO,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAA+B;AAC3C,UAAM,aAAa,QAAQ;AAC3B,UAAM,SAAS,KAAK,IAAI,UAAU;AAElC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,WAAW,UAAU,sCAAsC,UAAU;AAAA,MACvE;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,MAAc,gBAAgB,WAAoB;AAC7D,UAAM,OAAO,MAAM,IAAI;AAGvB,QAAI,KAAK,UAAU,OAAO,KAAK,WAAW,UAAU;AAClD,aAAO;AAAA,IACT;AAGA,UAAM,QAAS,KAAK,SAA4C,CAAC;AACjE,UAAM,iBAAyB,MAAM,IAAI,CAAC,SAAS;AACjD,YAAM,OAAO,KAAK;AAGlB,UAAI,KAAK,SAAS,GAAG,GAAG;AACtB,eAAO;AAAA,MACT;AAGA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM,GAAG,aAAa,IAAI,IAAI;AAAA,MAChC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,MAAO,KAAK,QAAmB;AAAA,MAC/B,QAAQ;AAAA,MACR,cAAc;AAAA,QACZ,SAAS,KAAK,WAAW;AAAA,QACzB,UAAU,KAAK,YAAY,EAAE,OAAO,MAAM,QAAQ,IAAI;AAAA,QACtD,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,OAAO;AAAA,MACP,UAAU;AAAA,QACR,YAAY,KAAK;AAAA,QACjB,SAAU,KAAK,WAAsB;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eACJ,YACA,QACA,UAAyB,CAAC,GACA;AAC1B,UAAM,SAAS,KAAK,IAAI,UAAU;AAElC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,WAAW,UAAU,aAAa;AAAA,IACpD;AAEA,WAAO,OAAO,SAAS,MAAM,QAAQ,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,MACJ,YACA,QACA,UAAwB,CAAC,GACL;AACpB,UAAM,SAAS,KAAK,IAAI,UAAU;AAElC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,WAAW,UAAU,aAAa;AAAA,IACpD;AAEA,QAAI,CAAC,OAAO,SAAS,OAAO;AAC1B,YAAM,IAAI;AAAA,QACR,WAAW,UAAU;AAAA,MACvB;AAAA,IACF;AAEA,WAAO,OAAO,SAAS,MAAM,QAAQ,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QACJ,SACAA,gBACA,UAAsB,CAAC,GACH;AACpB,UAAM,SAAS,KAAK,cAAc,OAAO;AACzC,UAAM,WAAsB,CAAC;AAC7B,UAAM,SAAS,KAAK,aAAa,OAAO,IAAI;AAE5C,UAAM,MAAkB;AAAA,MACtB;AAAA,MACA,eAAAA;AAAA,MACA,UAAUA,eAAc,YAAY;AAAA,MACpC;AAAA,MACA,YAAY,CAAC,YAAY;AACvB,iBAAS,KAAK,OAAO;AACrB,QAAAA,eAAc,WAAW,OAAO;AAChC,gBAAQ,YAAY,OAAO;AAAA,MAC7B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,UAAM,YAAY;AAAA,MAChB;AAAA,MACA,MAAM;AAAA,MACN,UAAU,CAAC,CAAE,QAAoC;AAAA,MACjD,QAAQ,CAAC;AAAA,MACT,QAAQ,EAAE,SAAS,SAAS,kBAAkB,EAAE;AAAA,MAChD,UAAUA,eAAc,YAAY;AAAA,MACpC;AAAA,MACA;AAAA,MACA,OAAO,WAAW;AAAA,IACpB;AAGA,eAAW,UAAUA,eAAc,WAAW,GAAG;AAC/C,UAAI,OAAO,WAAW,OAAO,OAAO,OAAO,YAAY;AACrD,YAAI;AACF,gBAAM,OAAO,OAAO,MAAM,WAAW;AAAA,YACnC,GAAG;AAAA,YACH,QAAQ,OAAO;AAAA,UACjB,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,iBAAO,KAAK,UAAU,OAAO,OAAO,IAAI,uBAAuB,GAAG,EAAE;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAGA,QAAI,SAAS,MAAM,OAAO,OAAO,QAAQ,SAAS,GAAG;AAGrD,eAAW,UAAUA,eAAc,WAAW,GAAG;AAC/C,UAAI,OAAO,WAAW,OAAO,OAAO,OAAO,UAAU;AACnD,YAAI;AACF,mBAAS,MAAM,OAAO,OAAO,MAAM,SAAS,QAAQ;AAAA,YAClD,GAAG;AAAA,YACH,QAAQ,OAAO;AAAA,YACf,UAAU,OAAO;AAAA,UACnB,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,iBAAO,KAAK,UAAU,OAAO,OAAO,IAAI,qBAAqB,GAAG,EAAE;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAAgD;AACrE,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,IAAI;AAEV,QAAI,OAAO,EAAE,SAAS,YAAY,CAAC,EAAE,MAAM;AACzC,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,QAAI,OAAO,EAAE,YAAY,YAAY,CAAC,EAAE,SAAS;AAC/C,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAEA,QAAI,CAAC,MAAM,QAAQ,EAAE,SAAS,KAAK,EAAE,UAAU,WAAW,GAAG;AAC3D,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AAEA,QAAI,CAAC,EAAE,YAAY,OAAO,EAAE,aAAa,UAAU;AACjD,YAAM,IAAI,MAAM,6BAA6B;AAAA,IAC/C;AAEA,QAAI,CAAC,EAAE,UAAU,OAAO,EAAE,WAAW,UAAU;AAC7C,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,MAA4B;AAC/C,UAAM,SAAS,WAAW,IAAI;AAC9B,WAAO;AAAA,MACL,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,MAC3D,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,MACzD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,MACzD,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,IAC7D;AAAA,EACF;AACF;AAKO,IAAM,gBAAgB,IAAI,cAAc;;;AC/UxC,IAAM,qBAAqB;;;ACdlC,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,WAAAC,UAAS,cAAAC,mBAAkB;AACpC,OAAO,UAAU;AACjB,SAAS,SAAS;;;ACmBX,IAAM,qBAAqB;;;ADFlC,IAAM,iBAAiB;AAKvB,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACjC,SAAS,EAAE,OAAO,EAAE,QAAQ,GAAG;AAAA,EAC/B,SAAS,EACN;AAAA,IACC,EAAE,OAAO;AAAA,MACP,MAAM,EAAE,OAAO;AAAA,MACf,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS;AAAA,MACvC,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IACnC,CAAC;AAAA,EACH,EACC,SAAS;AAAA,EACZ,UAAU,EACP,OAAO;AAAA,IACN,SAAS,EAAE,KAAK,CAAC,YAAY,WAAW,QAAQ,CAAC,EAAE,SAAS;AAAA,IAC5D,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,IAC/B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,CAAC,EACA,SAAS;AACd,CAAC;AAKM,IAAM,gBAAN,MAAoB;AAAA,EACjB,UAA0B,CAAC;AAAA,EAC3B,SAA6B;AAAA,EAC7B,cAAc;AAAA;AAAA;AAAA;AAAA,EAKd,iBAAmC,CAAC;AAAA,EACpC,iBAA4B,CAAC;AAAA;AAAA;AAAA;AAAA,EAKrC,MAAM,WAAW,YAA2C;AAC1D,UAAM,QAAQ,aACV,CAAC,UAAU,IACX;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEJ,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAWC,YAAW,IAAI,IAAI,OAAOC,SAAQ,QAAQ,IAAI,GAAG,IAAI;AACtE,UAAI,WAAW,QAAQ,GAAG;AACxB,cAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,cAAM,SAAS,KAAK,SAAS,OAAO,IAChC,KAAK,MAAM,OAAO,IAClB,KAAK,MAAM,OAAO;AACtB,aAAK,SAAS,kBAAkB,MAAM,MAAM;AAC5C,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAGA,SAAK,SAAS,EAAE,SAAS,KAAK,SAAS,CAAC,GAAG,UAAU,CAAC,EAAE;AACxD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cAA6B;AACjC,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,KAAK,WAAW;AAAA,IACxB;AAEA,UAAM,gBAAgB,KAAK,QAAQ,WAAW,CAAC;AAE/C,eAAW,gBAAgB,eAAe;AACxC,UAAI,aAAa,YAAY,MAAO;AAEpC,UAAI;AACF,cAAM,SAAS,MAAM,KAAK,WAAW,YAAY;AACjD,aAAK,QAAQ,KAAK,MAAM;AAAA,MAC1B,SAAS,KAAK;AACZ,gBAAQ;AAAA,UACN,yBAAyB,aAAa,IAAI;AAAA,UAC1C,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAW,QAA6C;AACpE,UAAM,EAAE,MAAM,QAAQ,eAAe,CAAC,EAAE,IAAI;AAC5C,QAAI;AACJ,QAAI;AAGJ,QAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,KAAKD,YAAW,IAAI,GAAG;AAEvE,YAAM,WAAWA,YAAW,IAAI,IAAI,OAAOC,SAAQ,QAAQ,IAAI,GAAG,IAAI;AACtE,YAAM,SAAS,MAAM,OAAO;AAC5B,eAAS,OAAO,WAAW;AAC3B,eAAS;AAAA,IACX,WAAW,KAAK,WAAW,SAAS,GAAG;AAErC,YAAM,SAAS,MAAM,OAAO;AAC5B,eAAS,OAAO,WAAW;AAC3B,eAAS;AAAA,IACX,OAAO;AAEL,YAAM,SAAS,MAAM,OAAO;AAC5B,eAAS,OAAO,WAAW;AAC3B,eAAS;AAAA,IACX;AAGA,SAAK,eAAe,MAAM;AAG1B,QAAI,iBAAiB;AACrB,QAAI,OAAO,cAAc;AACvB,UAAI;AACF,yBAAiB,OAAO,aAAa,MAAM,YAAY;AAAA,MACzD,SAAS,KAAK;AACZ,cAAM,IAAI;AAAA,UACR,6BAA6B,IAAI,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAAgD;AACrE,QAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,SAAS,YAAY,CAAC,EAAE,MAAM;AACzC,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AACA,QAAI,OAAO,EAAE,YAAY,YAAY,CAAC,EAAE,SAAS;AAC/C,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,aAAc,EAAE,cAAyB;AAC/C,QAAI,aAAa,oBAAoB;AACnC,YAAM,IAAI;AAAA,QACR,+BAA+B,UAAU,yBAAyB,kBAAkB;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,QAAqB,SAAkC,CAAC,GAAS;AACzE,SAAK,eAAe,MAAM;AAC1B,SAAK,QAAQ,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAA4B;AAChC,QAAI,KAAK,YAAa;AAGtB,eAAW,UAAU,KAAK,SAAS;AACjC,UAAI,OAAO,OAAO,UAAU;AAC1B,cAAM,WACJ,OAAO,OAAO,OAAO,aAAa,aAC9B,MAAM,OAAO,OAAO,SAAS,IAC7B,OAAO,OAAO;AACpB,aAAK,eAAe,KAAK,GAAG,QAAQ;AAAA,MACtC;AAAA,IACF;AAGA,UAAM,KAAK,SAAS,UAAU,CAAC,MAAM,QAAQ,KAAK,GAAG,CAAC;AAEtD,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,UAAM,KAAK,SAAS,aAAa,CAAC,MAAM,QAAQ,KAAK,GAAG,CAAC;AACzD,SAAK,UAAU,CAAC;AAChB,SAAK,iBAAiB,CAAC;AACvB,SAAK,iBAAiB,CAAC;AACvB,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,SAAwB;AACjC,SAAK,eAAe,KAAK,OAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,UAAkC;AAC5C,SAAK,eAAe,KAAK,GAAG,QAAQ;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAsB;AACpB,SAAK,iBAAiB,CAAC;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,aAA6B;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,MAAuB;AAC/B,WAAO,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS,IAAI;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,cAAsD;AAClE,UAAM,aAAyB;AAAA,MAC7B,SAAS;AAAA,MACT,kBAAkB;AAAA,IACpB;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,QAAQ,KAAK,aAAa,QAAQ;AAAA,MAClC,OAAO,WAAW;AAAA,IACpB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,MAA4B;AAC/C,UAAM,SAAS,IAAI,IAAI;AACvB,WAAO;AAAA,MACL,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,MAC3D,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,MACzD,MAAM,CAAC,QAAQ,SAAS,QAAQ,KAAK,QAAQ,KAAK,GAAG,IAAI;AAAA,MACzD,OAAO,CAAC,QAAQ,SAAS,QAAQ,MAAM,QAAQ,KAAK,GAAG,IAAI;AAAA,IAC7D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SACJ,UACA,UAIe;AACf,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,OAAO,OAAO,OAAO,QAAQ,QAAQ;AAC3C,UAAI,MAAM;AACR,cAAM,MAAM,KAAK,cAAc,OAAO,MAAM;AAC5C,YAAI,SAAS,KAAK,aAAa,OAAO,OAAO,IAAI;AACjD,YAAI;AACF,gBAAM,SAAS,MAAqC,GAAG;AAAA,QACzD,SAAS,KAAK;AACZ,kBAAQ;AAAA,YACN,mBAAmB,OAAO,OAAO,IAAI,IAAI,QAAQ;AAAA,YACjD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,UACA,UAIc;AACd,UAAM,UAAe,CAAC;AAEtB,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,OAAO,OAAO,OAAO,QAAQ,QAAQ;AAC3C,UAAI,MAAM;AACR,cAAM,MAAM,KAAK,cAAc,OAAO,MAAM;AAC5C,YAAI,SAAS,KAAK,aAAa,OAAO,OAAO,IAAI;AACjD,YAAI;AACF,gBAAM,SAAS,MAAM;AAAA,YACnB;AAAA,YACA;AAAA,UACF;AACA,cAAI,WAAW,QAAQ,WAAW,QAAW;AAC3C,gBAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,sBAAQ,KAAK,GAAG,MAAM;AAAA,YACxB,OAAO;AACL,sBAAQ,KAAK,MAAM;AAAA,YACrB;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ;AAAA,YACN,mBAAmB,OAAO,OAAO,IAAI,IAAI,QAAQ;AAAA,YACjD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,UACA,SACA,UAKY;AACZ,QAAI,QAAQ;AAEZ,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,OAAO,OAAO,OAAO,QAAQ,QAAQ;AAC3C,UAAI,MAAM;AACR,cAAM,MAAM,KAAK,cAAc,OAAO,MAAM;AAC5C,YAAI,SAAS,KAAK,aAAa,OAAO,OAAO,IAAI;AACjD,YAAI;AACF,kBAAQ,MAAM;AAAA,YACZ;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF,SAAS,KAAK;AACZ,kBAAQ;AAAA,YACN,mBAAmB,OAAO,OAAO,IAAI,IAAI,QAAQ;AAAA,YACjD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,IAAM,gBAAgB,IAAI,cAAc;","names":["pluginManager","resolve","isAbsolute","isAbsolute","resolve"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vulcn/engine",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Fast, modern security testing engine — record browser sessions, replay with attack payloads, and detect vulnerabilities automatically. Pluggable driver and detection system for web application penetration testing.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",