Plugin System

Plugin System

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

Purpose and Scope

This document describes WSHawk's extensible plugin architecture that enables users to add custom payloads, vulnerability detectors, and protocol handlers without modifying core code. The plugin system provides lazy loading, validation, sandboxing, and versioning capabilities to ensure production-grade reliability and security.

For information about the core payload management system that plugins extend, see Payload Management System. For configuration of plugin directories and plugin-related settings, see Configuration System.

Sources: wshawk/plugin_system.py:1-10


Architecture Overview

The plugin system extends WSHawk's scanning capabilities through three primary plugin types, managed by a centralized PluginManager. The system employs lazy loading to minimize memory footprint and startup time, only loading plugins when their functionality is requested.

Plugin System Components

graph TB
    subgraph "Core Scanner Integration"
        Scanner["WSHawkV2<br/>scanner_v2.py"]
        WSPayloads["WSPayloads<br/>Static Payloads<br/>22,000+ vectors"]
    end
    
    subgraph "Plugin Manager"
        PM["PluginManager<br/>plugin_system.py:98-439"]
        Registry["Plugin Registries<br/>_payload_plugins<br/>_detector_plugins<br/>_protocol_plugins"]
        Metadata["Metadata Tracking<br/>_plugin_metadata<br/>_plugin_checksums"]
        Lazy["Lazy Loading<br/>_loaded_plugins<br/>_available_plugins"]
    end
    
    subgraph "Plugin Base Classes"
        PB["PluginBase<br/>ABC with get_metadata()"]
        PP["PayloadPlugin<br/>get_payloads(vuln_type)"]
        DP["DetectorPlugin<br/>detect(response, payload)"]
        ProtoP["ProtocolPlugin<br/>handle_message(msg)"]
    end
    
    subgraph "Plugin Directory"
        PluginDir["plugins/<br/>Custom .py files"]
        Example1["custom_xss.py"]
        Example2["nosql_detector.py"]
        Example3["protobuf_handler.py"]
    end
    
    subgraph "Validation & Security"
        Valid["Plugin Validation<br/>_validate_plugin()"]
        Version["Version Checking<br/>_is_compatible_version()"]
        Checksum["Integrity Check<br/>SHA-256 checksums"]
    end
    
    Scanner --> PM
    WSPayloads --> PM
    
    PM --> Registry
    PM --> Metadata
    PM --> Lazy
    PM --> Valid
    
    PB --> PP
    PB --> DP
    PB --> ProtoP
    
    PluginDir --> Example1
    PluginDir --> Example2
    PluginDir --> Example3
    
    Example1 -.->|"implements"| PP
    Example2 -.->|"implements"| DP
    Example3 -.->|"implements"| ProtoP
    
    PM -.->|"scans"| PluginDir
    PM -.->|"loads on demand"| Lazy
    
    Valid --> Version
    Valid --> Checksum

Diagram: Plugin System Architecture and Component Relationships

The PluginManager class serves as the central orchestrator, maintaining three separate registries for each plugin type. The system scans the plugins/ directory at initialization but defers actual module loading until plugin functionality is requested.

Sources: wshawk/plugin_system.py:98-134


Plugin Types and Base Classes

WSHawk defines three plugin types through abstract base classes, each serving a distinct extension point in the scanning pipeline.

Plugin Type Summary

| Plugin Type | Base Class | Primary Method | Use Case | |-------------|------------|----------------|----------| | Payload | PayloadPlugin | get_payloads(vuln_type: str) | Custom payload collections for specific vulnerability types | | Detector | DetectorPlugin | detect(response, payload, context) | Custom vulnerability detection logic and pattern matching | | Protocol | ProtocolPlugin | async handle_message(message, context) | Custom protocol handlers for non-standard WebSocket protocols |

Sources: wshawk/plugin_system.py:35-96

PluginBase Abstract Class

All plugins inherit from PluginBase and must implement get_metadata() to provide structured metadata:

