diff --git a/studio/backend/core/inference/tools.py b/studio/backend/core/inference/tools.py index f75ea66c04..fe6d800d44 100644 --- a/studio/backend/core/inference/tools.py +++ b/studio/backend/core/inference/tools.py @@ -5853,12 +5853,12 @@ def _bash_exec( base = f"Blocked command(s) for safety: {', '.join(sorted(blocked))}." if blocked & _NETWORK_BLOCKED_COMMANDS: return ( - base + " Direct network commands are blocked in the " - "terminal. Public files on allowlisted sites (such as " - "github.com, huggingface.co, or pypi.org) can be downloaded " - "from Python code instead; the user's own machine and other " - "private or arbitrary hosts are not reachable, so for those " - "ask the user to upload the files they want you to work with." + base + " These download commands are blocked by name in " + "the terminal. Public files on allowlisted sites (such as " + "github.com, huggingface.co, or pypi.org) can be fetched " + "from Python code instead. If you need files from another " + "location, ask the user to place them in the working " + "directory or give a path the sandbox can read." ) return base elif not _harden_parent_against_proc_env_leak(): diff --git a/studio/backend/routes/inference.py b/studio/backend/routes/inference.py index 6a8385bfe9..b386103415 100644 --- a/studio/backend/routes/inference.py +++ b/studio/backend/routes/inference.py @@ -2405,12 +2405,13 @@ _TOOL_WEB_EXPANDED_TIP = ( _TOOL_CODE_TIP = ( "Use code execution for math, calculations, data processing, or to parse " "and analyze information from tool results. It runs in a sandbox whose " - "working directory is an isolated scratch space that may already hold files " - "from earlier work; it cannot access the user's own computer, and its " - "internet access is limited, so do not assume a file, folder, or repository " - "the user mentions is already present. List the working directory to see " - "what is there; if what you need is not present, ask the user to provide it " - "or give an exact path rather than running commands against a guessed one." + "working directory is an isolated scratch space that is the default " + "location for your work and may already hold files from earlier work; it " + "is not a copy of the user's computer, and its internet access is limited, " + "so do not assume a file, folder, or repository the user mentions is " + "already present. List the working directory to see what is there; if what " + "you need is not present, ask the user to provide it or give an exact path " + "rather than running commands against a guessed one." ) _TOOL_ARTIFACT_TIP = ( "For HTML, CSS, or JavaScript canvas requests, call render_html once when " diff --git a/studio/backend/tests/test_sandbox_paths_note.py b/studio/backend/tests/test_sandbox_paths_note.py index 14943c7b25..90345f82f3 100644 --- a/studio/backend/tests/test_sandbox_paths_note.py +++ b/studio/backend/tests/test_sandbox_paths_note.py @@ -95,3 +95,37 @@ def test_blocked_network_command_message_points_to_python_egress(): assert "cannot reach other machines or remote hosts" not in msg assert "python" in msg assert "github.com" in msg + + +def test_blocked_network_command_message_scopes_claim_to_the_command(): + # The block is by command name; the terminal keeps its network namespace and + # does not block git/pip, so `git clone http:///repo` can still + # reach a private host. The message must not assert the destination itself is + # unreachable, and must attribute the block to the command by name. + 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 + assert "are not reachable" not in msg + assert "hosts are not reachable" not in msg + + +def test_blocked_network_command_message_does_not_recommend_chat_upload(): + # Chat attachments land in the RAG store, not the sandbox workdir, so telling + # the user to upload the file to chat is a dead end. The message must instead + # point at an accessible path or placing the file in the working directory. + msg = _bash_exec("curl https://raw.githubusercontent.com/foo/bar/main/x.py").lower() + assert "upload" not in msg + assert "working directory" in msg or "path the sandbox can read" in msg + + +def test_code_execution_nudge_does_not_deny_local_file_access(): + # On this no-Landlock branch the child runs on the host with only cwd set, so an + # exact local path the user supplies is readable. The code-execution nudge must + # frame the workdir as the default work location, not assert the user's own + # computer is inaccessible, and it must still allow an exact path. + from routes.inference import _TOOL_CODE_TIP + + lowered = _TOOL_CODE_TIP.lower() + assert "cannot access the user's own computer" not in lowered + assert "default" in lowered and "location for your work" in lowered + assert "give an exact path" in lowered