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) -> intprovides 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
- Returns:
- 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) -> strprocesses custom protocol messages - Protocol Name:
get_protocol_name() -> strreturns 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:
- Creates
plugins/and.plugin_cache/directories if missing - Scans
plugins/directory for.pyfiles (excluding_prefixed files) - Calculates SHA-256 checksums for integrity verification
- 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:
PluginMetadataconstruction with required fields- Conditional payload return based on
vuln_typeparameter - 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
contextdict 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:
- Separate Processes: Not implemented (plugins run in-process)
- Exception Handling: All plugin execution wrapped in try-except blocks
- Validation: Metadata and version checks before loading
- 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:
- Creating Payload Plugins
- Creating Detector Plugins
- Creating Protocol Plugins
- Plugin Validation and Versioning
For development environment setup and testing, see Development Guide.
Sources: plugins/README.md:1-37, wshawk/plugin_system.py:498-551