Plugin Architecture Overview
Plugin Architecture Overview
The following files were used as context for generating this wiki page:
- RELEASE_3.0.0.md
- RELEASE_SUMMARY.md
- docs/DOCKER.md
- docs/V3_COMPLETE_GUIDE.md
- plugins/README.md
- wshawk/plugin_system.py
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:
- Payload plugins: Creating Payload Plugins
- Detector plugins: Creating Detector Plugins
- Protocol plugins: Creating Protocol Plugins
- Plugin validation requirements: Plugin Validation and Versioning
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_pluginsmaps plugin names to file paths without loading them;_loaded_pluginstracks which have been imported - Type-Specific Registries: Separate dictionaries for each plugin type enable O(1) lookup and type safety
- Metadata Store:
_plugin_metadatapreserves plugin information for introspection and validation - Checksum Validation: SHA-256 checksums in
_plugin_checksumsdetect file modifications - Thread Safety:
threading.Lockensures thread-safe registration in concurrent environments - Override Policy:
allow_overrideflag 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.pyfiles- Calculates SHA-256 checksums for integrity verification
- Populates
_available_pluginsand_plugin_checksumswithout 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
- Validates metadata via
_validate_plugin()wshawk/plugin_system.py:203-235 - Registers in appropriate registry via
_register_plugin_internal()wshawk/plugin_system.py:263-313 - Marks as loaded in
_loaded_pluginsset
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:
- Available: Plugin file detected in
plugin_dir, checksum computed, but not yet loaded into memory - Loading:
importlib.import_module()executing, Python bytecode compilation in progress - Validating:
_validate_plugin()wshawk/plugin_system.py:203-235 verifying metadata structure, version format, and compatibility - Registered: Plugin instance stored in appropriate registry (
_payload_plugins,_detector_plugins, or_protocol_plugins) - Active: Plugin methods being invoked by scanner, responses cached via
@lru_cache
Failure Conditions:
- Invalid metadata (missing
nameorversion) - 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:
- Major Version Match: Required major version must equal current major version (e.g., plugin requiring 2.x.x rejected on WSHawk 3.x.x)
- Minor Version Minimum: Current minor version must be >= required minor version (e.g., plugin requiring 2.5.0 accepted on WSHawk 2.6.0)
- 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_checksumsfor 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:
- Plugin loading is atomic (no partial registration)
- Duplicate checks are race-condition free
- Registry updates are serialized
_loaded_pluginsset 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
PluginManagerinstance lifetime - New
PluginManagerinstance 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:
- Payload Augmentation:
get_payloads(vuln_type)supplements static payloads fromWSPayloadsclass (see Payload Management System) - Detection Enhancement:
run_detectors()wshawk/plugin_system.py:375-407 complements built-inVulnerabilityVerifier(see Analysis and Verification Modules) - 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