classDiagram
    class PluginBase {
        <<abstract>>
        +get_metadata() PluginMetadata
        +get_name() str
        +get_version() str
        +get_description() str
    }
    
    class PluginMetadata {
        +name: str
        +version: str
        +description: str
        +author: str
        +requires: List[str]
        +min_wshawk_version: str
        +checksum: str
        +to_dict() Dict
        +from_dict(data) PluginMetadata
    }
    
    class PayloadPlugin {
        <<abstract>>
        +get_payloads(vuln_type: str) List[str]
        +get_payload_count(vuln_type: str) int
    }
    
    class DetectorPlugin {
        <<abstract>>
        +detect(response, payload, context) tuple
        +get_supported_types() List[str]
    }
    
    class ProtocolPlugin {
        <<abstract>>
        +handle_message(message, context) str
        +get_protocol_name() str
    }
    
    PluginBase <|-- PayloadPlugin
    PluginBase <|-- DetectorPlugin
    PluginBase <|-- ProtocolPlugin
    PluginBase --> PluginMetadata

Diagram: Plugin Class Hierarchy

The PluginMetadata dataclass wshawk/plugin_system.py:17-33 encapsulates versioning and compatibility information required for validation. The min_wshawk_version field enables forward compatibility checks.

Sources: wshawk/plugin_system.py:17-51

PayloadPlugin Interface

The PayloadPlugin class extends scanning capabilities with custom payload collections:

  • Primary Method: get_payloads(vuln_type: str) -> List[str] returns payloads for a specific vulnerability type (e.g., "xss", "sqli", "nosql")
  • Count Method: get_payload_count(vuln_type: str) -> int provides count without loading all payloads for optimization
  • Lazy Loading: Payloads are generated or loaded only when requested, supporting large payload files

Example implementation at wshawk/plugin_system.py:443-465 demonstrates a custom XSS payload pack.

Sources: wshawk/plugin_system.py:52-62, wshawk/plugin_system.py:443-465

DetectorPlugin Interface

The DetectorPlugin class enables custom vulnerability detection logic:

  • Primary Method: detect(response: str, payload: str, context: Dict = None) -> tuple[bool, str, str]
    • Returns: (is_vulnerable, confidence, description)
    • Confidence levels: "LOW", "MEDIUM", "HIGH"
    • Context dict may include headers, timing data, status codes
  • Type Declaration: get_supported_types() -> List[str] lists supported vulnerability types

Example implementation at wshawk/plugin_system.py:467-494 shows an advanced NoSQL injection detector with multi-indicator analysis.

Sources: wshawk/plugin_system.py:64-84, wshawk/plugin_system.py:467-494

ProtocolPlugin Interface

The ProtocolPlugin class handles non-standard WebSocket protocols:

  • Primary Method: async handle_message(message: str, context: Dict = None) -> str processes custom protocol messages
  • Protocol Name: get_protocol_name() -> str returns protocol identifier (e.g., "protobuf", "msgpack")
  • Async Support: Uses async/await for compatibility with WSHawk's async scanning engine

Sources: wshawk/plugin_system.py:86-96


PluginManager Lifecycle

The PluginManager class orchestrates plugin discovery, loading, validation, and execution through a multi-stage lifecycle.

Initialization and Discovery

sequenceDiagram
    participant User
    participant PM as "PluginManager"
    participant FS as "File System"
    participant Cache as "Plugin Cache"
    
    User->>PM: __init__(plugin_dir, cache_dir)
    PM->>FS: _ensure_directories()
    FS-->>PM: Created plugins/ and .plugin_cache/
    
    PM->>FS: _scan_available_plugins()
    FS->>FS: List .py files in plugins/
    
    loop For each plugin file
        FS->>FS: Read file bytes
        FS->>FS: Calculate SHA-256 checksum
        FS-->>PM: Plugin name, path, checksum
        PM->>PM: Store in _available_plugins
        PM->>PM: Store in _plugin_checksums
    end
    
    PM-->>User: Manager ready (plugins not loaded)

Diagram: PluginManager Initialization Sequence

During initialization wshawk/plugin_system.py:109-134, the manager:

  1. Creates plugins/ and .plugin_cache/ directories if missing
  2. Scans plugins/ directory for .py files (excluding _ prefixed files)
  3. Calculates SHA-256 checksums for integrity verification
  4. Stores plugin metadata without loading Python modules

