BUG-HUNT: [security] PluginLoader.validate_protocol() instantiates untrusted plugin class causing arbitrary code execution during validation #7331

Closed
opened 2026-04-10 17:08:13 +00:00 by HAL9000 · 1 comment
Owner

Bug Report: Security — Arbitrary Code Execution in Plugin Protocol Validation

Severity Assessment

  • Impact: validate_protocol() creates a live instance of the plugin class (klass()) during protocol validation. Any side effects in the plugin's __init__ (network connections, file I/O, command execution, etc.) are triggered during the validation step — before the plugin is approved for use. This allows malicious plugin code to execute during the validation phase.
  • Likelihood: Low — requires a malicious or buggy plugin to be present in the entry-point registry or configuration
  • Priority: Medium

Location

  • File: src/cleveragents/infrastructure/plugins/loader.py
  • Function/Class: PluginLoader.validate_protocol()
  • Lines: 219–255

Description

The protocol validation method instantiates the plugin class to perform an isinstance check:

try:
    instance = klass()              # ← Creates a LIVE INSTANCE!
    if isinstance(instance, protocol):
        return True
except Exception:
    # fallback...

This creates a live object in order to validate its type. Any code in klass.__init__ runs during this validation call. If the plugin class:

  • Opens network connections in __init__
  • Creates files or processes
  • Has expensive initialization
  • Raises errors with sensitive information
  • Contains deliberately malicious code

...these all execute DURING validation, before the plugin is confirmed safe.

Protocol compliance can be checked structurally without instantiation using issubclass(klass, protocol) for @runtime_checkable protocols, or by inspecting klass.__abstractmethods__ / method signatures.

Evidence

# infrastructure/plugins/loader.py lines 237-248
try:
    instance = klass()   # ← LIVE INSTANTIATION — side effects run here!
    if isinstance(instance, protocol):
        return True
except Exception:
    # If instantiation fails, try subclass check
    try:
        if issubclass(klass, protocol):
            return True
    except TypeError:
        pass

The module docstring explicitly lists the security goal: "Security: module imports are restricted to a configurable prefix allowlist...This prevents arbitrary code execution from untrusted configuration." But the instantiation in validate_protocol() bypasses this intent.

Expected Behavior

Protocol validation should be purely structural — checking that the class declares the required methods — without instantiating the class.

Actual Behavior

The plugin class is instantiated during validation, running its __init__ method with all associated side effects.

Suggested Fix

Use structural checking only:

@staticmethod
def validate_protocol(klass: type[Any], protocol: type[Any]) -> bool:
    # Structural check only - no instantiation
    try:
        if issubclass(klass, protocol):
            return True
    except TypeError:
        pass
    # Check required methods structurally
    msg = f"Class '{klass.__name__}' does not satisfy protocol '{protocol.__name__}'"
    raise ProtocolMismatchError(msg)

Category

security

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: @tdd_issue, @tdd_issue_<this-issue-number>, and @tdd_expected_fail to prove the bug exists before fixing it.


Automated by CleverAgents Bot
Supervisor: Bug Detection Pool | Agent: bug-hunt-pool-supervisor

## Bug Report: Security — Arbitrary Code Execution in Plugin Protocol Validation ### Severity Assessment - **Impact**: `validate_protocol()` creates a live instance of the plugin class (`klass()`) during protocol validation. Any side effects in the plugin's `__init__` (network connections, file I/O, command execution, etc.) are triggered during the validation step — before the plugin is approved for use. This allows malicious plugin code to execute during the validation phase. - **Likelihood**: Low — requires a malicious or buggy plugin to be present in the entry-point registry or configuration - **Priority**: Medium ### Location - **File**: `src/cleveragents/infrastructure/plugins/loader.py` - **Function/Class**: `PluginLoader.validate_protocol()` - **Lines**: 219–255 ### Description The protocol validation method instantiates the plugin class to perform an `isinstance` check: ```python try: instance = klass() # ← Creates a LIVE INSTANCE! if isinstance(instance, protocol): return True except Exception: # fallback... ``` This creates a live object in order to validate its type. Any code in `klass.__init__` runs during this validation call. If the plugin class: - Opens network connections in `__init__` - Creates files or processes - Has expensive initialization - Raises errors with sensitive information - Contains deliberately malicious code ...these all execute DURING validation, before the plugin is confirmed safe. Protocol compliance can be checked structurally without instantiation using `issubclass(klass, protocol)` for `@runtime_checkable` protocols, or by inspecting `klass.__abstractmethods__` / method signatures. ### Evidence ```python # infrastructure/plugins/loader.py lines 237-248 try: instance = klass() # ← LIVE INSTANTIATION — side effects run here! if isinstance(instance, protocol): return True except Exception: # If instantiation fails, try subclass check try: if issubclass(klass, protocol): return True except TypeError: pass ``` The module docstring explicitly lists the security goal: "Security: module imports are restricted to a configurable prefix allowlist...This prevents arbitrary code execution from untrusted configuration." But the instantiation in `validate_protocol()` bypasses this intent. ### Expected Behavior Protocol validation should be purely structural — checking that the class declares the required methods — without instantiating the class. ### Actual Behavior The plugin class is instantiated during validation, running its `__init__` method with all associated side effects. ### Suggested Fix Use structural checking only: ```python @staticmethod def validate_protocol(klass: type[Any], protocol: type[Any]) -> bool: # Structural check only - no instantiation try: if issubclass(klass, protocol): return True except TypeError: pass # Check required methods structurally msg = f"Class '{klass.__name__}' does not satisfy protocol '{protocol.__name__}'" raise ProtocolMismatchError(msg) ``` ### Category security ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. The test will use tags: `@tdd_issue`, `@tdd_issue_<this-issue-number>`, and `@tdd_expected_fail` to prove the bug exists before fixing it. --- **Automated by CleverAgents Bot** Supervisor: Bug Detection Pool | Agent: bug-hunt-pool-supervisor
Author
Owner

Closing as Duplicate

This issue is a duplicate of #7380 which describes the same PluginLoader.validate_protocol() arbitrary code execution vulnerability. Issue #7380 is more complete (has milestone v3.5.0, MoSCoW/Must have, Points/3, State/Verified).

Closing as duplicate of #7380.


Automated by CleverAgents Bot
Supervisor: Backlog Groomer | Agent: backlog-grooming-pool-supervisor

## Closing as Duplicate This issue is a duplicate of #7380 which describes the same `PluginLoader.validate_protocol()` arbitrary code execution vulnerability. Issue #7380 is more complete (has milestone v3.5.0, MoSCoW/Must have, Points/3, State/Verified). Closing as duplicate of #7380. --- **Automated by CleverAgents Bot** Supervisor: Backlog Groomer | Agent: backlog-grooming-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#7331
No description provided.