fix(providers): replace dynamic code execution with safe bracket-access parser in _enrich#6520
Conversation
…n _enrich Removes CWE-95 code injection vulnerability where user-uploaded workflow YAML enrichment values were passed to a dynamic execution function with a bypassable restricted builtins sandbox. Replaced with _safe_results_access() which only supports sequential bracket-based index/key lookups using regex parsing.
|
|
|
No linked issues found. Please add the corresponding issues in the pull request description. |
|
Thanks for the automated checks — two notes:
No code review feedback to address yet — standing by for maintainer review. |
|
Signed on behalf of:) |
Vulnerability Summary
CWE-95 — Eval of Workflow Expression in
BaseProvider._enrich()In
keep/providers/base/base_provider.py, the_enrich()method useseval()to evaluate workflow enrichment expressions that start withresults[. While the call restricts__builtins__to an empty dict, Python sandbox escapes via__subclasses__()/__class__.__mro__chains are well-documented and can bypass an empty-builtins restriction to achieve arbitrary code execution.Affected code (before this fix):
The
converted_valuevariable originates fromenrichment["value"]in a workflow YAML definition. A user with workflow creation/edit permissions (write:workflowsscope) can craft a malicious enrichment value that escapes the restrictedeval()sandbox.Severity: Low — exploitation requires authenticated access with
write:workflowspermission, and the restricted eval context (empty__builtins__) raises the bar significantly. However, Python sandbox escapes against empty builtins are a solved problem in the security research community, making this a defense-in-depth concern worth fixing.Fix Description
This PR replaces the
eval()call with a new static method_safe_results_access()that uses a regex-based parser to walk bracket-access expressions (results[0]["key"]["nested"]). The parser:results^\[(\d+|"[^"]*"|'[^']*')\]intfor index access, strips quotes for string keysValueErroron any unexpected syntaxThis approach supports the exact same access patterns the original
eval()was used for (numeric indices and quoted string keys), without executing arbitrary Python code.Testing
We verified the fix handles the expected access patterns correctly:
Proof of Concept
An authenticated user with
write:workflowspermission can create a workflow YAML containing a malicious enrichment value. The enrichment value flows through_enrich()→converted_value→ previously intoeval().Example malicious enrichment value in workflow YAML:
After dot-to-bracket conversion, this becomes:
While the dot-to-bracket conversion interferes with some payloads, crafted bracket-notation expressions bypass conversion entirely:
This would fail with empty
__builtins__in most cases, but more sophisticated payloads leveraging().__class__.__bases__[0].__subclasses__()to findos._wrap_closeorsubprocess.Popenin the subclass list are well-documented (e.g., see "Python sandbox escape" literature). The empty-builtins restriction is not a reliable security boundary.Adversarial Review
Before submitting, we considered whether this finding is practically exploitable given the mitigations in place. The
eval()call uses{"__builtins__": {}}which blocks direct use of__import__,open, etc. However, Python's introspection capabilities (__class__.__mro__.__subclasses__()) are specifically designed to bypass empty-builtins sandboxes — this is a well-known class of escape documented extensively in CTF writeups and security research (e.g., Ned Batchelder's "Eval really is dangerous"). The dot-to-bracket conversion adds friction but does not prevent exploitation via expressions already in bracket notation. The auth requirement (write:workflows) limits the attack surface to authenticated users, making this low severity but still worth fixing as defense-in-depth.Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.