This deferred loading approach minimizes startup time and memory usage.

Sources: wshawk/plugin_system.py:109-156

Lazy Loading Mechanism

Plugins are loaded on-demand when their functionality is requested:

flowchart TD
    Request["get_payloads(vuln_type)<br/>or run_detectors()"]
    CheckLoaded{"Plugin in<br/>_loaded_plugins?"}
    ReturnCached["Return from<br/>registry"]
    CheckAvailable{"Plugin in<br/>_available_plugins?"}
    LoadModule["Import module via<br/>importlib"]
    FindClasses["Scan module for<br/>PluginBase subclasses"]
    Validate["_validate_plugin()<br/>Check metadata"]
    Register["_register_plugin_internal()<br/>Add to registry"]
    MarkLoaded["Add to _loaded_plugins"]
    Execute["Execute plugin method"]
    Error["Log error, skip plugin"]
    
    Request --> CheckLoaded
    CheckLoaded -->|Yes| ReturnCached
    CheckLoaded -->|No| CheckAvailable
    CheckAvailable -->|Yes| LoadModule
    CheckAvailable -->|No| Error
    LoadModule --> FindClasses
    FindClasses --> Validate
    Validate -->|Valid| Register
    Validate -->|Invalid| Error
    Register --> MarkLoaded
    MarkLoaded --> Execute
    ReturnCached --> Execute

Diagram: Lazy Loading Decision Flow

The _load_plugin_lazy(plugin_name) method wshawk/plugin_system.py:158-201 implements thread-safe lazy loading with a lock to prevent concurrent loading of the same plugin.

Sources: wshawk/plugin_system.py:158-201


Plugin Validation and Security

The plugin system enforces validation rules to ensure security and compatibility before loading plugins.

Validation Checks

The _validate_plugin() method wshawk/plugin_system.py:203-235 performs the following checks:

| Check | Method | Purpose | |-------|--------|---------| | Metadata Presence | Check name and version fields | Ensure required metadata exists | | Version Format | _is_valid_version() | Validate semantic versioning (X.Y.Z) | | Compatibility | _is_compatible_version() | Check against min_wshawk_version | | Duplicate Detection | Compare with existing registry | Prevent plugin name conflicts |

Sources: wshawk/plugin_system.py:203-235

Semantic Versioning Rules

Version compatibility follows semantic versioning principles:

graph LR
    Required["Required Version<br/>e.g., 2.1.0"]
    Current["Current WSHawk<br/>e.g., 2.3.5"]
    
    Major["Major Version<br/>Must Match"]
    Minor["Minor Version<br/>Current >= Required"]
    Patch["Patch Version<br/>Ignored"]
    
    Result{"Compatible?"}
    Accept["Plugin Loaded"]
    Reject["Plugin Rejected"]
    
    Required --> Major
    Current --> Major
    Required --> Minor
    Current --> Minor
    
    Major --> Result
    Minor --> Result
    
    Result -->|"Yes"| Accept
    Result -->|"No"| Reject

Diagram: Version Compatibility Rules

The _is_compatible_version() method wshawk/plugin_system.py:245-261 implements this logic. Major versions must match exactly (breaking changes), while minor versions use >= comparison (backward compatible additions).

Sources: wshawk/plugin_system.py:237-261

Duplicate Detection and Override Policy

The PluginManager maintains an allow_override flag wshawk/plugin_system.py:130 that controls duplicate handling:

  • Default (allow_override=False): Duplicate plugin names are rejected with error
  • Override Mode (allow_override=True): New plugin replaces existing with warning

This prevents accidental conflicts while supporting intentional plugin replacement during development.

Sources: wshawk/plugin_system.py:263-313


Plugin Registration and Discovery

Directory Structure

The plugin system expects the following directory structure:

project_root/
├── plugins/              # Plugin directory (configurable)
│   ├── README.md         # Plugin documentation
│   ├── custom_xss.py     # Payload plugin example
│   ├── nosql_detector.py # Detector plugin example
│   └── protobuf_handler.py # Protocol plugin example
└── .plugin_cache/        # Cache directory (configurable)
    └── metadata.json     # Cached plugin metadata

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

