Plugin Architecture Overview

Plugin Architecture Overview

The following files were used as context for generating this wiki page:

Purpose and Scope

This document describes WSHawk's extensible plugin architecture, which enables custom payload collections, vulnerability detectors, and protocol handlers to be added without modifying core code. The plugin system implements lazy loading, metadata validation, version compatibility checking, and thread-safe registration.

For guides on creating specific plugin types, see:

Sources: wshawk/plugin_system.py:1-551, plugins/README.md:1-37


Plugin Type Hierarchy

WSHawk defines three plugin categories, all derived from the PluginBase abstract class:

| Plugin Type | Base Class | Primary Method | Purpose | |------------|------------|----------------|---------| | Payload | PayloadPlugin | get_payloads(vuln_type: str) | Provide custom attack vectors for specific vulnerability types | | Detector | DetectorPlugin | detect(response, payload, context) | Implement custom vulnerability detection logic | | Protocol | ProtocolPlugin | handle_message(message, context) | Handle non-standard WebSocket protocols (e.g., protobuf, msgpack) |

All plugins must implement get_metadata() which returns a PluginMetadata dataclass containing name, version, description, author, dependencies, and minimum WSHawk version requirements.

Sources: wshawk/plugin_system.py:35-97, plugins/README.md:5-10


PluginManager Architecture

Core Components

graph TB
    subgraph PluginManager["PluginManager"]
        Scanner["_scan_available_plugins()"]
        Loader["_load_plugin_lazy()"]
        Validator["_validate_plugin()"]
        Registry["_register_plugin_internal()"]
    end
    
    subgraph "Plugin Registries"
        PayloadReg["_payload_plugins: Dict[str, PayloadPlugin]"]
        DetectorReg["_detector_plugins: Dict[str, DetectorPlugin]"]
        ProtocolReg["_protocol_plugins: Dict[str, ProtocolPlugin]"]
    end
    
    subgraph "Metadata Tracking"
        MetaStore["_plugin_metadata: Dict[str, PluginMetadata]"]
        Checksums["_plugin_checksums: Dict[str, str]"]
        Available["_available_plugins: Dict[str, str]"]
        Loaded["_loaded_plugins: set"]
    end
    
    subgraph "Thread Safety"
        Lock["_lock: threading.Lock"]
    end
    
    subgraph "Caching"
        LRU["@lru_cache(maxsize=128)"]
    end
    
    Scanner -->|"discovers"| Available
    Available -->|"triggers"| Loader
    Loader -->|"validates via"| Validator
    Validator -->|"registers via"| Registry
    Registry -->|"populates"| PayloadReg
    Registry -->|"populates"| DetectorReg
    Registry -->|"populates"| ProtocolReg
    Registry -->|"stores"| MetaStore
    Lock -->|"protects"| Registry
    LRU -->|"accelerates"| PayloadReg

Diagram: PluginManager Component Architecture

The PluginManager class wshawk/plugin_system.py:98-438 manages the complete plugin lifecycle. Key architectural features:

  • Plugin Directories: plugin_dir (default: "plugins") for plugin source files, cache_dir (default: ".plugin_cache") for metadata caching
  • Lazy Loading Tracking: _available_plugins maps plugin names to file paths without loading them; _loaded_plugins tracks which have been imported
  • Type-Specific Registries: Separate dictionaries for each plugin type enable O(1) lookup and type safety
  • Metadata Store: _plugin_metadata preserves plugin information for introspection and validation
  • Checksum Validation: SHA-256 checksums in _plugin_checksums detect file modifications
  • Thread Safety: threading.Lock ensures thread-safe registration in concurrent environments
  • Override Policy: allow_override flag controls duplicate plugin handling

Sources: wshawk/plugin_system.py:98-135


Lazy Loading System

Lazy Loading Mechanism

sequenceDiagram
    participant Client
    participant PM as "PluginManager"
    participant FS as "Filesystem"
    participant Importer as "importlib"
    participant Plugin
    
    Note over PM: Initialization
    PM->>FS: _scan_available_plugins()
    FS-->>PM: List of .py files
    PM->>PM: Calculate checksums
    PM->>PM: Populate _available_plugins
    
    Note over Client,PM: Lazy Loading Request
    Client->>PM: get_payloads("xss")
    PM->>PM: Check _loaded_plugins
    alt Plugin not loaded
        PM->>PM: _load_plugin_lazy("xss_plugin")
        PM->>Importer: importlib.import_module()
        Importer->>Plugin: Import module
        Plugin-->>PM: Plugin class
        PM->>PM: _validate_plugin()
        PM->>PM: _register_plugin_internal()
        PM->>PM: Add to _loaded_plugins
    end
    PM->>Plugin: get_payloads("xss")
    Plugin-->>PM: List of payloads
    PM-->>Client: Return payloads

Diagram: Plugin Lazy Loading Sequence

The lazy loading algorithm wshawk/plugin_system.py:158-201 operates in three phases:

