Fix command injection and sandbox escape in CodeInterpreterTool#4643
Fix command injection and sandbox escape in CodeInterpreterTool#4643Ratnaditya-J wants to merge 1 commit intocrewAIInc:mainfrom
Conversation
Fix two security vulnerabilities in CodeInterpreterTool:
1. Command injection (CWE-78): Replace os.system(f"pip install {library}")
with subprocess.run(["pip", "install", library]) to prevent shell injection
via crafted library names.
2. Sandbox escape (CWE-94): Block __class__, __bases__, __subclasses__,
__mro__, and __qualname__ attribute access in SandboxPython to prevent
introspection-based escape. Also block getattr, setattr, delattr, and
breakpoint builtins.
Fixes crewAIInc#4516
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Free Tier Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| if attr in code: | ||
| raise RuntimeError( | ||
| f"Access to '{attr}' is not allowed in the sandbox." | ||
| ) |
There was a problem hiding this comment.
Sandbox escape via __getattribute__ bypasses restricted attrs check
High Severity
The _check_restricted_attrs method uses plain substring matching, which is trivially bypassed via string concatenation combined with __getattribute__. Since getattr is blocked from builtins but __getattribute__ is an unblocked method on every Python object, an attacker can write ().__getattribute__("__cl"+"ass__").__getattribute__("__ba"+"ses__")[0].__getattribute__("__subcl"+"asses__")() to perform the full __subclasses__() introspection escape without triggering any restricted attribute substring match.
Additional Locations (1)
| @@ -1,4 +1,4 @@ | |||
| from unittest.mock import patch | |||
| from unittest.mock import patch, call | |||
There was a problem hiding this comment.
Unused call import in test file
Low Severity
The call object imported from unittest.mock is never used anywhere in the test file. It was added alongside the new test classes but none of the assertions use the call helper—they all use mock methods like assert_called_once_with and assert_any_call instead.
| subprocess.run( | ||
| ["pip", "install", library], # noqa: S607 | ||
| check=True, | ||
| ) |
There was a problem hiding this comment.
Unhandled CalledProcessError from failed pip install propagates
Medium Severity
The subprocess.run call with check=True sits outside the try/except block that wraps the exec call. If pip install fails (e.g., invalid package name, network error), a subprocess.CalledProcessError is raised and propagates uncaught through _run and BaseTool.run. The old os.system() call silently ignored failures and always continued to code execution. The function's contract (return type str, docstring promising an error message) implies it never throws, but now it can.


Fixes the two security issues reported in #4516.
The first issue is command injection in pip install. The library name was interpolated into an os.system() call, so a crafted library name like "numpy; rm -rf /" could execute arbitrary commands. Fixed by switching to subprocess.run() with the arguments as a list, which prevents shell interpretation of special characters.
The second issue is sandbox escape via subclasses() introspection. Python's object model lets you walk up from any object to builtins through ().class.bases[0].subclasses(), which gives access to every loaded class including importers that can load blocked modules like os. Fixed by checking submitted code for class, bases, subclasses, mro, and qualname before execution, and also blocking getattr, setattr, delattr, and breakpoint from the sandbox builtins to close related bypass paths.
Both fixes include new tests. All 20 tests in the code interpreter test file pass, including the 9 pre-existing ones.
Note
Medium Risk
Security-sensitive changes to code execution and dependency installation; could break previously-working sandboxed snippets that rely on blocked builtins or dunder-introspection patterns.
Overview
Closes two security gaps in
CodeInterpreterTool: unsafe-mode library installs no longer useos.systemstring interpolation and instead callsubprocess.run(["pip","install",...], check=True)to prevent command injection.The restricted sandbox now proactively blocks common escape primitives by removing additional builtins (
getattr/setattr/delattr/breakpoint) and rejecting code containing restricted dunder attribute names (e.g.,__class__,__bases__,__subclasses__,__mro__,__qualname__) before execution.Adds focused tests covering the command-injection fix, blocked introspection attempts, and ensuring normal sandboxed code still runs.
Written by Cursor Bugbot for commit d5ec441. This will update automatically on new commits. Configure here.