Skip to content

[rajarshidattapy] fix(outputs): close sandbox escape in backend-code executor (#134)#137

Open
rajarshidattapy wants to merge 1 commit into
openswarm-ai:mainfrom
rajarshidattapy:fix/outputs-executor-sandbox-escape-134
Open

[rajarshidattapy] fix(outputs): close sandbox escape in backend-code executor (#134)#137
rajarshidattapy wants to merge 1 commit into
openswarm-ai:mainfrom
rajarshidattapy:fix/outputs-executor-sandbox-escape-134

Conversation

@rajarshidattapy

Copy link
Copy Markdown

Fixes #134.

The Output backend-code sandbox was bypassable because the AST allowlist did not detect access to sys.modules["os"]. The subprocess preamble removed dangerous builtins but left sys and sys.modules intact, allowing backend code to retrieve the already-loaded os module without using an import statement or any blocked builtin. The same bypass applied to object-graph traversal techniques such as ().__class__.__subclasses__() and __globals__(). Additionally, /api/outputs/execute executed validated-but-unforced code in the privileged environment, making os.system() reachable without explicit user consent.

Changes

  • Extend the AST validation to block:
    • Direct access to sys
    • __import__
    • __builtins__
    • All dunder attribute access
    • Dynamic attribute/introspection builtins such as getattr, setattr, delattr, vars, and related primitives
  • Decouple force_env from skip_validation, ensuring that the privileged environment (PATH/COMSPEC) is used only when the request explicitly specifies force=true.
  • Add comprehensive sandbox regression tests in tests/test_outputs_executor_sandbox.py.

Validation

  • Added 13 regression tests covering the identified sandbox bypasses and privilege-escalation paths.
  • All tests pass.
  • Linter passes on all modified files.

…m-ai#134)

The AST allowlist never saw `sys.modules["os"]`: the subprocess preamble
scrubs builtins but leaves sys/sys.modules live, so user code retrieved the
already-loaded os module with no import statement and no blocked-builtin call
(same for the ().__class__.__subclasses__() / __globals__ object walk). And
/api/outputs/execute ran empty-warning code with force_env, inheriting
PATH/COMSPEC so os.system was reachable with no consent.

- executor: block bare names sys/__import__/__builtins__, all dunder
  attribute access, and dynamic-attr builtins (getattr/setattr/vars/...),
  so user code can't reach a withheld module.
- executor: split force_env from skip_validation; the privileged
  inherit-real-env mode is now an explicit, separate decision.
- outputs: gate force_env on body.force alone, so vetted-but-unforced
  code stays in the minimal env.
- add tests/test_outputs_executor_sandbox.py regression suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rajarshidattapy rajarshidattapy changed the title fix(outputs): close sandbox escape in backend-code executor (#134) [rajarshidattapy] fix(outputs): close sandbox escape in backend-code executor (#134) Jul 20, 2026
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.

Sandbox escape in Output backend-code executor: sys.modules[...] bypasses the AST allowlist → arbitrary file R/W and RCE (regression of the #30 fix)

1 participant