@vulcn/engine 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Page, Browser } from 'playwright';
1
+ import { Page, Dialog, ConsoleMessage, Request, Response, Browser } from 'playwright';
2
2
  import { z } from 'zod';
3
3
 
4
4
  declare const StepSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
@@ -417,6 +417,53 @@ declare function parseSession(yaml: string): Session;
417
417
  */
418
418
  declare function serializeSession(session: Session): string;
419
419
 
420
+ /**
421
+ * Payload Types for Vulcn
422
+ * Core types used by the engine and plugins
423
+ */
424
+ /**
425
+ * Valid payload categories
426
+ */
427
+ type PayloadCategory = "xss" | "sqli" | "ssrf" | "xxe" | "command-injection" | "path-traversal" | "open-redirect" | "reflection" | "custom";
428
+ /**
429
+ * Payload source types
430
+ */
431
+ type PayloadSource = "builtin" | "custom" | "payloadbox" | "plugin";
432
+ /**
433
+ * Runtime payload structure - used by plugins and the runner
434
+ */
435
+ interface RuntimePayload {
436
+ /** Unique payload name */
437
+ name: string;
438
+ /** Vulnerability category */
439
+ category: PayloadCategory;
440
+ /** Human-readable description */
441
+ description: string;
442
+ /** Array of payload strings to inject */
443
+ payloads: string[];
444
+ /** Patterns to detect vulnerability (as RegExp) */
445
+ detectPatterns: RegExp[];
446
+ /** Where this payload came from */
447
+ source: PayloadSource;
448
+ }
449
+ /**
450
+ * Custom payload schema for YAML/JSON files (used by loader plugins)
451
+ */
452
+ interface CustomPayload {
453
+ name: string;
454
+ category: PayloadCategory;
455
+ description?: string;
456
+ payloads: string[];
457
+ detectPatterns?: string[];
458
+ }
459
+ /**
460
+ * Custom payload file schema
461
+ */
462
+ interface CustomPayloadFile {
463
+ version?: string;
464
+ payloads: CustomPayload[];
465
+ }
466
+
420
467
  type BrowserType = "chromium" | "firefox" | "webkit";
