Compare commits

...
Author SHA1 Message Date
Vasanthdev2004 49b5a19551 fix(sandbox): scrub the daemon token file pointer (#677)
scrubSensitiveEnv removed ZERO_DAEMON_REMOTE_TOKEN but not its file
counterpart. TokenFromEnv accepts either form, so a sandboxed command
still inherited the path to the bridge token, and the default posture is
read-all, so it could then read the token itself.

Scrubbing the pointer is the whole fix rather than half of one: the path
comes from this variable alone and TokenFromEnv has no default location
to fall back on, so a command that never sees the variable has nothing to
guess at. This mirrors GOOGLE_APPLICATION_CREDENTIALS, which is already
scrubbed for the same reason.

Extends the existing scrub table, which compares the surviving
environment exactly, so the case fails if the entry is dropped. Verified:
without the fix the assertion reports ZERO_DAEMON_REMOTE_TOKEN_FILE
surviving into the scrubbed environment.
2026-07-28 13:51:55 +05:30
2 changed files with 12 additions and 0 deletions

No files matched your search

+7
View File
@@ -1027,6 +1027,13 @@ func scrubSensitiveEnv(env []string, additionalKeys ...string) []string {
"GH_TOKEN",
"ZERO_WEBSEARCH_API_KEY",
"ZERO_DAEMON_REMOTE_TOKEN",
// The file form of the same bridge token. TokenFromEnv accepts either,
// so scrubbing only the inline variable left the pointer readable, and
// the default sandbox posture is read-all. There is no fallback
// location to guess at, the path comes from this variable alone, so
// removing it closes the leak rather than half of it. Same reasoning as
// GOOGLE_APPLICATION_CREDENTIALS below.
"ZERO_DAEMON_REMOTE_TOKEN_FILE",
}
for _, descriptor := range providercatalog.All() {
for _, key := range descriptor.AuthEnvVars {
+5
View File
@@ -699,6 +699,11 @@ func TestScrubSensitiveEnv(t *testing.T) {
"XAI_API_KEY=xai-12345",
"HUGGINGFACE_API_KEY=hf_12345",
"GOOGLE_APPLICATION_CREDENTIALS=/home/user/sa-key.json",
// Both forms of the daemon bridge token. The inline one was already
// scrubbed; the file form was not, which left a sandboxed command the
// path to read it from (#677).
"ZERO_DAEMON_REMOTE_TOKEN=bridge-token-inline",
"ZERO_DAEMON_REMOTE_TOKEN_FILE=/home/user/.zero/remote-token",
"COMPANY_LLM_SECRET=custom-secret",
"ZERO_OAUTH_MY_SVC_CLIENT_SECRET=oauth-secret",
"zero_oauth_second_client_secret=case-insensitive-secret",