Phase 1: Discovery (Initialization)

  • _scan_available_plugins() wshawk/plugin_system.py:141-156 scans the plugin directory for .py files
  • Calculates SHA-256 checksums for integrity verification
  • Populates _available_plugins and _plugin_checksums without importing

Phase 2: On-Demand Loading

  • First access triggers _load_plugin_lazy(plugin_name) wshawk/plugin_system.py:158-201
  • Uses importlib.import_module() to dynamically import the plugin module
  • Introspects module for classes derived from PluginBase

Phase 3: Validation and Registration

This approach minimizes startup time and memory footprint, as plugins are only loaded when their functionality is required.

Sources: wshawk/plugin_system.py:141-201


Plugin Lifecycle

State Transitions

stateDiagram-v2
    [*] --> Available: _scan_available_plugins()
    Available --> Loading: _load_plugin_lazy()
    Loading --> Validating: importlib.import_module()
    Validating --> Registered: _validate_plugin() returns True
    Validating --> Failed: Validation fails
    Registered --> Active: First use
    Active --> Active: Cached access
    Failed --> [*]
    
    note right of Available
        File exists in plugin_dir
        Checksum calculated
    end note
    
    note right of Validating
        Check metadata fields
        Verify version format
        Check compatibility
    end note
    
    note right of Registered
        Added to type registry
        Metadata stored
        Marked as loaded
    end note

Diagram: Plugin Lifecycle State Machine

The plugin lifecycle consists of five states:

  1. Available: Plugin file detected in plugin_dir, checksum computed, but not yet loaded into memory
  2. Loading: importlib.import_module() executing, Python bytecode compilation in progress
  3. Validating: _validate_plugin() wshawk/plugin_system.py:203-235 verifying metadata structure, version format, and compatibility
  4. Registered: Plugin instance stored in appropriate registry (_payload_plugins, _detector_plugins, or _protocol_plugins)
  5. Active: Plugin methods being invoked by scanner, responses cached via @lru_cache

Failure Conditions:

  • Invalid metadata (missing name or version)
  • Malformed semver string
  • Incompatible min_wshawk_version
  • Duplicate registration when allow_override=False
  • Import exceptions during module loading

Sources: wshawk/plugin_system.py:158-313


Validation and Compatibility

Metadata Validation

The PluginMetadata dataclass wshawk/plugin_system.py:17-33 defines the plugin contract:

@dataclass
class PluginMetadata:
    name: str
    version: str
    description: str
    author: str = "Regaan"
    requires: List[str] = None
    min_wshawk_version: str = "2.0.0"
    checksum: str = ""

The _validate_plugin() method wshawk/plugin_system.py:203-235 enforces:

| Validation Check | Implementation | Failure Result | |-----------------|----------------|----------------| | Required Fields | Checks metadata.name and metadata.version are non-empty | Plugin rejected, error logged | | Semver Format | _is_valid_version() wshawk/plugin_system.py:237-243 verifies X.Y.Z format with numeric components | Plugin rejected | | Version Compatibility | _is_compatible_version() wshawk/plugin_system.py:245-261 checks major version match and minor version >= required | Plugin rejected with version mismatch error |

Version Compatibility Algorithm

The compatibility checker wshawk/plugin_system.py:245-261 implements semantic versioning rules:

  1. Major Version Match: Required major version must equal current major version (e.g., plugin requiring 2.x.x rejected on WSHawk 3.x.x)
  2. Minor Version Minimum: Current minor version must be >= required minor version (e.g., plugin requiring 2.5.0 accepted on WSHawk 2.6.0)
  3. Patch Version Ignored: Patch versions do not affect compatibility

Sources: wshawk/plugin_system.py:17-261


Security and Duplicate Handling

Duplicate Detection

flowchart TD
    Register["_register_plugin_internal()"]
    CheckType{"Plugin type?"}
    CheckDup{"Already<br/>registered?"}
    CheckOverride{"allow_override<br/>== True?"}
    Reject["Log ERROR<br/>Return False"]
    Override["Log WARNING<br/>Override existing"]
    Accept["Register plugin<br/>Log OK<br/>Return True"]
    
    Register --> CheckType
    CheckType -->|PayloadPlugin| CheckDup
    CheckType -->|DetectorPlugin| CheckDup
    CheckType -->|ProtocolPlugin| CheckDup
    
    CheckDup -->|No| Accept
    CheckDup -->|Yes| CheckOverride
    CheckOverride -->|No| Reject
    CheckOverride -->|Yes| Override
    Override --> Accept

Diagram: Duplicate Plugin Registration Flow

The _register_plugin_internal() method wshawk/plugin_system.py:263-313 implements defense against duplicate plugins:

Duplicate Detection Logic:

  • Checks if plugin name exists in type-specific registry (_payload_plugins, _detector_plugins, or _protocol_plugins)
  • If duplicate found and allow_override=False (default), registration fails with error log
  • If allow_override=True, existing plugin replaced with warning log

Security Implications:

  • Prevents malicious plugins from silently replacing legitimate ones
  • Explicit override policy enforces intentional replacement
  • Warning logs provide audit trail for plugin substitutions

