BUG-HUNT: [SECURITY] Code injection vulnerability in LangGraph function execution #7053

Open
opened 2026-04-10 07:27:29 +00:00 by HAL9000 · 2 comments
Owner

Metadata

  • Branch: bugfix/m32-langgraph-function-injection
  • Commit Message: fix(langgraph): validate function names against allowlist before execution in NodeConfig
  • Milestone: v3.2.0
  • Parent Epic: (to be linked — see orphan note below)

Background and Context

A critical security vulnerability exists in src/cleveragents/langgraph/nodes.py at approximately line 320, inside the _execute_function() method of the LangGraph node execution engine.

The function name is sourced directly from user-configurable NodeConfig and is passed without any validation to an agents dictionary lookup. While the lookup itself is bounded to the registered agents registry, the absence of an explicit allowlist or sanitization step means that an attacker who can influence graph configuration (e.g., via a crafted actor YAML, a compromised plan, or a malicious A2A message) could cause arbitrary registered functions to be invoked — including functions that were never intended to be exposed as graph nodes.

This violates the fail-fast argument validation mandate in CONTRIBUTING.md (all public and protected methods must validate arguments before any other logic) and the spec-first security invariant requiring that all actor graph execution be sandboxed and validated.

Current Behavior

async def _execute_function(self, state: GraphState) -> dict[str, Any]:
    func_name = self.config.function  # User-controlled input — no validation
    if not func_name:
        raise ValueError(f"Function node {self.name} has no function specified")
    fn = self.agents.get(func_name)  # Direct lookup without allowlist check
    if not fn:
        raise ValueError(f"Function {func_name} not found")
    if not callable(fn):
        raise ValueError(f"Function {func_name} is not callable")
    # ... function execution follows with no further sandboxing

Attack vector: An attacker who can control NodeConfig.function (e.g., via a crafted actor YAML file, a malicious plan blueprint, or a tampered A2A message/send payload) can cause any function registered in the agents dictionary to be invoked — including internal infrastructure functions not intended for external invocation.

Expected Behavior

  • _execute_function() must validate func_name against an explicit allowlist of permitted function names before performing any lookup.
  • The allowlist must be derived from the actor's declared interface (e.g., the functions: section of the actor YAML config), not from the full agents registry.
  • Any func_name not present in the allowlist must raise a ValueError with a clear message before the registry lookup occurs.
  • Validation must occur as the first operation in the method, before any other logic (per CONTRIBUTING.md argument validation mandate).

Acceptance Criteria

  • _execute_function() validates func_name against an explicit allowlist before any registry lookup
  • The allowlist is derived from the actor's declared interface, not the full agents registry
  • Any func_name not in the allowlist raises ValueError immediately (fail-fast)
  • Validation is the first operation in the method body
  • No # type: ignore directives introduced
  • All existing Behave BDD scenarios for nodes.py continue to pass
  • New Behave scenarios cover: valid function name (passes), unlisted function name (raises), empty function name (raises), function name present in registry but not in allowlist (raises)
  • New Robot Framework integration test covers the end-to-end actor graph execution path with a crafted malicious NodeConfig
  • nox -s security_scan passes with no new findings
  • nox -s typecheck passes (Pyright strict)
  • Coverage remains ≥ 97%

Supporting Information

Location: src/cleveragents/langgraph/nodes.py, line ~320, method _execute_function()

Evidence:

async def _execute_function(self, state: GraphState) -> dict[str, Any]:
    func_name = self.config.function  # User-controlled input
    if not func_name:
        raise ValueError(f"Function node {self.name} has no function specified")
    fn = self.agents.get(func_name)  # Direct lookup without validation
    if not fn:
        raise ValueError(f"Function {func_name} not found")
    if not callable(fn):
        raise ValueError(f"Function {func_name} is not callable")
    # ... function execution follows

Impact: An attacker who can influence graph configuration could potentially execute arbitrary functions available in the agents registry, leading to code injection attacks.

Suggested Fix: Implement function name allowlist validation derived from the actor's declared interface. The allowlist should be constructed at NodeConfig parse time and stored as a frozenset[str]. The _execute_function() method should check membership before any registry lookup.

Related issues: Similar security patterns have been found in:

  • #6670TransformExecutor sandbox escape via str.__mro__
  • #6677InlineToolExecutor subprocess inherits all process environment variables
  • #6712load_from_entry_points() bypasses module prefix allowlist

TDD Requirement: Per CONTRIBUTING.md Bug Fix Workflow, a companion TDD: issue must be created and merged before this fix. The bug fix PR must remove @tdd_expected_fail from all @tdd_issue_<N> scenarios.

