Tighten comments in the sandbox isolation hint path
This commit is contained in:
parent
f5b9e1a4ab
commit
2e91a44202
4 changed files with 23 additions and 39 deletions
|
|
@ -143,8 +143,7 @@ _BLOCKED_COMMANDS = (
|
|||
else _BLOCKED_COMMANDS_COMMON
|
||||
)
|
||||
|
||||
# Blocked commands that reach the network / another machine; hitting one steers
|
||||
# the model to fetch via code or ask for an accessible path.
|
||||
# Network/remote-reaching commands; blocking one steers the model to fetch via code or ask for a path.
|
||||
_NETWORK_BLOCKED_COMMANDS = frozenset(
|
||||
{"curl", "wget", "nc", "ncat", "netcat", "socat", "ssh", "scp", "sftp", "rsync"}
|
||||
)
|
||||
|
|
@ -2949,12 +2948,10 @@ WEB_SEARCH_TOOL = {
|
|||
},
|
||||
}
|
||||
|
||||
# Appended to python/terminal descriptions: stop models writing to /mnt/data or
|
||||
# guessing a local path for a repo the user mentioned but never uploaded.
|
||||
# Split so the Bypass Permissions variant can drop the network sentence: under
|
||||
# bypass, _python_exec/_bash_exec skip the safety analysis and curl/wget blocklist
|
||||
# (there is no network namespace), so egress works and claiming those downloads
|
||||
# are unavailable would falsely block a user-supplied remote resource.
|
||||
# Appended to python/terminal descriptions to stop models writing to /mnt/data or
|
||||
# guessing a local path. Split so the Bypass variant can drop the network sentence:
|
||||
# under bypass egress works, so claiming downloads are unavailable would wrongly
|
||||
# block a user-supplied remote resource.
|
||||
_SANDBOX_PATHS_NOTE_INTRO = (
|
||||
" The working directory is an isolated scratch space that may already hold "
|
||||
"files from earlier work in this conversation or project, plus anything you "
|
||||
|
|
@ -2980,13 +2977,11 @@ _SANDBOX_PATHS_NOTE_TAIL = (
|
|||
"you need are not here, ask the user to provide them or an exact path "
|
||||
"instead of guessing one."
|
||||
)
|
||||
# Default (sandboxed) note: steers python at public sources, keeps the curl/wget
|
||||
# restriction without overstating the host check as a hard wall.
|
||||
# Default (sandboxed) note: steers python at public sources, keeps the curl/wget restriction.
|
||||
_SANDBOX_PATHS_NOTE = (
|
||||
_SANDBOX_PATHS_NOTE_INTRO + _SANDBOX_PATHS_NOTE_NETWORK + _SANDBOX_PATHS_NOTE_TAIL
|
||||
)
|
||||
# Bypass variant: same guidance minus the network sentence (stays neutral rather
|
||||
# than claiming egress works).
|
||||
# Bypass variant: same guidance minus the network sentence.
|
||||
_SANDBOX_PATHS_NOTE_BYPASS = _SANDBOX_PATHS_NOTE_INTRO + _SANDBOX_PATHS_NOTE_TAIL
|
||||
|
||||
PYTHON_TOOL = {
|
||||
|
|
@ -3102,8 +3097,7 @@ def _with_sandbox_note(tool: dict, note: str) -> dict:
|
|||
return {**tool, "function": fn}
|
||||
|
||||
|
||||
# Bypass variants: descriptions omit the curl/wget restriction, skipped when the
|
||||
# sandbox is disabled (disable_sandbox = bypass_permissions in the tool loops).
|
||||
# Bypass variants: descriptions omit the curl/wget restriction, used when the sandbox is disabled.
|
||||
PYTHON_TOOL_BYPASS = _with_sandbox_note(PYTHON_TOOL, _SANDBOX_PATHS_NOTE_BYPASS)
|
||||
TERMINAL_TOOL_BYPASS = _with_sandbox_note(TERMINAL_TOOL, _SANDBOX_PATHS_NOTE_BYPASS)
|
||||
_BYPASS_TOOL_OVERRIDES = {
|
||||
|
|
|
|||
|
|
@ -2426,8 +2426,7 @@ _TOOL_CODE_TIP = (
|
|||
"you need is not present, ask the user to provide it or give an exact path "
|
||||
"rather than running commands against a guessed one."
|
||||
)
|
||||
# Bypass variant: drops the "internet access is limited" clause (false when the
|
||||
# sandbox is disabled), keeping the workdir-default framing.
|
||||
# Bypass variant: drops the "internet access is limited" clause, keeps the workdir-default framing.
|
||||
_TOOL_CODE_TIP_BYPASS = (
|
||||
"Use code execution for math, calculations, data processing, or to parse "
|
||||
"and analyze information from tool results. It runs in a sandbox whose "
|
||||
|
|
@ -2518,8 +2517,7 @@ async def _select_request_tools(
|
|||
# Drop the RAG tool without a scope: nothing to search over.
|
||||
if not payload.rag_scope:
|
||||
tools = [t for t in tools if t["function"]["name"] != "search_knowledge_base"]
|
||||
# A sandbox-disabled request runs python/terminal with disable_sandbox=True, so
|
||||
# the descriptions must not claim the curl/wget block that no longer applies.
|
||||
# Sandbox-disabled: descriptions must not claim the curl/wget block that no longer applies.
|
||||
if _sandbox_disabled(payload):
|
||||
tools = apply_bypass_tool_notes(tools)
|
||||
if mcp_allowed:
|
||||
|
|
@ -12878,8 +12876,7 @@ async def anthropic_messages(
|
|||
requested_studio_tools,
|
||||
payload.enabled_tools,
|
||||
)
|
||||
# A sandbox-disabled request runs unsandboxed, so drop the curl/wget
|
||||
# restriction from the python/terminal descriptions here too.
|
||||
# Sandbox-disabled: drop the curl/wget restriction from the descriptions here too.
|
||||
if _sandbox_disabled(payload):
|
||||
openai_tools = apply_bypass_tool_notes(openai_tools)
|
||||
|
||||
|
|
|
|||
|
|
@ -174,8 +174,7 @@ class TestToolActionNudge:
|
|||
assert _build_tool_action_nudge(tools = [], model_name = "Llama-3.1-8B-Instruct") == ""
|
||||
|
||||
def test_code_tip_does_not_name_a_disabled_sibling_code_tool(self):
|
||||
# has_code fires for either python or terminal, so the code tip must stay
|
||||
# tool-neutral: enabling one must never name the other as available.
|
||||
# has_code fires for either python or terminal, so the code tip must stay tool-neutral (never name the sibling).
|
||||
py_only = _build_tool_action_nudge(
|
||||
tools = [{"type": "function", "function": {"name": "python"}}],
|
||||
model_name = "Llama-3.1-8B-Instruct",
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ def test_note_does_not_claim_project_sandbox_starts_empty():
|
|||
|
||||
def test_note_does_not_claim_local_files_are_inaccessible():
|
||||
lowered = _SANDBOX_PATHS_NOTE.lower()
|
||||
# No filesystem isolation on this branch, so an exact local path is readable;
|
||||
# the note must frame the workdir as the default work location, not deny access.
|
||||
# No filesystem isolation here, so the note frames the workdir as the default work location, not a denial of access.
|
||||
assert "cannot see the user's own computer" not in lowered
|
||||
assert "default location for your work" in lowered
|
||||
|
||||
|
|
@ -67,9 +66,8 @@ def test_note_is_appended_to_both_tool_descriptions():
|
|||
|
||||
|
||||
def test_note_scopes_network_block_to_the_terminal_not_all_shell_commands():
|
||||
# The terminal keeps the network namespace and doesn't block git/pip, so the
|
||||
# note names only the blocked commands (curl/wget) and puts the allowlist on the
|
||||
# python tool rather than claiming shell network is fully blocked.
|
||||
# Terminal keeps networking (git/pip work), so the note names only curl/wget and
|
||||
# puts the allowlist on the python tool, not on all shell network.
|
||||
lowered = _SANDBOX_PATHS_NOTE.lower()
|
||||
assert "shell network commands are blocked" not in lowered
|
||||
assert "curl" in lowered and "wget" in lowered
|
||||
|
|
@ -87,9 +85,8 @@ def test_note_distinguishes_attachments_from_sandbox_uploads():
|
|||
|
||||
|
||||
def test_blocked_network_command_message_gates_code_fallback_on_tool_availability():
|
||||
# A blocked curl/wget must not claim the sandbox is offline, nor name a specific
|
||||
# remedy tool (python may be absent from the schema -- an invalid call); the
|
||||
# fallback is gated on a code-execution tool being enabled this turn.
|
||||
# A blocked curl/wget must not claim the sandbox is offline nor name a specific remedy
|
||||
# tool (python may be absent); the fallback is gated on a code tool being enabled this turn.
|
||||
msg = _bash_exec("curl https://raw.githubusercontent.com/foo/bar/main/x.py").lower()
|
||||
assert "blocked command" in msg
|
||||
assert "cannot reach other machines or remote hosts" not in msg
|
||||
|
|
@ -99,9 +96,8 @@ def test_blocked_network_command_message_gates_code_fallback_on_tool_availabilit
|
|||
|
||||
|
||||
def test_blocked_network_command_message_scopes_claim_to_the_command():
|
||||
# The block is by command name; the terminal keeps networking (git/pip work),
|
||||
# so a private host is still reachable. The message must attribute the block to
|
||||
# the command name, not assert the destination is unreachable.
|
||||
# The block is by command name and the terminal keeps networking (a private host is still
|
||||
# reachable), so the message must attribute the block to the name, not call the host unreachable.
|
||||
msg = _bash_exec("wget http://10.0.0.5/internal/repo.tar.gz").lower()
|
||||
assert "by name" in msg
|
||||
assert "not reachable" not in msg
|
||||
|
|
@ -173,9 +169,8 @@ def test_bypass_code_execution_nudge_drops_the_limited_internet_claim():
|
|||
|
||||
|
||||
def test_code_execution_nudge_does_not_deny_local_file_access():
|
||||
# No filesystem isolation here, so an exact local path is readable; the nudge
|
||||
# must frame the workdir as the default work location and still allow an exact
|
||||
# path, not deny access to the user's computer.
|
||||
# No filesystem isolation here, so the nudge frames the workdir as the default work
|
||||
# location and still allows an exact path, rather than denying access.
|
||||
from routes.inference import _TOOL_CODE_TIP
|
||||
|
||||
lowered = _TOOL_CODE_TIP.lower()
|
||||
|
|
@ -185,9 +180,8 @@ def test_code_execution_nudge_does_not_deny_local_file_access():
|
|||
|
||||
|
||||
def test_sandbox_disabled_treats_permission_mode_full_as_unsandboxed():
|
||||
# Both agent loops fold permission_mode "full" into disable_sandbox, so "full"
|
||||
# runs python/terminal unsandboxed. _sandbox_disabled keys off the effective
|
||||
# flag so the notes match what executes even if that fold is refactored away.
|
||||
# Both agent loops fold permission_mode "full" into disable_sandbox, so "full" runs
|
||||
# unsandboxed. _sandbox_disabled keys off the effective flag, not just the fold.
|
||||
from types import SimpleNamespace
|
||||
|
||||
from routes.inference import _sandbox_disabled
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue