How to Use PluginStage for Vulnerability Discovery Phases in VulnClaw

VulnClaw models penetration testing as a sequential workflow where plugins declare their preferred execution phases by populating the stages attribute with PluginStage enum members, enabling automatic orchestration during the discovery phase.

The Unclecheng-li/VulnClaw repository implements a plugin-based architecture that mirrors standard penetration testing methodologies. By categorizing plugins into discrete stages—reconnaissance, discovery, verification, exploitation, post-exploitation, and reporting—the framework ensures that vulnerability checks execute only during their intended workflow phases. This article explains how to leverage PluginStage to designate discovery-phase plugins and trigger them through both programmatic and command-line interfaces.

Understanding the PluginStage Enum

VulnClaw defines the workflow stages as a string-based enumeration in vulnclaw/plugins/result.py. The PluginStage enum provides six distinct phases that map to the penetration testing lifecycle:

from enum import Enum

class PluginStage(str, Enum):
    RECON = "recon"
    DISCOVERY = "discovery"
    VERIFICATION = "verification"
    EXPLOITATION = "exploitation"
    POST_EXPLOITATION = "post_exploitation"
    REPORTING = "reporting"

Each stage represents a specific phase in the security assessment workflow. The discovery phase specifically focuses on identifying potential vulnerabilities, misconfigurations, and security weaknesses without fully exploiting them.

Declaring Discovery Stage in Plugins

Plugins declare their execution eligibility by defining a class-level stages tuple containing one or more PluginStage members. When a plugin supports discovery operations, it includes PluginStage.DISCOVERY in this tuple.

In vulnclaw/plugins/web/headers.py, the SecurityHeadersPlugin demonstrates this pattern by supporting both discovery and verification phases:

from vulnclaw.plugins.result import PluginStage
from vulnclaw.plugins.base import VulnPlugin

class SecurityHeadersPlugin(VulnPlugin):
    stages = (PluginStage.DISCOVERY, PluginStage.VERIFICATION)
    # Additional plugin implementation...

The runtime engine inspects this attribute during the PluginRuntime.execute() call in vulnclaw/plugins/runtime.py to determine whether a plugin should run for the current workflow phase.

Triggering Discovery Plugins

VulnClaw provides three methods to execute discovery-phase plugins: direct Python API calls, CLI commands, and automatic orchestration during full scans.

Programmatic Execution via Python

To manually trigger a discovery plugin, instantiate PluginRuntime and create a PluginContext with stage=PluginStage.DISCOVERY:

import asyncio
from vulnclaw.plugins.runtime import PluginRuntime
from vulnclaw.plugins.result import PluginStage, PluginContext

async def run_discovery():
    runtime = PluginRuntime()  # Uses the global registry from registry.py

    context = PluginContext(
        target="https://example.com",
        stage=PluginStage.DISCOVERY,
        allow_destructive=False
    )
    result = await runtime.execute("security_headers", context)
    return result

asyncio.run(run_discovery())

The PluginRuntime.execute() method performs pre-flight validation—including stage matching, budget enforcement, and policy checks—before invoking the plugin's run() method with the supplied context.

Command-Line Interface

The VulnClaw CLI abstracts context creation through the plugins run sub-command. Specify the target and stage to execute a specific discovery plugin:

vulnclaw plugins run security_headers \
    --target https://example.com \
    --stage discovery

The CLI parses the --stage argument into a PluginStage enum value and constructs the PluginContext internally before delegating to PluginRuntime.execute().

Automatic Discovery During Full Scans

When running a comprehensive assessment, the orchestrator automatically executes all plugins registered for the discovery phase:

vulnclaw scan https://example.com

During execution, the engine iterates through workflow stages and invokes every plugin from the global registry (defined in vulnclaw/plugins/registry.py) that lists the current stage in its stages tuple. This eliminates manual plugin management while ensuring comprehensive coverage.

Runtime Configuration and Constraints

Control discovery-phase execution through several configuration parameters accessible via the vulnclaw config command:

  • plugin_runtime_enabled – Globally enables or disables plugin execution (boolean)
  • plugin_max_requests_per_target – Enforces request budget limits per target (integer)
  • plugin_default_timeout – Sets default execution timeout in seconds for discovery plugins (integer)
  • allow_destructive=False – Prevents plugins marked with destructive=True from running during discovery (boolean)

Configure these settings to constrain discovery operations:

vulnclaw config set plugin_max_requests_per_target 50
vulnclaw config set plugin_default_timeout 30
vulnclaw config set plugin_runtime_enabled true

Summary

  • PluginStage enum in vulnclaw/plugins/result.py defines six penetration testing phases including DISCOVERY.
  • Plugins declare stage support via the stages class attribute tuple (e.g., stages = (PluginStage.DISCOVERY,)).
  • PluginRuntime.execute() in vulnclaw/plugins/runtime.py filters plugins by stage before execution.
  • Trigger discovery plugins manually via Python API with PluginContext(stage=PluginStage.DISCOVERY) or via CLI with --stage discovery.
  • Full scans (vulnclaw scan) automatically execute all discovery-phase plugins in sequence.
  • Runtime constraints including request budgets, timeouts, and destructive-action policies govern discovery-phase behavior.

Frequently Asked Questions

How do I create a custom plugin that runs only during the discovery phase?

Define your plugin class with stages = (PluginStage.DISCOVERY,) and inherit from VulnPlugin. Ensure you import PluginStage from vulnclaw.plugins.result. The runtime will automatically include your plugin during discovery-phase execution while excluding it from exploitation or reporting phases.

What is the difference between DISCOVERY and VERIFICATION stages in VulnClaw?

The DISCOVERY stage identifies potential vulnerabilities and security misconfigurations superficially, while the VERIFICATION stage confirms whether identified issues are actually exploitable. Many plugins like SecurityHeadersPlugin include both stages in their stages tuple to support comprehensive assessment workflows.

Can I restrict destructive plugins from running during discovery?

Yes. Set allow_destructive=False in your PluginContext or omit the --allow-destructive flag in CLI commands. This prevents plugins marked with the destructive=True attribute from executing during discovery, ensuring that reconnaissance and vulnerability identification remain non-intrusive.

Where does VulnClaw store the plugin registry that maps IDs to discovery plugins?

The global registry is implemented in vulnclaw/plugins/registry.py, which maintains the mapping between plugin IDs (like "security_headers") and their corresponding classes. PluginRuntime consults this registry via self.registry.get(plugin_id) to resolve plugin instances before execution.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →