Skip to content

fix: harden workflow execution against code injection (RCE)#455

Open
wzg2311 wants to merge 2 commits into
AgentFlocks:devfrom
wzg2311:fix/webhook-rce-security
Open

fix: harden workflow execution against code injection (RCE)#455
wzg2311 wants to merge 2 commits into
AgentFlocks:devfrom
wzg2311:fix/webhook-rce-security

Conversation

@wzg2311

@wzg2311 wzg2311 commented Jun 18, 2026

Copy link
Copy Markdown

Summary

Addresses the security vulnerabilities reported in #454.

Three hardening measures to prevent code injection / RCE through workflow execution:

  • Sandbox exec() builtins in PythonExecRuntime — block eval/exec/compile/__import__ and restrict imports to a safe allowlist, so even if workflow node code unsafely processes inputs (e.g. eval(inputs["expression"])), attackers cannot import os/subprocess or call dangerous builtins
  • Switch Jinja2 to SandboxedEnvironment in LLM and HTTP request nodes — prevents SSTI through template rendering
  • Add security guard comment to PUBLIC_PATH_REGEXES warning against adding workflow webhook paths to the auth bypass whitelist

Files changed

File Change
flocks/workflow/repl_runtime.py Add _make_safe_builtins() with blocked builtins + import allowlist; apply to exec() context
flocks/workflow/engine.py Replace jinja2.Template with jinja2.sandbox.SandboxedEnvironment in LLM + HTTP request nodes
flocks/server/auth.py Add security comment to PUBLIC_PATH_REGEXES referencing #454

Context

When a workflow webhook trigger has auth.type="none" (the default) and the webhook path is in the auth bypass whitelist, an anonymous attacker can trigger workflow execution with attacker-controlled inputs. If a Python node then processes those inputs unsafely (e.g. eval(inputs["expr"])), it leads to RCE in the host process.

This PR hardens the execution layer so that even in the worst case (auth bypass + unsafe node code), the blast radius is contained:

  • eval()/exec()/compile() are removed from builtins
  • Only safe stdlib modules can be imported
  • Jinja2 templates run in a sandbox

See #454 for full vulnerability details, reproduction steps, and additional recommendations (P0: enforce webhook trigger auth, P2: change TriggerAuth default).

Test plan

  • Verify existing workflow Python nodes still work (they typically use json, re, datetime etc. which are in the allowlist)
  • Verify eval()/exec() are blocked in workflow node code
  • Verify import os / import subprocess are blocked in workflow node code
  • Verify Jinja2 templates still render correctly in LLM and HTTP request nodes
  • Verify Jinja2 sandbox blocks attribute access to dangerous objects

Closes #454

Three security hardening measures:

1. Sandbox exec() builtins in PythonExecRuntime — block eval/exec/compile/__import__
   and restrict imports to a safe allowlist, preventing arbitrary code execution
   even when workflow node code unsafely processes inputs.

2. Switch Jinja2 Template to SandboxedEnvironment in LLM and HTTP request nodes,
   preventing SSTI attacks through template rendering.

3. Add security comment to PUBLIC_PATH_REGEXES warning against adding workflow
   webhook paths to the auth bypass whitelist (refs AgentFlocks#454).

Closes AgentFlocks#454

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@duguwanglong duguwanglong self-requested a review June 18, 2026 06:07
@duguwanglong

Copy link
Copy Markdown
Contributor

Findings
[P1] PythonExecRuntime sandboxing can be bypassed via Python object reflection.
The PR restricts builtins and imports in flocks/workflow/repl_runtime.py, but this is not a reliable security boundary. Because the runtime module imports subprocess at module load time, workflow code can still reach subprocess.Popen through object.subclasses() and execute shell commands. I reproduced this against the exported PR tree: the payload found subprocess.Popen and executed a shell command successfully. This means the proposed fix does not actually prevent RCE once attacker-controlled input reaches Python execution.

[P1] Anonymous workflow webhook triggering still remains when applied to current dev.
Current dev exposes /webhook/workflows/{workflow_id}/{trigger_id} through the global auth exemption in auth.py (line 68), and _authorize_webhook_trigger() still allows auth is None or auth.type == "none" in workflow.py (line 2787). PR #455 only adds a warning comment; it does not make webhook auth fail closed. The original anonymous-trigger risk remains unless webhook/custom_webhook triggers require api_key or hmac.
[P2] The fixed import allowlist breaks workflows that use metadata.requirements.
The runner still installs workflow-declared requirements, but the new runtime allowlist blocks importing installed packages such as pydantic, yaml, and httpx. I reproduced all three failing with ImportError. This creates a confusing regression: dependencies can install successfully but workflow execution cannot import them.

@stephamie7 stephamie7 changed the base branch from main to dev June 18, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] Webhook Trigger 认证绕过导致未授权远程代码执行 (RCE)

3 participants