Manual vs. Automatic Registration

The PluginManager supports two registration patterns:

1. Automatic Discovery (File-Based):

manager = PluginManager(plugin_dir="plugins")
# Plugins scanned but not loaded
manager.load_all_plugins()  # Explicit load all
# OR
payloads = manager.get_payloads("xss")  # Lazy load as needed

2. Manual Registration (Programmatic):

manager = PluginManager()
custom_plugin = CustomXSSPayloads()
manager.register_plugin(custom_plugin)  # Immediate registration

The register_plugin() method wshawk/plugin_system.py:320-333 performs validation and registration in a single operation.

Sources: wshawk/plugin_system.py:315-333

Thread Safety

All plugin operations use a threading lock to ensure thread-safe access:

sequenceDiagram
    participant T1 as "Thread 1"
    participant T2 as "Thread 2"
    participant Lock as "_lock"
    participant PM as "PluginManager"
    
    T1->>Lock: Acquire lock
    Lock-->>T1: Lock acquired
    T1->>PM: _load_plugin_lazy("plugin_a")
    
    T2->>Lock: Acquire lock
    Note over T2,Lock: Blocked waiting
    
    T1->>PM: Import, validate, register
    T1->>Lock: Release lock
    
    Lock-->>T2: Lock acquired
    T2->>PM: _load_plugin_lazy("plugin_b")
    T2->>PM: Import, validate, register
    T2->>Lock: Release lock

Diagram: Thread-Safe Plugin Loading

The _lock member wshawk/plugin_system.py:127 is a threading.Lock() that protects critical sections during plugin loading and registration.

Sources: wshawk/plugin_system.py:127, wshawk/plugin_system.py:168-170


Using Plugins in Scanning

Payload Plugin Integration

The scanner integrates payload plugins through the get_payloads() method with built-in caching:

# Get payloads from all plugins
all_payloads = manager.get_payloads("xss")

# Get payloads from specific plugin
custom_payloads = manager.get_payloads("xss", plugin_name="custom_xss")

The method uses @lru_cache(maxsize=128) wshawk/plugin_system.py:335 to cache results, avoiding repeated plugin execution for the same vulnerability type.

Sources: wshawk/plugin_system.py:335-373

Detector Plugin Integration

The scanner executes all detector plugins through run_detectors():

results = manager.run_detectors(
    response="MongoDB error with $ne operator",
    payload='{"username": {"$ne": null}}',
    context={"timing": 1.23, "status": 500}
)

# Results format:
# [
#     {
#         'plugin': 'nosql_detector_advanced',
#         'version': '1.0.0',
#         'confidence': 'HIGH',
#         'description': 'NoSQL injection detected (3 indicators)'
#     }
# ]

Detector plugins run sequentially, with exceptions caught and logged to prevent one plugin from breaking the entire detection pipeline.

Sources: wshawk/plugin_system.py:375-407

Plugin Listing and Introspection

The list_plugins() method provides visibility into plugin state:

# List all plugins (loaded and available)
all_plugins = manager.list_plugins(loaded_only=False)
# Returns:
# {
#     'available': ['custom_xss', 'nosql_detector', 'protobuf_handler'],
#     'loaded': ['custom_xss', 'nosql_detector'],
#     'payload_plugins': ['custom_xss'],
#     'detector_plugins': ['nosql_detector'],
#     'protocol_plugins': []
# }

# List only loaded plugins
loaded = manager.list_plugins(loaded_only=True)

The get_plugin_info() method retrieves detailed metadata:

info = manager.get_plugin_info("custom_xss")
# Returns PluginMetadata.to_dict() output

Sources: wshawk/plugin_system.py:409-438


Example Plugin Implementations

CustomXSSPayloads (Payload Plugin)

The example payload plugin wshawk/plugin_system.py:443-465 demonstrates:

  • PluginMetadata construction with required fields
  • Conditional payload return based on vuln_type parameter
  • In-production use case: loading from external file or database