Subtasks

  • Create companion TDD issue (TDD: [SECURITY] Code injection vulnerability in LangGraph function execution) with @tdd_issue @tdd_issue_<N> @tdd_expected_fail Behave scenarios
  • Implement allowlist construction in NodeConfig at parse/validation time
  • Add allowlist membership check as first operation in _execute_function()
  • Add Behave BDD unit test scenarios covering all validation paths
  • Add Robot Framework integration test for end-to-end malicious NodeConfig scenario
  • Remove @tdd_expected_fail tag from all @tdd_issue_<N> scenarios in the bug fix PR
  • Run nox -s security_scan and confirm no new findings
  • Run nox -s typecheck and confirm zero Pyright errors
  • Run nox -s coverage_report and confirm coverage ≥ 97%

Definition of Done

  • Allowlist validation implemented and is the first operation in _execute_function()
  • All new Behave BDD scenarios pass (including the previously @tdd_expected_fail scenario)
  • Robot Framework integration test passes
  • nox -s security_scan passes with no new high/critical findings
  • nox -s typecheck passes (zero Pyright strict errors)
  • All nox stages pass
  • Coverage ≥ 97%
  • PR merged and this issue closed

Automated by CleverAgents Bot
Supervisor: BUG-HUNT | Agent: new-issue-creator

## Metadata - **Branch**: `bugfix/m32-langgraph-function-injection` - **Commit Message**: `fix(langgraph): validate function names against allowlist before execution in NodeConfig` - **Milestone**: v3.2.0 - **Parent Epic**: *(to be linked — see orphan note below)* ## Background and Context A critical security vulnerability exists in `src/cleveragents/langgraph/nodes.py` at approximately line 320, inside the `_execute_function()` method of the LangGraph node execution engine. The function name is sourced directly from user-configurable `NodeConfig` and is passed without any validation to an `agents` dictionary lookup. While the lookup itself is bounded to the registered agents registry, the absence of an explicit allowlist or sanitization step means that an attacker who can influence graph configuration (e.g., via a crafted actor YAML, a compromised plan, or a malicious A2A message) could cause arbitrary registered functions to be invoked — including functions that were never intended to be exposed as graph nodes. This violates the **fail-fast argument validation** mandate in CONTRIBUTING.md (all public and protected methods must validate arguments before any other logic) and the **spec-first security invariant** requiring that all actor graph execution be sandboxed and validated. ## Current Behavior ```python async def _execute_function(self, state: GraphState) -> dict[str, Any]: func_name = self.config.function # User-controlled input — no validation if not func_name: raise ValueError(f"Function node {self.name} has no function specified") fn = self.agents.get(func_name) # Direct lookup without allowlist check if not fn: raise ValueError(f"Function {func_name} not found") if not callable(fn): raise ValueError(f"Function {func_name} is not callable") # ... function execution follows with no further sandboxing ``` **Attack vector**: An attacker who can control `NodeConfig.function` (e.g., via a crafted actor YAML file, a malicious plan blueprint, or a tampered A2A `message/send` payload) can cause any function registered in the `agents` dictionary to be invoked — including internal infrastructure functions not intended for external invocation. ## Expected Behavior - `_execute_function()` must validate `func_name` against an explicit allowlist of permitted function names before performing any lookup. - The allowlist must be derived from the actor's declared interface (e.g., the `functions:` section of the actor YAML config), not from the full `agents` registry. - Any `func_name` not present in the allowlist must raise a `ValueError` with a clear message before the registry lookup occurs. - Validation must occur as the **first** operation in the method, before any other logic (per CONTRIBUTING.md argument validation mandate). ## Acceptance Criteria - [ ] `_execute_function()` validates `func_name` against an explicit allowlist before any registry lookup - [ ] The allowlist is derived from the actor's declared interface, not the full `agents` registry - [ ] Any `func_name` not in the allowlist raises `ValueError` immediately (fail-fast) - [ ] Validation is the first operation in the method body - [ ] No `# type: ignore` directives introduced - [ ] All existing Behave BDD scenarios for `nodes.py` continue to pass - [ ] New Behave scenarios cover: valid function name (passes), unlisted function name (raises), empty function name (raises), function name present in registry but not in allowlist (raises) - [ ] New Robot Framework integration test covers the end-to-end actor graph execution path with a crafted malicious `NodeConfig` - [ ] `nox -s security_scan` passes with no new findings - [ ] `nox -s typecheck` passes (Pyright strict) - [ ] Coverage remains ≥ 97% ## Supporting Information **Location**: `src/cleveragents/langgraph/nodes.py`, line ~320, method `_execute_function()` **Evidence**: ```python async def _execute_function(self, state: GraphState) -> dict[str, Any]: func_name = self.config.function # User-controlled input if not func_name: raise ValueError(f"Function node {self.name} has no function specified") fn = self.agents.get(func_name) # Direct lookup without validation if not fn: raise ValueError(f"Function {func_name} not found") if not callable(fn): raise ValueError(f"Function {func_name} is not callable") # ... function execution follows ``` **Impact**: An attacker who can influence graph configuration could potentially execute arbitrary functions available in the agents registry, leading to code injection attacks. **Suggested Fix**: Implement function name allowlist validation derived from the actor's declared interface. The allowlist should be constructed at `NodeConfig` parse time and stored as a `frozenset[str]`. The `_execute_function()` method should check membership before any registry lookup. **Related issues**: Similar security patterns have been found in: - #6670 — `TransformExecutor` sandbox escape via `str.__mro__` - #6677 — `InlineToolExecutor` subprocess inherits all process environment variables - #6712 — `load_from_entry_points()` bypasses module prefix allowlist **TDD Requirement**: Per CONTRIBUTING.md Bug Fix Workflow, a companion `TDD:` issue must be created and merged before this fix. The bug fix PR must remove `@tdd_expected_fail` from all `@tdd_issue_<N>` scenarios. ## Subtasks - [ ] Create companion TDD issue (`TDD: [SECURITY] Code injection vulnerability in LangGraph function execution`) with `@tdd_issue @tdd_issue_<N> @tdd_expected_fail` Behave scenarios - [ ] Implement allowlist construction in `NodeConfig` at parse/validation time - [ ] Add allowlist membership check as first operation in `_execute_function()` - [ ] Add Behave BDD unit test scenarios covering all validation paths - [ ] Add Robot Framework integration test for end-to-end malicious `NodeConfig` scenario - [ ] Remove `@tdd_expected_fail` tag from all `@tdd_issue_<N>` scenarios in the bug fix PR - [ ] Run `nox -s security_scan` and confirm no new findings - [ ] Run `nox -s typecheck` and confirm zero Pyright errors - [ ] Run `nox -s coverage_report` and confirm coverage ≥ 97% ## Definition of Done - [ ] Allowlist validation implemented and is the first operation in `_execute_function()` - [ ] All new Behave BDD scenarios pass (including the previously `@tdd_expected_fail` scenario) - [ ] Robot Framework integration test passes - [ ] `nox -s security_scan` passes with no new high/critical findings - [ ] `nox -s typecheck` passes (zero Pyright strict errors) - [ ] All nox stages pass - [ ] Coverage ≥ 97% - [ ] PR merged and this issue closed --- **Automated by CleverAgents Bot** Supervisor: BUG-HUNT | Agent: new-issue-creator
HAL9000 added this to the v3.2.0 milestone 2026-04-10 07:27:41 +00:00
Author
Owner

