Harden sandbox review paths

This commit is contained in:
shimmyshimmer 2026-07-19 00:34:25 -07:00
commit e3e1d38678
6 changed files with 649 additions and 72 deletions

View file

@ -34,6 +34,7 @@ import io
import json
import os
import sys
import types
# Code-interpreter convention prefixes. Remapping is gated on the prefix being
# ABSENT (see _remap) so a genuine host mount / user dir is never shadowed.
@ -56,20 +57,60 @@ _original_import = builtins.__import__
_original_import_module = importlib.import_module
def _path_is_in_sandbox(filename):
def _initial_trusted_library_roots():
"""Capture interpreter-managed package roots before sandbox code can edit sys.path."""
roots = []
for entry in sys.path:
if not isinstance(entry, str) or not entry:
continue
try:
path = os.path.realpath(entry)
except OSError:
continue
if os.path.basename(path).lower() not in {"site-packages", "dist-packages"}:
continue
if path not in roots:
roots.append(path)
return tuple(roots)
_TRUSTED_LIBRARY_ROOTS = _initial_trusted_library_roots()
def _path_is_in_roots(filename, roots):
if not isinstance(filename, str) or filename.startswith("<"):
return False
try:
cwd = os.path.realpath(os.getcwd())
path = os.path.realpath(filename)
return os.path.commonpath((cwd, path)) == cwd
return any(os.path.commonpath((root, path)) == root for root in roots)
except (OSError, ValueError):
return False
def _sandbox_code_requested_import():
def _trusted_http_client_frame(frame):
module = frame.f_globals.get("__name__", "")
root = module.split(".", 1)[0]
return root in {"httpx", "httpcore"} and _path_is_in_roots(
frame.f_globals.get("__file__"), _TRUSTED_LIBRARY_ROOTS
)
def _trusted_httpx_in_call_stack(skip = 1):
try:
frame = sys._getframe(1)
frame = sys._getframe(skip)
except ValueError:
return False
while frame is not None:
module = frame.f_globals.get("__name__", "")
if module == "httpx" or module.startswith("httpx."):
return _trusted_http_client_frame(frame)
frame = frame.f_back
return False
def _sandbox_code_requested_import(skip = 1):
try:
frame = sys._getframe(skip)
except ValueError:
return True
while frame is not None:
@ -82,9 +123,7 @@ def _sandbox_code_requested_import():
):
frame = frame.f_back
continue
if module == "__main__" or _path_is_in_sandbox(frame.f_globals.get("__file__")):
return True
return False
return not _trusted_http_client_frame(frame)
return True
@ -105,6 +144,91 @@ def _raise_blocked_network_module(root):
)
_HTTP_CORE_METADATA = frozenset(
{
"__cached__",
"__doc__",
"__file__",
"__loader__",
"__name__",
"__package__",
"__path__",
"__spec__",
}
)
class _GuardedHttpcoreModule(types.ModuleType):
"""Keep httpx working while denying cached low-level APIs to sandbox code."""
def __getattribute__(self, name):
if name not in _HTTP_CORE_METADATA and _sandbox_code_requested_import(2):
_raise_blocked_network_module("httpcore")
return types.ModuleType.__getattribute__(self, name)
def __setattr__(self, name, value):
if _sandbox_code_requested_import(2):
_raise_blocked_network_module("httpcore")
return types.ModuleType.__setattr__(self, name, value)
def __delattr__(self, name):
if _sandbox_code_requested_import(2):
_raise_blocked_network_module("httpcore")
return types.ModuleType.__delattr__(self, name)
def _guard_httpcore_backend_method(cls, method_name):
original = cls.__dict__.get(method_name)
if not callable(original) or getattr(original, "_unsloth_httpcore_backend_guard", False):
return
code = getattr(original, "__code__", None)
if code is not None and code.co_flags & 0x80: # CO_COROUTINE
async def guarded(*args, **kwargs):
if not _trusted_httpx_in_call_stack(2):
_raise_blocked_network_module("httpcore")
return await original(*args, **kwargs)
else:
def guarded(*args, **kwargs):
if not _trusted_httpx_in_call_stack(2):
_raise_blocked_network_module("httpcore")
return original(*args, **kwargs)
guarded._unsloth_httpcore_backend_guard = True
guarded.__name__ = getattr(original, "__name__", method_name)
guarded.__qualname__ = getattr(original, "__qualname__", guarded.__name__)
guarded.__doc__ = getattr(original, "__doc__", None)
setattr(cls, method_name, guarded)
def _guard_httpcore_network_backends(module):
"""Guard httpcore's connection boundary even if module attribute lookup is bypassed."""
seen = set()
for value in vars(module).values():
if not isinstance(value, type) or id(value) in seen:
continue
seen.add(id(value))
_guard_httpcore_backend_method(value, "connect_tcp")
_guard_httpcore_backend_method(value, "connect_unix_socket")
def _guard_loaded_httpcore_modules():
"""Harden httpcore modules loaded transitively by an approved high-level client."""
for name, module in tuple(sys.modules.items()):
if name != "httpcore" and not name.startswith("httpcore."):
continue
if not isinstance(module, types.ModuleType) or isinstance(module, _GuardedHttpcoreModule):
continue
spec = getattr(module, "__spec__", None)
if getattr(spec, "_initializing", False):
continue
_guard_httpcore_network_backends(module)
module.__class__ = _GuardedHttpcoreModule
def _absolute_import_name(
name,
package = None,
@ -134,17 +258,25 @@ def _guarded_import(
level = 0,
):
package = globals.get("__package__") if isinstance(globals, dict) else None
root = _blocked_network_module(_absolute_import_name(name, package, level))
absolute_name = _absolute_import_name(name, package, level)
root = _blocked_network_module(absolute_name)
if root is not None:
_raise_blocked_network_module(root)
return _original_import(name, globals, locals, fromlist, level)
module = _original_import(name, globals, locals, fromlist, level)
if isinstance(absolute_name, str) and absolute_name.split(".", 1)[0] == "httpcore":
_guard_loaded_httpcore_modules()
return module
def _guarded_import_module(name, package = None):
root = _blocked_network_module(_absolute_import_name(name, package))
absolute_name = _absolute_import_name(name, package)
root = _blocked_network_module(absolute_name)
if root is not None:
_raise_blocked_network_module(root)
return _original_import_module(name, package)
module = _original_import_module(name, package)
if isinstance(absolute_name, str) and absolute_name.split(".", 1)[0] == "httpcore":
_guard_loaded_httpcore_modules()
return module
def _network_import_audit(event, args):

View file

@ -5,6 +5,8 @@
(DuckDuckGo), Python code execution, and terminal commands."""
import ast
import base64
import binascii
import codecs
import fnmatch
import http.client
@ -207,6 +209,154 @@ def _env_assignment_is_unsafe(name: str) -> bool:
_FIND_EXEC_FLAGS = frozenset({"-exec", "-execdir", "-ok", "-okdir"})
_SANDBOX_PYTHON_ENV_VARS = frozenset(
{"PATH", "PYTHONHOME", "PYTHONPATH", "UNSLOTH_STUDIO_SANDBOXED"}
)
_PYTHON_LAUNCH_WRAPPERS = _COMMAND_PREFIXES | frozenset({"conda", "hatch", "pipx", "poetry", "uv"})
def _python_executable_token(token: str) -> bool:
base = os.path.basename(token.replace("\\", "/")).lower()
if base.endswith(".exe"):
base = base[:-4]
return bool(re.fullmatch(r"(?:python(?:w)?(?:\d+(?:\.\d+)*)?|py)", base))
def _shell_command_segments(command: str) -> list[list[str]]:
try:
lexer = shlex.shlex(command, posix = sys.platform != "win32", punctuation_chars = ";&|()`")
lexer.whitespace_split = True
tokens = list(lexer)
except ValueError:
tokens = command.split()
segments: list[list[str]] = [[]]
for token in tokens:
if token in _SHELL_SEPARATORS:
if segments[-1]:
segments.append([])
continue
segments[-1].append(token.strip("\"'"))
return [segment for segment in segments if segment]
def _segment_python_index(segment: list[str]) -> int | None:
command_index = 0
while command_index < len(segment) and _ASSIGNMENT_RE.match(segment[command_index]):
command_index += 1
if command_index >= len(segment):
return None
command = os.path.basename(segment[command_index].replace("\\", "/")).lower()
if _python_executable_token(segment[command_index]):
return command_index
if command not in _PYTHON_LAUNCH_WRAPPERS:
if command not in {"find", "fd"}:
return None
for index in range(command_index + 1, len(segment)):
if _python_executable_token(segment[index]) and any(
token in _FIND_EXEC_FLAGS for token in segment[command_index:index]
):
return index
return None
for index in range(command_index + 1, len(segment)):
if _python_executable_token(segment[index]):
return index
return None
def _python_flags_skip_sitecustomize(arguments: list[str]) -> bool:
skip_next = False
for argument in arguments:
if skip_next:
skip_next = False
continue
if argument in {"-c", "-m"} or not argument.startswith("-"):
break
if argument == "--":
break
if argument in {"--ignore-environment", "--isolated", "--no-site"}:
return True
if re.fullmatch(r"-[^-]*[SEI][^-]*", argument):
return True
if argument in {"-W", "-X", "--check-hash-based-pycs"}:
skip_next = True
return False
def _segment_mutates_sandbox_python_env(segment: list[str], python_index: int) -> bool:
before_python = segment[:python_index]
for index, token in enumerate(before_python):
assignment = _ASSIGNMENT_RE.match(token)
if assignment and token.split("=", 1)[0].upper() in _SANDBOX_PYTHON_ENV_VARS:
return True
lowered = token.lower()
if lowered in {"-i", "--ignore-environment"} and before_python:
if os.path.basename(before_python[0]).lower() == "env":
return True
if lowered.startswith("--unset="):
if token.split("=", 1)[1].upper() in _SANDBOX_PYTHON_ENV_VARS:
return True
if lowered in {"-u", "--unset"} and index + 1 < len(before_python):
if before_python[index + 1].upper() in _SANDBOX_PYTHON_ENV_VARS:
return True
return False
def _segment_persistently_mutates_sandbox_python_env(segment: list[str]) -> bool:
if not segment:
return False
first = os.path.basename(segment[0].replace("\\", "/")).lower()
if all(_ASSIGNMENT_RE.match(token) for token in segment):
return any(token.split("=", 1)[0].upper() in _SANDBOX_PYTHON_ENV_VARS for token in segment)
if first in {"unset", "unsetenv"}:
return any(token.upper() in _SANDBOX_PYTHON_ENV_VARS for token in segment[1:])
if first in {"export", "set", "setenv"}:
return any(
token.split("=", 1)[0].upper() in _SANDBOX_PYTHON_ENV_VARS for token in segment[1:]
)
return False
def _sandbox_python_startup_bypasses_guard(command: str, depth: int = 0) -> bool:
"""Detect terminal-launched Python that suppresses the sandbox sitecustomize guard."""
if depth > 4:
return True
environment_tainted = False
shell_names = {"bash", "cmd", "cmd.exe", "dash", "fish", "ksh", "sh", "zsh"}
for segment in _shell_command_segments(command):
first = os.path.basename(segment[0].replace("\\", "/")).lower()
wrapper_context = first in _PYTHON_LAUNCH_WRAPPERS
for shell_index, shell_token in enumerate(segment):
shell = os.path.basename(shell_token.replace("\\", "/")).lower()
if shell not in shell_names or (shell_index and not wrapper_context):
continue
for index in range(shell_index + 1, len(segment) - 1):
token = segment[index]
if token.lower() == "/c" or (token.startswith("-") and token.lower().endswith("c")):
nested = segment[index + 1]
if _segment_mutates_sandbox_python_env(segment, shell_index):
nested = f"PYTHONPATH=; {nested}"
if _sandbox_python_startup_bypasses_guard(nested, depth + 1):
return True
break
break
if first == "env":
for index, token in enumerate(segment):
if token in {"-S", "--split-string"} and index + 1 < len(segment):
if _sandbox_python_startup_bypasses_guard(segment[index + 1], depth + 1):
return True
elif token.startswith("--split-string="):
if _sandbox_python_startup_bypasses_guard(token.split("=", 1)[1], depth + 1):
return True
python_index = _segment_python_index(segment)
if python_index is not None:
if environment_tainted or _segment_mutates_sandbox_python_env(segment, python_index):
return True
if _python_flags_skip_sitecustomize(segment[python_index + 1 :]):
return True
environment_tainted = (
environment_tainted or _segment_persistently_mutates_sandbox_python_env(segment)
)
return False
def _find_blocked_commands(command: str) -> set[str]:
@ -2436,11 +2586,12 @@ _RENDER_HTML_SET_ATTRIBUTE_START_RE = re.compile(
re.IGNORECASE,
)
_RENDER_HTML_PROPERTY_ASSIGNMENT_START_RE = re.compile(
r"\.\s*(?P<attr>src|href|srcset|action|formaction|poster|data|ping)\s*=(?!=)",
r"\.\s*(?P<attr>src|href|srcset|action|formaction|poster|data|ping|srcdoc)\s*"
r"(?P<operator>\+=|&&=|\|\|=|\?\?=|=(?!=))",
re.IGNORECASE,
)
_RENDER_HTML_MARKUP_ASSIGNMENT_START_RE = re.compile(
r"\.\s*(?:innerHTML|outerHTML)\s*=(?!=)",
r"\.\s*(?:innerHTML|outerHTML)\s*(?:\+=|&&=|\|\|=|\?\?=|=(?!=))",
re.IGNORECASE,
)
_RENDER_HTML_MARKUP_CALL_START_RE = re.compile(
@ -2449,6 +2600,26 @@ _RENDER_HTML_MARKUP_CALL_START_RE = re.compile(
r"\s*(?:\?\.\s*)?\(",
re.IGNORECASE,
)
_RENDER_HTML_COMPUTED_ASSIGNMENT_START_RE = re.compile(
r"\[\s*(?P<member>[^\]]+)\s*\]\s*(?:\+=|&&=|\|\|=|\?\?=|=(?!=))",
re.IGNORECASE | re.DOTALL,
)
_RENDER_HTML_COMPUTED_CALL_START_RE = re.compile(
r"(?:(?P<document>\bdocument)\s*)?\[\s*(?P<member>[^\]]+)\s*\]\s*"
r"(?:\?\.\s*)?\(",
re.IGNORECASE | re.DOTALL,
)
_RENDER_HTML_REFLECT_SET_START_RE = re.compile(
r"\bReflect\s*\.\s*set\s*(?:\?\.\s*)?\(", re.IGNORECASE
)
_RENDER_HTML_OBJECT_ASSIGN_START_RE = re.compile(
r"\bObject\s*\.\s*assign\s*(?:\?\.\s*)?\(", re.IGNORECASE
)
_RENDER_HTML_OBJECT_PROPERTY_START_RE = re.compile(
r"(?P<quote>['\"]?)(?P<member>src|href|srcset|action|formaction|poster|data|ping|"
r"srcdoc|innerHTML|outerHTML)(?P=quote)\s*:\s*",
re.IGNORECASE,
)
_RENDER_HTML_NETWORK_MEMBERS = frozenset(
{
"fetch",
@ -2466,6 +2637,9 @@ _RENDER_HTML_NETWORK_ATTRIBUTES = frozenset(
)
_RENDER_HTML_URL_LIST_ATTRIBUTES = frozenset({"srcset", "ping"})
_RENDER_HTML_URL_LIST_NETWORK_RE = re.compile(r"(?:^|[\s,])(?:https?:|/)", re.IGNORECASE)
_RENDER_HTML_ACTIVE_DATA_MIME_TYPES = frozenset(
{"text/html", "application/xhtml+xml", "image/svg+xml"}
)
def _leading_js_string(expression: str) -> tuple[str, int] | None:
@ -2592,10 +2766,37 @@ def _static_js_assignment_string(expression: str) -> str | None:
return None
def _render_html_attribute_reaches_network(name: str, value: str | None) -> bool:
def _render_html_data_document_reaches_network(value: str, depth: int) -> bool:
"""Inspect executable document payloads embedded in data: URLs."""
if not value.lower().startswith("data:"):
return False
header, separator, payload = value[5:].partition(",")
if not separator:
return True
parts = [part.strip() for part in header.split(";")]
media_type = (parts[0] or "text/plain").lower()
if media_type not in _RENDER_HTML_ACTIVE_DATA_MIME_TYPES:
return False
try:
payload_bytes = urllib.parse.unquote_to_bytes(payload)
if any(part.lower() == "base64" for part in parts[1:]):
payload_bytes = base64.b64decode(b"".join(payload_bytes.split()), validate = True)
markup = payload_bytes.decode("utf-8", errors = "replace")
except (ValueError, binascii.Error):
return True
return _render_html_code_reaches_network(markup, depth + 1)
def _render_html_attribute_reaches_network(
name: str,
value: str | None,
depth: int = 0,
) -> bool:
if value is None:
return False
value = value.lstrip()
if value.lower().startswith("data:"):
return _render_html_data_document_reaches_network(value, depth)
if name in _RENDER_HTML_URL_LIST_ATTRIBUTES:
return bool(_RENDER_HTML_URL_LIST_NETWORK_RE.search(value))
return value.lower().startswith(("http:", "https:", "/"))
@ -2617,7 +2818,7 @@ class _RenderHtmlAttributeParser(HTMLParser):
if name == "xlink:href":
name = "href"
if name in _RENDER_HTML_NETWORK_ATTRIBUTES and _render_html_attribute_reaches_network(
name, value
name, value, self.depth
):
self.reaches_network = True
return
@ -2667,6 +2868,51 @@ def _js_call_arguments(code: str, offset: int) -> list[str] | None:
return None
def _render_html_assigned_member_reaches_network(
member: str, value: str | None, depth: int
) -> bool:
member = member.lower()
if member in {"innerhtml", "outerhtml", "srcdoc"}:
return value is None or _render_html_code_reaches_network(value, depth + 1)
if member in _RENDER_HTML_NETWORK_ATTRIBUTES:
return value is None or _render_html_attribute_reaches_network(member, value, depth)
return False
def _render_html_set_attribute_arguments(arguments: list[str], method: str, depth: int) -> bool:
if method == "setattributens":
name_index, value_index = 1, 2
else:
name_index, value_index = 0, 1
if len(arguments) <= value_index:
return False
name = _static_js_string(arguments[name_index])
value = _static_js_string(arguments[value_index])
if name is None:
return bool(
value is None
or _RENDER_HTML_URL_LIST_NETWORK_RE.search(value.lstrip())
or _render_html_code_reaches_network(value, depth + 1)
)
name = name.lower().rsplit(":", 1)[-1]
return _render_html_assigned_member_reaches_network(name, value, depth)
def _render_html_markup_call_reaches_network(arguments: list[str], method: str, depth: int) -> bool:
if method == "insertadjacenthtml":
if len(arguments) < 2:
return False
markup = _static_js_string(arguments[1])
else:
if not arguments:
return False
parts = [_static_js_string(argument) for argument in arguments]
markup = "".join(part for part in parts if part is not None)
if any(part is None for part in parts):
markup = None
return markup is None or _render_html_code_reaches_network(markup, depth + 1)
def _render_html_computed_network_access(code: str, depth: int = 0) -> bool:
for match in _RENDER_HTML_GLOBAL_BRACKET_RE.finditer(code):
expression = match.group(1)
@ -2689,29 +2935,15 @@ def _render_html_computed_network_access(code: str, depth: int = 0) -> bool:
arguments = _js_call_arguments(code, match.end())
if arguments is None:
return True
if match.group("method").lower() == "setattributens":
name_index, value_index = 1, 2
else:
name_index, value_index = 0, 1
if len(arguments) <= value_index:
continue
name = _static_js_string(arguments[name_index])
value = _static_js_string(arguments[value_index])
if name is None:
if value is None or _RENDER_HTML_URL_LIST_NETWORK_RE.search(value.lstrip()):
return True
continue
name = name.lower().rsplit(":", 1)[-1]
if name not in _RENDER_HTML_NETWORK_ATTRIBUTES:
continue
if value is None or _render_html_attribute_reaches_network(name, value):
if _render_html_set_attribute_arguments(arguments, match.group("method").lower(), depth):
return True
for match in _RENDER_HTML_PROPERTY_ASSIGNMENT_START_RE.finditer(code):
value = _static_js_assignment_string(code[match.end() :])
if value is None:
return True
if _render_html_attribute_reaches_network(match.group("attr").lower(), value):
name = match.group("attr").lower()
if _render_html_assigned_member_reaches_network(name, value, depth):
return True
for match in _RENDER_HTML_MARKUP_ASSIGNMENT_START_RE.finditer(code):
@ -2726,19 +2958,58 @@ def _render_html_computed_network_access(code: str, depth: int = 0) -> bool:
if arguments is None:
return True
method = (match.group("insert") or match.group("write")).lower()
if method == "insertadjacenthtml":
if len(arguments) < 2:
continue
markup = _static_js_string(arguments[1])
else:
if not arguments:
continue
parts = [_static_js_string(argument) for argument in arguments]
markup = "".join(part for part in parts if part is not None)
if any(part is None for part in parts):
markup = None
if markup is None or _render_html_code_reaches_network(markup, depth + 1):
if _render_html_markup_call_reaches_network(arguments, method, depth):
return True
for match in _RENDER_HTML_COMPUTED_ASSIGNMENT_START_RE.finditer(code):
member = _static_js_string(match.group("member"))
if member is None:
continue
value = _static_js_assignment_string(code[match.end() :])
if _render_html_assigned_member_reaches_network(member, value, depth):
return True
for match in _RENDER_HTML_COMPUTED_CALL_START_RE.finditer(code):
method = _static_js_string(match.group("member"))
if method is None:
continue
method = method.lower()
arguments = _js_call_arguments(code, match.end())
if arguments is None:
return True
if method in {"setattribute", "setattributens"}:
if _render_html_set_attribute_arguments(arguments, method, depth):
return True
elif method == "insertadjacenthtml" or (
method in {"write", "writeln"} and match.group("document")
):
if _render_html_markup_call_reaches_network(arguments, method, depth):
return True
for match in _RENDER_HTML_REFLECT_SET_START_RE.finditer(code):
arguments = _js_call_arguments(code, match.end())
if arguments is None:
return True
if len(arguments) < 3:
continue
member = _static_js_string(arguments[1])
if member is None:
continue
value = _static_js_string(arguments[2])
if _render_html_assigned_member_reaches_network(member, value, depth):
return True
for match in _RENDER_HTML_OBJECT_ASSIGN_START_RE.finditer(code):
arguments = _js_call_arguments(code, match.end())
if arguments is None:
return True
for source in arguments[1:]:
for property_match in _RENDER_HTML_OBJECT_PROPERTY_START_RE.finditer(source):
value = _static_js_assignment_string(source[property_match.end() :])
if _render_html_assigned_member_reaches_network(
property_match.group("member"), value, depth
):
return True
return False
@ -6217,6 +6488,8 @@ def _bash_exec(
# Block dangerous commands (skipped when the sandbox is disabled)
if not disable_sandbox:
if _sandbox_python_startup_bypasses_guard(command):
return "Blocked: sandboxed Python cannot disable the Studio runtime guard."
blocked = _find_blocked_commands(command)
if blocked:
return f"Blocked command(s) for safety: {', '.join(sorted(blocked))}"

View file

@ -159,6 +159,67 @@ def test_bash_blocklist_enforced_when_sandboxed(captured_popen):
assert "cmd" not in captured_popen # never reached Popen
@pytest.mark.parametrize(
"command",
[
'python -S -c "import boto3"',
'python -E -c "import boto3"',
'python -I -c "import boto3"',
'python --no-site -c "import boto3"',
'python --ignore-environment -c "import boto3"',
'python --isolated -c "import boto3"',
'env -u PYTHONPATH python -c "import boto3"',
'env --unset=UNSLOTH_STUDIO_SANDBOXED python3 -c "import boto3"',
'env -i python -c "import boto3"',
'PYTHONPATH= python -c "import boto3"',
'unset PYTHONPATH; python -c "import boto3"',
'export UNSLOTH_STUDIO_SANDBOXED=0; python -c "import boto3"',
'uv run python -S -c "import boto3"',
'bash -lc "python -I -c import\\ boto3"',
'env -S "python -S -c import\\ boto3"',
'env --split-string="python -I -c import\\ boto3"',
'env -u PYTHONPATH sh -c "python -c import\\ boto3"',
'command sh -c "python -I -c import\\ boto3"',
'find . -exec python -S -c "import boto3" ;',
],
)
def test_bash_blocks_python_startup_guard_bypasses(captured_popen, command):
out = _bash_exec(command, None, 5, "t", disable_sandbox = False)
assert "cannot disable the Studio runtime guard" in out
assert "cmd" not in captured_popen
@pytest.mark.parametrize(
"command",
[
'python -c "print(1)"',
"python script.py -S",
"echo python -S",
"python -c \"print('-S')\"",
],
)
def test_bash_allows_python_without_startup_guard_bypass(captured_popen, command):
out = _bash_exec(command, None, 5, "t", disable_sandbox = False)
assert out == "FAKEOUT"
assert "cmd" in captured_popen
@pytest.mark.parametrize(
("command", "blocked"),
[
('py -3.12 -I -c "import boto3"', True),
('C:\\Python312\\python.exe -S -c "import boto3"', True),
('set PYTHONPATH= & python -c "import boto3"', True),
('cmd /c "python -E -c import boto3"', True),
('python.exe -c "print(1)"', False),
("echo python -S", False),
],
)
def test_python_startup_guard_windows_command_parsing(monkeypatch, command, blocked):
monkeypatch.setattr(tools.sys, "platform", "win32")
assert tools._sandbox_python_startup_bypasses_guard(command) is blocked
def test_bash_blocklist_skipped_when_bypassed(captured_popen):
out = _bash_exec("rm -rf /", None, 5, "t", disable_sandbox = True)
assert out == "FAKEOUT" # blocklist skipped -> reached (faked) execution

View file

@ -1080,8 +1080,34 @@ def test_render_html_gated_only_when_networked():
assert rh("<script>const i={};i.src='./local.png'.replace('local','/api')</script>") is True
assert rh("<script>const i={};i.srcset='local.png 1x, https://evil/x 2x'</script>") is True
assert rh("<script>document.body.innerHTML='<img src=https://evil/x>'</script>") is True
assert rh("<script>document.body.innerHTML += '<img src=https://evil/x>'</script>") is True
assert rh("<script>document.body.innerHTML += '<p>Local</p>'</script>") is False
assert rh("<script>document.body.innerHTML ||= '<img src=https://evil/x>'</script>") is True
assert rh("<script>document.body.innerHTML='<p>Local</p>'</script>") is False
assert rh("<script>document.body.innerHTML=markup</script>") is True
assert rh("<script>frame.srcdoc='<img src=https://evil/x>'</script>") is True
assert rh("<script>frame.srcdoc='<h1>Local</h1>'</script>") is False
assert rh("<script>frame.srcdoc=markup</script>") is True
assert rh("<script>frame.setAttribute('srcdoc','<img src=https://evil/x>')</script>") is True
assert rh("<script>frame.setAttribute('srcdoc','<h1>Local</h1>')</script>") is False
assert rh("<script>img['src']='https://evil/x'</script>") is True
assert rh("<script>frame['srcdoc']='<img src=https://evil/x>'</script>") is True
assert rh("<script>document.body['inner'+'HTML']='<img src=https://evil/x>'</script>") is True
assert rh("<script>img['setAttribute']('src','https://evil/x')</script>") is True
assert (
rh("<script>node['insertAdjacentHTML']('beforeend','<img src=https://evil/x>')</script>")
is True
)
assert rh("<script>Reflect.set(img,'src','https://evil/x')</script>") is True
assert rh("<script>Object.assign(img,{src:'https://evil/x'})</script>") is True
assert rh("<script>Object.assign(frame,{'srcdoc':'<img src=https://evil/x>'})</script>") is True
assert rh("<script>img['src']='./local.png'</script>") is False
assert rh("<script>img['setAttribute']('src','./local.png')</script>") is False
assert rh("<script>Reflect.set(obj,'title','https://evil/x')</script>") is False
assert (
rh("<script>Object.assign(obj,{src:'./local.png',title:'https://evil/x'})</script>")
is False
)
assert rh("<script>node.outerHTML='<script>fetch(1)<\\/script>'</script>") is True
assert rh("<script>node.insertAdjacentHTML('beforeend','<img src=/api/x>')</script>") is True
assert rh("<script>document.write('<img sr','c=https://evil/x>')</script>") is True
@ -1103,6 +1129,20 @@ def test_render_html_gated_only_when_networked():
)
is False
)
assert rh('<iframe src="data:text/html,<img src=https://evil/x>"></iframe>') is True
assert (
rh('<iframe src="data:text/html,%3Cimg%20src%3Dhttps%3A%2F%2Fevil%2Fx%3E"></iframe>')
is True
)
assert (
rh('<iframe src="data:text/html;base64,PGltZyBzcmM9aHR0cHM6Ly9ldmlsL3g+"></iframe>') is True
)
assert rh('<iframe src="data:text/html,<h1>Local</h1>"></iframe>') is False
assert rh('<iframe src="data:text/plain,<img src=https://evil/x>"></iframe>') is False
assert rh('<img src="data:image/png;base64,iVBORw0KGgo=">') is False
assert rh('<object data="data:image/svg+xml,<image href=https://evil/x>"></object>') is True
assert rh('<iframe src="data:text/html;base64,not-valid-***"></iframe>') is True
assert rh("<script>frame.src='data:text/html,<img src=https://evil/x>'</script>") is True
assert (
rh(
"<script>const i=document.createElement('img');"
@ -1122,10 +1162,7 @@ def test_render_html_gated_only_when_networked():
assert rh("<script>const i={};i.setAttribute('disabled')</script>") is False
assert rh("<script>const i={};i.src='./local.png'</script>") is False
assert (
rh(
"<script>const a=document.createElement('a');"
"a.setAttribute('href','#section')</script>"
)
rh("<script>const a=document.createElement('a');a.setAttribute('href','#section')</script>")
is False
)
assert (

View file

@ -144,14 +144,8 @@ class TestLowLevelNetworkModules:
"load = getattr(importlib, 'import_' + 'module'); "
"print(load('boto3').__name__)"
),
(
"import importlib; "
"print(getattr(importlib, 'import_module')(name='boto3').__name__)"
),
(
"import importlib; "
"print(importlib.import_module(name='botocore.session').__name__)"
),
("import importlib; print(getattr(importlib, 'import_module')(name='boto3').__name__)"),
("import importlib; print(importlib.import_module(name='botocore.session').__name__)"),
("import importlib; print(vars(importlib)['import_module']('httpcore').__name__)"),
("import importlib; print(importlib.__dict__['import_module']('boto3').__name__)"),
("import builtins; print(getattr(builtins, '__import__')('botocore').__name__)"),
@ -164,10 +158,7 @@ class TestLowLevelNetworkModules:
"code",
[
"m = __import__('statistics'); print(m.mean([1, 2]))",
(
"from importlib import import_module as load; "
"print(load('statistics').mean([1, 2]))"
),
("from importlib import import_module as load; print(load('statistics').mean([1, 2]))"),
(
"import importlib; "
"print(getattr(importlib, 'import_module')(name='statistics').mean([1, 2]))"
@ -280,7 +271,7 @@ class TestUploadDenylist:
)
def test_plain_post_json_not_blocked(self):
_ok("import requests\n" 'requests.post("https://api.weather.gov/lookup", json={"k": "v"})')
_ok('import requests\nrequests.post("https://api.weather.gov/lookup", json={"k": "v"})')
class TestSandboxEnvIsolation:
@ -462,6 +453,76 @@ class TestSandboxEnvIsolation:
assert result.returncode != 0
assert "Blocked: low-level network module 'httpcore'" in result.stderr
@pytest.mark.parametrize("module_name", ["loader", "httpx"])
def test_runtime_import_guard_blocks_external_module_httpcore_import(
self, tmp_path, module_name
):
from core.inference.tools import _build_safe_env
workdir = tmp_path / "sandbox"
external = tmp_path / "external"
workdir.mkdir()
external.mkdir()
(external / f"{module_name}.py").write_text(
"name = ''.join(['http', 'core'])\nprint(__import__(name).__name__)\n",
encoding = "utf-8",
)
code = f"import sys; sys.path.insert(0, {str(external)!r}); import {module_name}"
result = subprocess.run(
[sys.executable, "-c", code],
cwd = workdir,
env = _build_safe_env(str(workdir)),
capture_output = True,
text = True,
check = False,
)
assert result.returncode != 0
assert "Blocked: low-level network module 'httpcore'" in result.stderr
@pytest.mark.parametrize(
"code",
[
(
"import httpx, sys; client = httpx.Client(); client.close(); "
"print(sys.modules['httpcore'].request)"
),
(
"import httpx, sys; client = httpx.Client(); client.close(); "
"print(sys.modules['httpcore._sync.connection_pool'].ConnectionPool)"
),
(
"import httpx, sys, types; client = httpx.Client(); client.close(); "
"module = sys.modules['httpcore']; "
"request = types.ModuleType.__getattribute__(module, 'request'); "
"request('GET', 'http://127.0.0.1:9/probe')"
),
(
"import asyncio, httpx, sys, types\n"
"async def main():\n"
" async with httpx.AsyncClient():\n"
" pass\n"
" module = sys.modules['httpcore']\n"
" pool_type = types.ModuleType.__getattribute__(module, 'AsyncConnectionPool')\n"
" async with pool_type() as pool:\n"
" await pool.request('GET', 'http://127.0.0.1:9/probe')\n"
"asyncio.run(main())"
),
],
)
def test_runtime_import_guard_blocks_cached_httpcore_access(self, tmp_path, code):
from core.inference.tools import _build_safe_env
result = subprocess.run(
[sys.executable, "-c", code],
cwd = tmp_path,
env = _build_safe_env(str(tmp_path)),
capture_output = True,
text = True,
check = False,
)
assert result.returncode != 0
assert "Blocked: low-level network module 'httpcore'" in result.stderr
@pytest.mark.parametrize("module", ["httpx", "requests", "huggingface_hub"])
def test_runtime_import_guard_keeps_supported_clients_available(self, tmp_path, module):
from core.inference.tools import _build_safe_env
@ -477,6 +538,21 @@ class TestSandboxEnvIsolation:
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == module
def test_runtime_import_guard_keeps_httpx_transport_available(self, tmp_path):
from core.inference.tools import _build_safe_env
code = "import httpx; client = httpx.Client(); print(type(client).__name__); client.close()"
result = subprocess.run(
[sys.executable, "-c", code],
cwd = tmp_path,
env = _build_safe_env(str(tmp_path)),
capture_output = True,
text = True,
check = False,
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "Client"
def test_home_points_at_sandbox_workdir(self, tmp_path):
from core.inference.tools import _build_safe_env
@ -693,15 +769,11 @@ class TestHfUploadImportGate:
def test_hf_bare_name_upload_folder_safe_allowed(self):
_ok(
"from huggingface_hub import upload_folder;"
" upload_folder(folder_path='x', repo_id='r')"
"from huggingface_hub import upload_folder; upload_folder(folder_path='x', repo_id='r')"
)
def test_hf_bare_name_create_commit_safe_allowed(self):
_ok(
"from huggingface_hub import create_commit;"
" create_commit(operations=[], repo_id='r')"
)
_ok("from huggingface_hub import create_commit; create_commit(operations=[], repo_id='r')")
def test_bare_name_upload_file_without_hf_import_allowed(self):
# No HF import -- local helper named upload_file passes.

View file

@ -34,14 +34,16 @@ def test_model_selector_trigger_label_uses_leading_tight():
def test_sidebar_account_block_uses_leading_tight():
src = _read(APP_SIDEBAR)
# Match the account-block parent div regardless of its gap utility; this
# guard is about the leading-* class, not the spacing.
# Match class membership without assuming utility order.
pattern = re.compile(
r'<div\s+className="flex\s+flex-col\s+gap-\S+\s+(\S+)\s+group-data-\[collapsible=icon\]:hidden">',
r'<div\s+className="([^"]*\bflex\b[^"]*\bflex-col\b'
r'[^"]*\bgroup-data-\[collapsible=icon\]:hidden\b[^"]*)">',
)
matches = pattern.findall(src)
assert matches, "could not find sidebar account-block parent div"
leading_classes = [m for m in matches if m.startswith("leading-")]
leading_classes = [
token for classes in matches for token in classes.split() if token.startswith("leading-")
]
assert leading_classes, f"no leading-* class on sidebar account-block parent: {matches}"
for cls in leading_classes:
assert cls == "leading-tight", f"sidebar account-block must use leading-tight, got: {cls}"