class CustomXSSPayloads(PayloadPlugin):
    def get_metadata(self) -> PluginMetadata:
        return PluginMetadata(
            name="custom_xss",
            version="1.0.0",
            description="Custom XSS payloads for modern frameworks",
            author="WSHawk Team",
            min_wshawk_version="2.0.0"
        )
    
    def get_payloads(self, vuln_type: str) -> List[str]:
        if vuln_type == "xss":
            return [
                "<svg onload=alert(1)>",
                "<img src=x onerror=alert(1)>",
                # ... more payloads
            ]
        return []

Sources: wshawk/plugin_system.py:443-465

AdvancedNoSQLDetector (Detector Plugin)

The example detector plugin wshawk/plugin_system.py:467-494 demonstrates:

  • Multi-indicator detection logic (confidence scoring based on match count)
  • Context-aware analysis (can use context dict for timing, headers, etc.)
  • Descriptive output with confidence levels
class AdvancedNoSQLDetector(DetectorPlugin):
    def get_metadata(self) -> PluginMetadata:
        return PluginMetadata(
            name="nosql_detector_advanced",
            version="1.0.0",
            description="Advanced NoSQL injection detector with context analysis",
            author="WSHawk Team"
        )
    
    def detect(self, response: str, payload: str, context: Dict = None) -> tuple[bool, str, str]:
        nosql_indicators = ['mongodb', 'bson', 'query error', '$ne', '$gt', 'mongoose']
        response_lower = response.lower()
        matches = sum(1 for ind in nosql_indicators if ind in response_lower)
        
        if matches >= 2:
            return (True, "HIGH", f"NoSQL injection detected ({matches} indicators)")
        elif matches == 1:
            return (True, "MEDIUM", "Possible NoSQL injection")
        
        return (False, "LOW", "No NoSQL indicators found")

Sources: wshawk/plugin_system.py:467-494


Plugin System Integration Points

Scanner Integration

The WSHawkV2 scanner (see WSHawkV2 Scanner Engine) integrates plugins at key points:

flowchart LR
    Scanner["WSHawkV2<br/>scanner_v2.py"]
    WSPayloads["WSPayloads<br/>Static 22k+ payloads"]
    PluginMgr["PluginManager"]
    
    PayloadGen["Payload Generation<br/>Phase"]
    Detection["Detection<br/>Phase"]
    Verification["Verification<br/>Phase"]
    
    Scanner --> PayloadGen
    Scanner --> Detection
    Scanner --> Verification
    
    PayloadGen --> WSPayloads
    PayloadGen --> PluginMgr
    PluginMgr -.->|"get_payloads()"| PayloadGen
    
    Detection --> PluginMgr
    PluginMgr -.->|"run_detectors()"| Detection
    
    WSPayloads -.->|"Built-in payloads"| PayloadGen

Diagram: Plugin Integration with Scanning Pipeline

Plugins extend but do not replace core functionality, operating alongside built-in payloads and detectors.

Sources: wshawk/plugin_system.py:98-439

Configuration Integration

Plugin directories can be configured through the configuration system (see Configuration System):

# wshawk.yaml
plugins:
  directory: /custom/plugins
  cache_directory: /tmp/plugin_cache
  allow_override: false

Sources: wshawk/plugin_system.py:109


Security Considerations

Sandboxing and Isolation

The current plugin system provides basic isolation through:

  1. Separate Processes: Not implemented (plugins run in-process)
  2. Exception Handling: All plugin execution wrapped in try-except blocks
  3. Validation: Metadata and version checks before loading
  4. Checksum Verification: SHA-256 checksums detect file tampering

Note: Plugins have full access to Python runtime. Only load trusted plugins from verified sources.

Sources: wshawk/plugin_system.py:152-154, wshawk/plugin_system.py:203-235

Best Practices for Plugin Security

| Practice | Implementation | |----------|----------------| | Validate Inputs | Check all plugin parameters before passing to plugins | | Limit Scope | Plugins should only access provided context, not global state | | Error Isolation | Catch and log plugin exceptions without crashing scanner | | Audit Trail | Log all plugin loads, registrations, and executions | | Update Policy | Use semantic versioning to track breaking changes |

Sources: wshawk/plugin_system.py:1-551


Plugin Development Workflow

For detailed guides on creating specific plugin types, see:

For development environment setup and testing, see Development Guide.

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