Checksum Integrity:

  • SHA-256 checksums calculated during _scan_available_plugins() wshawk/plugin_system.py:152-153
  • Stored in _plugin_checksums for integrity verification
  • Enables detection of modified plugin files between scans

Sources: wshawk/plugin_system.py:263-313, wshawk/plugin_system.py:141-156


Thread Safety and Concurrency

The PluginManager implements thread-safe operations via threading.Lock wshawk/plugin_system.py:127:

Protected Critical Sections:

| Method | Lock Acquisition | Protected Resources | |--------|-----------------|---------------------| | _load_plugin_lazy() | with self._lock: wshawk/plugin_system.py:168 | _loaded_plugins, plugin registries | | register_plugin() | with self._lock: wshawk/plugin_system.py:330 | All registries, _plugin_metadata | | _register_plugin_internal() | Called within lock context | Type-specific registries |

Concurrency Scenarios:

  • Multiple scanner threads requesting same plugin type simultaneously
  • Parallel registration from different entry points
  • Concurrent metadata updates during plugin loading

The lock ensures that:

  1. Plugin loading is atomic (no partial registration)
  2. Duplicate checks are race-condition free
  3. Registry updates are serialized
  4. _loaded_plugins set remains consistent

Sources: wshawk/plugin_system.py:127, wshawk/plugin_system.py:168-201, wshawk/plugin_system.py:320-333


Caching Strategy

LRU Cache for Payload Retrieval

The get_payloads() method wshawk/plugin_system.py:335-373 uses @lru_cache(maxsize=128) to cache payload retrieval:

@lru_cache(maxsize=128)
def get_payloads(self, vuln_type: str, plugin_name: Optional[str] = None) -> List[str]:

Cache Behavior:

  • Cache Key: Tuple of (vuln_type, plugin_name)
  • Cache Size: 128 most recent unique queries
  • Eviction Policy: Least Recently Used (LRU) when cache full
  • Effectiveness: Eliminates repeated payload generation for same vulnerability types

Performance Benefits:

  • Payload Generation Cost: Plugins may load payloads from files, databases, or compute them dynamically
  • Scan Patterns: Scanners repeatedly access same vulnerability types across multiple targets
  • Memory Trade-off: 128-entry cache provides significant speedup with minimal memory overhead

Cache Invalidation:

  • LRU cache persists for PluginManager instance lifetime
  • New PluginManager instance creates fresh cache
  • Plugin hot-reload requires manager recreation

Sources: wshawk/plugin_system.py:335-373


Plugin Introspection API

The PluginManager provides methods for runtime introspection:

Plugin Listing

def list_plugins(self, loaded_only: bool = False) -> Dict:

wshawk/plugin_system.py:409-432

Returns:

{
  "available": ["plugin1", "plugin2"],
  "loaded": ["plugin1"],
  "payload_plugins": ["custom_xss"],
  "detector_plugins": ["nosql_detector_advanced"],
  "protocol_plugins": []
}

Plugin Metadata Retrieval

def get_plugin_info(self, plugin_name: str) -> Optional[Dict]:

wshawk/plugin_system.py:434-438

Returns:

{
  "name": "custom_xss",
  "version": "1.0.0",
  "description": "Custom XSS payloads for modern frameworks",
  "author": "WSHawk Team",
  "min_wshawk_version": "2.0.0",
  "checksum": ""
}

These APIs enable:

  • Dashboard Integration: Web UI displays loaded plugins and their metadata
  • Debugging: Developers can inspect plugin state during development
  • Audit Logging: Security teams can verify active plugins in production

Sources: wshawk/plugin_system.py:409-438


Example Plugin Implementations

The plugin system includes reference implementations demonstrating best practices:

CustomXSSPayloads (PayloadPlugin)

wshawk/plugin_system.py:443-465

  • Returns XSS payloads for modern frameworks
  • Demonstrates lazy loading pattern (payloads could be loaded from external file)
  • Proper metadata implementation with versioning

AdvancedNoSQLDetector (DetectorPlugin)

wshawk/plugin_system.py:467-494

  • Implements confidence-based detection (HIGH/MEDIUM/LOW)
  • Counts multiple indicators for accuracy
  • Returns structured detection results with descriptions
  • Demonstrates get_supported_types() for vulnerability type declaration

Both examples follow the plugin contract and pass validation checks, serving as templates for custom plugin development.

Sources: wshawk/plugin_system.py:443-494


Integration with Scanner

The plugin system integrates with WSHawk's core scanning engine through:

  1. Payload Augmentation: get_payloads(vuln_type) supplements static payloads from WSPayloads class (see Payload Management System)
  2. Detection Enhancement: run_detectors() wshawk/plugin_system.py:375-407 complements built-in VulnerabilityVerifier (see Analysis and Verification Modules)
  3. Protocol Extension: Protocol plugins handle non-standard message formats before scanning (see Creating Protocol Plugins)

The plugin system operates as an extension layer, allowing custom functionality without modifying core scanner code. All plugins execute within the same process space as the scanner, enabling full access to scan context and results.

Sources: wshawk/plugin_system.py:335-407, docs/DOCKER.md:1-240