421
468
  interface RecorderOptions {
422
469
  browser?: BrowserType;
@@ -432,7 +479,7 @@ interface RunnerOptions {
432
479
  onFinding?: (finding: Finding) => void;
433
480
  }
434
481
  interface Finding {
435
- type: "xss" | "sqli" | "ssrf" | "path-traversal" | "custom";
482
+ type: PayloadCategory;
436
483
  severity: "critical" | "high" | "medium" | "low" | "info";
437
484
  title: string;
438
485
  description: string;
@@ -440,6 +487,8 @@ interface Finding {
440
487
  payload: string;
441
488
  url: string;
442
489
  evidence?: string;
490
+ /** Plugin-specific metadata */
491
+ metadata?: Record<string, unknown>;
443
492
  }
444
493
  interface RunResult {
445
494
  findings: Finding[];
@@ -449,6 +498,302 @@ interface RunResult {
449
498
  errors: string[];
450
499
  }
451
500
 
501
+ /**
502
+ * Vulcn Plugin System Types
503
+ * @module @vulcn/engine/plugin
504
+ */
505
+
506
+ /**
507
+ * Plugin API version - plugins declare compatibility
508
+ */
509
+ declare const PLUGIN_API_VERSION = 1;
510
+ /**
511
+ * Plugin source types for identification
512
+ */
513
+ type PluginSource = "builtin" | "npm" | "local" | "custom";
514
+ /**
515
+ * Main plugin interface
516
+ */
517
+ interface VulcnPlugin {
518
+ /** Unique plugin name (e.g., "@vulcn/plugin-payloads") */
519
+ name: string;
520
+ /** Plugin version (semver) */
521
+ version: string;
522
+ /** Plugin API version this plugin targets */
523
+ apiVersion?: number;
524
+ /** Human-readable description */
525
+ description?: string;
526
+ /** Lifecycle hooks */
527
+ hooks?: PluginHooks;
528
+ /**
529
+ * Payloads provided by this plugin (Loaders)
530
+ * Can be static array or async function for lazy loading
531
+ */
532
+ payloads?: RuntimePayload[] | (() => Promise<RuntimePayload[]>);
533
+ /**
534
+ * Zod schema for plugin configuration validation
535
+ */
536
+ configSchema?: z.ZodSchema;
537
+ }
538
+ /**
539
+ * Plugin lifecycle hooks
540
+ */
541
+ interface PluginHooks {
542
+ /**
543
+ * Called when plugin is loaded, before any operation
544
+ * Use for setup, loading payloads, etc.
545
+ */
546
+ onInit?: (ctx: PluginContext) => Promise<void>;
547
+ /**
548
+ * Called when plugin is unloaded/cleanup
549
+ */
550
+ onDestroy?: (ctx: PluginContext) => Promise<void>;
551
+ /** Called when recording starts */
552
+ onRecordStart?: (ctx: RecordContext) => Promise<void>;
553
+ /** Called for each recorded step, can transform */
554
+ onRecordStep?: (step: Step, ctx: RecordContext) => Promise<Step>;
555
+ /** Called when recording ends, can transform session */
556
+ onRecordEnd?: (session: Session, ctx: RecordContext) => Promise<Session>;
557
+ /** Called when run starts */
558
+ onRunStart?: (ctx: RunContext) => Promise<void>;
559
+ /** Called before each payload is injected, can transform payload */
560
+ onBeforePayload?: (payload: string, step: Step, ctx: RunContext) => Promise<string>;
561
+ /** Called after payload injection, for detection */
562
+ onAfterPayload?: (ctx: DetectContext) => Promise<Finding[]>;
563
+ /** Called when run ends, can transform results */
564
+ onRunEnd?: (result: RunResult, ctx: RunContext) => Promise<RunResult>;
565
+ /** Called on page load/navigation */
566
+ onPageLoad?: (page: Page, ctx: DetectContext) => Promise<Finding[]>;
567
+ /** Called when JavaScript alert/confirm/prompt appears */
568
+ onDialog?: (dialog: Dialog, ctx: DetectContext) => Promise<Finding | null>;
569
+ /** Called on console.log/warn/error */
570
+ onConsoleMessage?: (msg: ConsoleMessage, ctx: DetectContext) => Promise<Finding | null>;
571
+ /** Called on network request */
572
+ onNetworkRequest?: (request: Request, ctx: DetectContext) => Promise<Finding | null>;
573
+ /** Called on network response */
574
+ onNetworkResponse?: (response: Response, ctx: DetectContext) => Promise<Finding | null>;
575
+ }
576
+ /**
577
+ * Logger interface for plugins
578
+ */
579
+ interface PluginLogger {
580
+ debug: (msg: string, ...args: unknown[]) => void;
581
+ info: (msg: string, ...args: unknown[]) => void;
582
+ warn: (msg: string, ...args: unknown[]) => void;
583
+ error: (msg: string, ...args: unknown[]) => void;
584
+ }
585
+ /**
586
+ * Engine information exposed to plugins
587
+ */
588
+ interface EngineInfo {
589
+ version: string;
590
+ pluginApiVersion: number;
591
+ }
592
+ /**
593
+ * Base context available to all plugin hooks
594
+ */
595
+ interface PluginContext {
596
+ /** Plugin-specific config from vulcn.config.yml */
597
+ config: Record<string, unknown>;
598
+ /** Engine information */
599
+ engine: EngineInfo;
600
+ /** Shared payload registry - loaders add payloads here */
601
+ payloads: RuntimePayload[];
602
+ /** Shared findings collection - detectors add findings here */
603
+ findings: Finding[];
604
+ /** Scoped logger */
605
+ logger: PluginLogger;
606
+ /** Fetch API for network requests */
607
+ fetch: typeof fetch;
608
+ }
609
+ /**
610
+ * Context for recording phase hooks
611
+ */
612
+ interface RecordContext extends PluginContext {
613
+ /** Starting URL */
614
+ startUrl: string;
615
+ /** Browser type being used */
616
+ browser: BrowserType;
617
+ /** Playwright page instance */
618
+ page: Page;
619
+ }
620
+ /**
621
+ * Context for running phase hooks
622
+ */
623
+ interface RunContext extends PluginContext {
624
+ /** Session being executed */
625
+ session: Session;
626
+ /** Playwright page instance */
627
+ page: Page;
628
+ /** Browser type being used */
629
+ browser: BrowserType;
630
+ /** Whether running headless */
631
+ headless: boolean;
632
+ }
633
+ /**
634
+ * Context for detection hooks
635
+ */
636
+ interface DetectContext extends RunContext {
637
+ /** Current step being tested */
638
+ step: Step;
639
+ /** Current payload set being tested */
640
+ payloadSet: RuntimePayload;
641
+ /** Actual payload value injected */
642
+ payloadValue: string;
643
+ /** Step ID for reporting */
644
+ stepId: string;
645
+ }
646
+ /**
647
+ * Plugin configuration in vulcn.config.yml
648
+ */
649
+ interface PluginConfig {
650
+ /** Plugin name/path */
651
+ name: string;
652
+ /** Plugin-specific configuration */
653
+ config?: Record<string, unknown>;
654
+ /** Whether plugin is enabled (default: true) */
655
+ enabled?: boolean;
656
+ }
657
+ /**
658
+ * Vulcn configuration file schema
659
+ */
660
+ interface VulcnConfig {
661
+ /** Config version */
662
+ version: string;
663
+ /** Plugins to load */
664
+ plugins?: PluginConfig[];
665
+ /** Global settings */
666
+ settings?: {
667
+ browser?: BrowserType;
668
+ headless?: boolean;
669
+ timeout?: number;
670
+ };
671
+ }
672
+ /**
673
+ * Loaded plugin instance with resolved config
674
+ */
675
+ interface LoadedPlugin {
676
+ /** Plugin definition */
677
+ plugin: VulcnPlugin;
678
+ /** Resolved configuration */
679
+ config: Record<string, unknown>;
680
+ /** Source of the plugin */
681
+ source: PluginSource;
682
+ /** Whether plugin is enabled */
683
+ enabled: boolean;
684
+ }
685
+
686
+ /**
687
+ * Vulcn Plugin Manager
688
+ * Handles plugin loading, lifecycle, and hook execution
689
+ */
690
+
691
+ /**
692
+ * Plugin Manager - loads, configures, and orchestrates plugins
693
+ */
694
+ declare class PluginManager {
695
+ private plugins;
696
+ private config;
697
+ private initialized;
698
+ /**
699
+ * Shared context passed to all plugins
700
+ */
701
+ private sharedPayloads;
702
+ private sharedFindings;
703
+ /**
704
+ * Load configuration from vulcn.config.yml
705
+ */
706
+ loadConfig(configPath?: string): Promise<VulcnConfig>;
707
+ /**
708
+ * Load all plugins from config
709
+ */
710
+ loadPlugins(): Promise<void>;
711
+ /**
712
+ * Load a single plugin
713
+ */
714
+ private loadPlugin;
715
+ /**
716
+ * Validate plugin structure
717
+ */
718
+ private validatePlugin;
719
+ /**
720
+ * Add a plugin programmatically (for testing or dynamic loading)
721
+ */
722
+ addPlugin(plugin: VulcnPlugin, config?: Record<string, unknown>): void;
723
+ /**
724
+ * Initialize all plugins (call onInit hooks)
725
+ */
726
+ initialize(): Promise<void>;
727
+ /**
728
+ * Destroy all plugins (call onDestroy hooks)
729
+ */
730
+ destroy(): Promise<void>;
731
+ /**
732
+ * Get all loaded payloads
733
+ */
734
+ getPayloads(): RuntimePayload[];
735
+ /**
736
+ * Get all collected findings
737
+ */
738
+ getFindings(): Finding[];
739
+ /**
740
+ * Add a finding (used by detectors)
741
+ */
742
+ addFinding(finding: Finding): void;
743
+ /**
744
+ * Add payloads (used by loaders)
745
+ */
746
+ addPayloads(payloads: RuntimePayload[]): void;
747
+ /**
748
+ * Clear findings (for new run)
749
+ */
750
+ clearFindings(): void;
751
+ /**
752
+ * Get loaded plugins
753
+ */
754
+ getPlugins(): LoadedPlugin[];
755
+ /**
756
+ * Check if a plugin is loaded by name
757
+ */
758
+ hasPlugin(name: string): boolean;
759
+ /**
760
+ * Create base context for plugins
761
+ */
762
+ createContext(pluginConfig: Record<string, unknown>): PluginContext;
763
+ /**
764
+ * Create scoped logger for a plugin
765
+ */
766
+ private createLogger;
767
+ /**
768
+ * Call a hook on all plugins sequentially
769
+ */
770
+ callHook<K extends keyof PluginHooks>(hookName: K, executor: (hook: NonNullable<PluginHooks[K]>, ctx: PluginContext) => Promise<unknown>): Promise<void>;
771
+ /**
772
+ * Call a hook and collect results
773
+ */
774
+ callHookCollect<K extends keyof PluginHooks, R>(hookName: K, executor: (hook: NonNullable<PluginHooks[K]>, ctx: PluginContext) => Promise<R | R[] | null>): Promise<R[]>;
775
+ /**
776
+ * Call a hook that transforms a value through the pipeline
777
+ */
778
+ callHookPipe<T>(hookName: keyof PluginHooks, initial: T, executor: (hook: NonNullable<PluginHooks[typeof hookName]>, value: T, ctx: PluginContext) => Promise<T>): Promise<T>;
779
+ }
780
+ /**
781
+ * Default shared plugin manager instance
782
+ */
783
+ declare const pluginManager: PluginManager;
784
+
785
+ /**
786
+ * Recorder - captures browser interactions as a replayable session
787
+ * v0.2.0: Plugin hooks for recording customization
788
+ */
789
+
790
+ /**
791
+ * Configuration for the recorder
792
+ */
793
+ interface RecorderConfig {
794
+ /** Plugin manager to use (defaults to shared instance) */
795
+ pluginManager?: PluginManager;
796
+ }
452
797
  /**
453
798
  * Active recording session handle
454
799
  */
@@ -462,53 +807,70 @@ interface RecordingSession {
462
807
  }
463
808
  /**
464
809
  * Recorder - captures browser interactions as a replayable session
810
+ *
811
+ * Uses plugin hooks for:
812
+ * - onRecordStart: Called when recording starts
813
+ * - onRecordStep: Called for each step, can transform
814
+ * - onRecordEnd: Called when recording ends, can transform session
465
815
  */
466
816
  declare class Recorder {
467
817
  /**
468
818
  * Start a new recording session
469
819
  * Opens a browser window for the user to interact with
470
820
  */
471
- static start(startUrl: string, options?: RecorderOptions): Promise<RecordingSession>;
821
+ static start(startUrl: string, options?: RecorderOptions, config?: RecorderConfig): Promise<RecordingSession>;
822
+ /**
823
+ * Transform a step through plugin hooks
824
+ * Returns null if the step should be filtered out
825
+ */
826
+ private static transformStep;
472
827
  private static attachListeners;
473
828
  private static injectRecordingScript;
474
829
  }
475
830
 
476
831
  /**
477
- * Built-in security payloads
478
- */
479
- type PayloadCategory = "xss" | "sqli" | "ssrf" | "path-traversal";
480
- type PayloadName = "xss-basic" | "xss-event" | "xss-svg" | "sqli-basic" | "sqli-error" | "sqli-blind";
481
- interface Payload {
482
- name: PayloadName;
483
- category: PayloadCategory;
484
- payloads: string[];
485
- detectPatterns: RegExp[];
486
- description: string;
487
- }
488
- declare const BUILTIN_PAYLOADS: Record<PayloadName, Payload>;
489
- /**
490
- * Get payloads by name
491
- */
492
- declare function getPayload(name: PayloadName): Payload | undefined;
493
- /**
494
- * Get all payload names
495
- */
496
- declare function getPayloadNames(): PayloadName[];
497
- /**
498
- * Get payloads by category
832
+ * Runner - replays sessions with security payloads
833
+ * v0.2.0: Plugin-based architecture for extensibility
499
834
  */
500
- declare function getPayloadsByCategory(category: PayloadCategory): Payload[];
501
835
 
836
+ interface RunnerConfig {
837
+ /** Plugin manager to use (defaults to shared instance) */
838
+ pluginManager?: PluginManager;
839
+ }
502
840
  /**
503
841
  * Runner - replays sessions with security payloads
842
+ *
843
+ * Uses plugin hooks for:
844
+ * - Payload loading (onInit)
845
+ * - Payload transformation (onBeforePayload)
846
+ * - Vulnerability detection (onAfterPayload, onDialog, onConsoleMessage, etc.)
847
+ * - Results processing (onRunEnd)
504
848
  */
505
849
  declare class Runner {
506
850
  /**
507
- * Execute a session with security payloads
851
+ * Execute a session with security payloads from plugins
852
+ *
853
+ * @param session - The recorded session to replay
854
+ * @param options - Runner configuration
855
+ * @param config - Plugin manager configuration
856
+ */
857
+ static execute(session: Session, options?: RunnerOptions, config?: RunnerConfig): Promise<RunResult>;
858
+ /**
859
+ * Execute with explicit payloads (legacy API, for backwards compatibility)
860
+ */
861
+ static executeWithPayloads(session: Session, payloads: RuntimePayload[], options?: RunnerOptions): Promise<RunResult>;
862
+ /**
863
+ * Replay session steps with payload injected at target step
508
864
  */
509
- static execute(session: Session, payloadNames: PayloadName[], options?: RunnerOptions): Promise<RunResult>;
510
865
  private static replayWithPayload;
511
- private static checkForVulnerability;
866
+ /**
867
+ * Basic reflection check - fallback when no detection plugin is loaded
868
+ */
869
+ private static checkReflection;
870
+ /**
871
+ * Determine severity based on vulnerability category
872
+ */
873
+ private static getSeverity;
512
874
  }
513
875
 
514
876
  interface LaunchOptions {
@@ -543,4 +905,4 @@ declare function checkBrowsers(): Promise<{
543
905
  playwrightWebkit: boolean;
544
906
  }>;
545
907
 
546
- export { BUILTIN_PAYLOADS, type BrowserLaunchResult, BrowserNotFoundError, type BrowserType, type Finding, type LaunchOptions, type Payload, type PayloadCategory, type PayloadName, Recorder, type RecorderOptions, type RecordingSession, type RunResult, Runner, type RunnerOptions, type Session, SessionSchema, type Step, StepSchema, checkBrowsers, createSession, getPayload, getPayloadNames, getPayloadsByCategory, installBrowsers, launchBrowser, parseSession, serializeSession };
908
+ export { type BrowserLaunchResult, BrowserNotFoundError, type BrowserType, type CustomPayload, type CustomPayloadFile, type DetectContext, type EngineInfo, type Finding, type LaunchOptions, type LoadedPlugin, PLUGIN_API_VERSION, type PayloadCategory, type PayloadSource, type PluginConfig, type PluginContext, type PluginHooks, type PluginLogger, PluginManager, type PluginSource, type RecordContext, Recorder, type RecorderOptions, type RecordingSession, type RunContext, type RunResult, Runner, type RunnerOptions, type RuntimePayload, type Session, SessionSchema, type Step, StepSchema, type VulcnConfig, type VulcnPlugin, checkBrowsers, createSession, installBrowsers, launchBrowser, parseSession, pluginManager, serializeSession };