# How to Use PluginStage for Vulnerability Discovery Phases in VulnClaw

> Learn how to use PluginStage in VulnClaw to model penetration testing workflows and orchestrate vulnerability discovery phases automatically. Master vulnerability research.

- Repository: [Unclecheng/VulnClaw](https://github.com/Unclecheng-li/VulnClaw)
- Tags: how-to-guide
- Published: 2026-06-30

---

**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](https://github.com/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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/plugins/result.py)**. The `PluginStage` enum provides six distinct phases that map to the penetration testing lifecycle:

```python
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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/vulnclaw/plugins/web/headers.py)**, the `SecurityHeadersPlugin` demonstrates this pattern by supporting both discovery and verification phases:

```python
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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/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`:

```python
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:

```bash
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:

```bash
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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/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:

```bash
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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/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`](https://github.com/Unclecheng-li/VulnClaw/blob/main/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.