Repository navigation
sandbox exec always runs commands through a login shell, so sandbox-user startup files run before the requested command #2668
Description
Agent Diagnostic
Investigated from a source read of main at 5e2f0d1, not from a live cluster (this host cannot run a gateway — glibc 2.31, below the 2.32 minimum).
- Searched existing issues for
exec login shell profile: no matching issue found. - Latest release checked: v0.0.101. The code path below is unchanged on
mainat5e2f0d1. - Traced the full
sandbox execpath rather than reproducing, because the behavior is unconditional in source:handle_exec_sandboxbuilds a command string from the request, flattening argv and prepending--envpairs as shell assignments —crates/openshell-server/src/grpc/sandbox.rs:1776-1798.- It relays that string over the supervisor SSH transport —
stream_exec_over_relay,crates/openshell-server/src/grpc/sandbox.rs:1800-1806. - The supervisor spawns it under a login shell —
spawn_pipe_exec,crates/openshell-supervisor-process/src/ssh.rs:967and992-996.
- Confirmed the PTY path does the same at
crates/openshell-supervisor-process/src/ssh.rs:833-835. - Downstream evidence: [Ubuntu 24.04][Security] DCode connect probe executes sandbox-user login profile before failing closed NemoClaw#8624, where a QA-planted
/sandbox/.bash_profileran and left a marker file during a managed probe that passes exact argv and setsBASH_ENV=/ENV=to empty.
Description
sandbox exec runs every command through /bin/bash -lc, so the sandbox user's .profile / .bash_profile / .bashrc are sourced before the requested command starts.
// crates/openshell-supervisor-process/src/ssh.rs:992-996
|command| {
let mut c = Command::new("/bin/bash");
// Use login shell (-l) so that .profile/.bashrc are sourced and
// tool-specific env vars (VIRTUAL_ENV, UV_PYTHON_INSTALL_DIR, etc.)
// are available without hardcoding them here.
c.arg("-lc").arg(command);
c
}The intent is clear and reasonable for interactive and tool-discovery use. The problem is that there is no way to opt out, which makes sandbox exec unsuitable for a managed or trusted probe:
- Argv is not preserved.
build_remote_exec_commandjoins argv into a single shell string, so a caller passing exact argv after--still gets shell evaluation. - A trusted launcher cannot defend itself. A caller that execs a root-owned, mode-0755 helper still has the user's startup files run before that helper's first instruction.
--envcannot suppress it.--env BASH_ENV=and--env ENV=become command-prefix assignments inside the string passed tobash -lc, so they apply only after the login shell has already sourced the profile.
Consequences for a caller that needs a trusted result:
- Output integrity. A startup file writing to stdout/stderr interleaves with the command's output, so a caller parsing that output cannot distinguish command evidence from user-controlled text.
- Side effects. A startup file executes on every exec, so it can create files or mutate state even when the requested command is read-only and the caller's boundary intends to bypass user startup files.
Reproduction Steps
-
Create a sandbox and, as the sandbox user, write
/sandbox/.bash_profile:printf 'echo PROFILE-RAN >&2\ntouch /sandbox/profile-ran\n' > /sandbox/.bash_profile
-
From the host, run a command with exact argv and the shell startup variables cleared:
openshell sandbox exec -n <name> --no-tty --env BASH_ENV= --env ENV= -- /bin/sh -c 'printf OK'
-
Observe
PROFILE-RANon stderr, and/sandbox/profile-ranpresent, even though the requested command is/bin/sh -c 'printf OK'and both startup variables were cleared.
Environment
- OS: Ubuntu 24.04 (downstream report), source read on Ubuntu 20.04
- OpenShell: v0.0.85 in the downstream report; behavior verified unchanged in source on
mainat5e2f0d1, and v0.0.101 is the latest release - Latest release checked: yes
- Existing issues searched: yes, no duplicate found
Related issues
- feat(server): replace shell string exec with structured protocol (SEC-002 follow-up) #556 proposed replacing the shell-string exec with a structured protocol and was closed as
not plannedon 2026-05-01. I am not re-proposing that design here. This issue asks only for a way to opt out of profile sourcing, which does not require changing the transport. - bug(sandbox): SSH exec hardcodes /bin/bash and breaks BYOC images without Bash #2290 reports that these same two spawn sites hardcode
/bin/bashand break BYOC images without Bash. A fix there and a fix here touch the same lines, so they may be worth solving together.
Suggested direction
Given #556, the least invasive option that still fixes this:
- An explicit opt-out on
ExecSandboxRequest, for examplelogin_shell: booldefaulting to today's behavior, so every existing caller is unaffected and only a caller that asks for a clean environment gets one. - Alternatively, keep the login shell but apply request
environmententries to the shell process itself rather than as command-prefix assignments, soBASH_ENV=andENV=are honored before startup files are sourced. This is smaller, but it does not address.profilesourcing from-l, so it only partly fixes the problem.
I am happy to send a PR for whichever direction you prefer.
Activity
letv1nnn commentedon Aug 13, 2026
Hey @Dongni-Yang, I'd like to take this one.
Dongni-Yang commentedon Aug 13, 2026
Sure, go ahead. Note that I'm not a maintainer of OpenClaw, so I can't assign you to this issue or merge your PR.
krishicks commentedon Aug 13, 2026
@Dongni-Yang I'm working on revising our issue/bug template and had my agent rewrite this issue in the new style. Can you review this and tell me if it accurately captures your issue?
User Story
As a platform operator or integration author running managed checks inside an OpenShell sandbox, I want to execute a command without loading sandbox-user shell startup files so that the command’s output and side effects reflect only the requested operation.
Problem Statement
openshell sandbox execalways starts the requested command through a login shell. This causes sandbox-controlled files such as.profile,.bash_profile, and indirectly.bashrcto run before the requested command.Callers cannot opt out of this behavior. Passing an explicit executable after
--, disabling TTY allocation, or clearingBASH_ENVandENVdoes not prevent login-profile execution because OpenShell starts the login shell before applying those command-level environment assignments.Impact / Why This Matters
Login-shell behavior is useful for interactive development because it makes tool-specific environment configuration available automatically. It is insufficient for managed probes, health checks, validation commands, and integrations that need predictable output and side effects.
A sandbox user can modify a startup file to:
Launching a root-owned or otherwise trusted helper does not address the problem because the startup file runs before that helper’s first instruction. Callers must currently avoid
sandbox exec, modify the sandbox user’s profile, or add custom result-validation logic. These workarounds are fragile and preventsandbox execfrom serving as a predictable execution boundary for automation.Proposed Design
Add an explicit execution mode that runs a command without loading shell startup files while preserving the current login-shell behavior by default.
For example:
The user-facing behavior should be:
The CLI help and sandbox documentation should explain the tradeoff:
This design should also account for images that provide a POSIX shell but not Bash, as reported in #2290, so the new mode does not introduce or preserve an unnecessary /bin/bash requirement.
fix(advisor): bypass sandbox login profiles (#11590)