⚠️ Orphan Issue — Manual Parent Epic Linking Required

This issue was created without a parent Epic dependency link because no suitable LangGraph security or hardening Epic was found in the open issue backlog. Related security issues (#6670, #6677, #6712, #6558) are also unlinked to a parent Epic.

Per CONTRIBUTING.md, orphan issues are not permitted. A human project owner should:

  1. Identify or create an appropriate parent Epic (e.g., a "LangGraph Security Hardening" Epic or a broader "Security Vulnerability Remediation" Epic for v3.2.0).
  2. Link this issue as a child using the Forgejo dependency system:
    # This issue (#7053) BLOCKS the parent Epic
    POST /api/v1/repos/cleveragents/cleveragents-core/issues/7053/blocks
    { "owner": "cleveragents", "repo": "cleveragents-core", "index": <PARENT_EPIC_NUMBER> }
    

Note: Per CONTRIBUTING.md Bug Fix Workflow, a companion TDD issue must also be created before work begins on this fix. The TDD issue should be titled:

TDD: [SECURITY] Code injection vulnerability in LangGraph function execution

The bug fix issue (#7053) must depend on the TDD issue (TDD must be merged first).


Automated by CleverAgents Bot
Supervisor: BUG-HUNT | Agent: new-issue-creator

⚠️ **Orphan Issue — Manual Parent Epic Linking Required** This issue was created without a parent Epic dependency link because no suitable LangGraph security or hardening Epic was found in the open issue backlog. Related security issues (#6670, #6677, #6712, #6558) are also unlinked to a parent Epic. Per CONTRIBUTING.md, orphan issues are not permitted. A human project owner should: 1. Identify or create an appropriate parent Epic (e.g., a "LangGraph Security Hardening" Epic or a broader "Security Vulnerability Remediation" Epic for v3.2.0). 2. Link this issue as a child using the Forgejo dependency system: ``` # This issue (#7053) BLOCKS the parent Epic POST /api/v1/repos/cleveragents/cleveragents-core/issues/7053/blocks { "owner": "cleveragents", "repo": "cleveragents-core", "index": <PARENT_EPIC_NUMBER> } ``` **Note**: Per CONTRIBUTING.md Bug Fix Workflow, a companion TDD issue must also be created before work begins on this fix. The TDD issue should be titled: > `TDD: [SECURITY] Code injection vulnerability in LangGraph function execution` The bug fix issue (#7053) must **depend on** the TDD issue (TDD must be merged first). --- **Automated by CleverAgents Bot** Supervisor: BUG-HUNT | Agent: new-issue-creator
Author
Owner

Verified — Critical security bug: code injection vulnerability in LangGraph function execution. MoSCoW: Must-have. Priority: Critical — remote code execution risk.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Critical security bug: code injection vulnerability in LangGraph function execution. MoSCoW: Must-have. Priority: Critical — remote code execution risk. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-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.

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