Summary
Sandboxed commands use a read-all/write-restricted filesystem posture, but the default credential deny list only covers ~/.aws, ~/.config/gcloud, ~/.azure, and the GOOGLE_APPLICATION_CREDENTIALS target. Zero's own credential-bearing files remain readable.
This exposes:
- the default plaintext OAuth store at
~/.config/zero/oauth-tokens.json
credentials.json when plaintext provider-key storage is selected
- both
credentials.enc and its adjacent .secret AES key
- user
config.json, which can contain inline apiKey, authHeaderValue, and secret customHeaders
Relevant code
internal/sandbox/profile.go: permissionProfileReadRoots starts with the filesystem root; credentialDenyReadPaths does not include Zero files.
internal/oauth/store.go: the default storage case is the plaintext file backend.
internal/credstore/credstore.go: encrypted storage uses credentials.enc with securefile.NewCrypter(path + ".secret").
internal/config/types.go: provider profiles can persist inline keys and credential-bearing headers.
Observed at commit 6fc1220.
Impact
Environment scrubbing does not protect credentials stored on disk. Reading both the encrypted provider store and its co-located key is equivalent to reading plaintext.
Suggested fix
- Add exact default deny-read entries for user
config.json, OAuth/MCP token stores, credentials.json, credentials.enc, and its .secret.
- Include overridden token-store locations.
- Add platform integration tests that attempt sandboxed reads of each store.
Summary
Sandboxed commands use a read-all/write-restricted filesystem posture, but the default credential deny list only covers
~/.aws,~/.config/gcloud,~/.azure, and theGOOGLE_APPLICATION_CREDENTIALStarget. Zero's own credential-bearing files remain readable.This exposes:
~/.config/zero/oauth-tokens.jsoncredentials.jsonwhen plaintext provider-key storage is selectedcredentials.encand its adjacent.secretAES keyconfig.json, which can contain inlineapiKey,authHeaderValue, and secretcustomHeadersRelevant code
internal/sandbox/profile.go:permissionProfileReadRootsstarts with the filesystem root;credentialDenyReadPathsdoes not include Zero files.internal/oauth/store.go: the default storage case is the plaintext file backend.internal/credstore/credstore.go: encrypted storage usescredentials.encwithsecurefile.NewCrypter(path + ".secret").internal/config/types.go: provider profiles can persist inline keys and credential-bearing headers.Observed at commit
6fc1220.Impact
Environment scrubbing does not protect credentials stored on disk. Reading both the encrypted provider store and its co-located key is equivalent to reading plaintext.
Suggested fix
config.json, OAuth/MCP token stores,credentials.json,credentials.enc, and its.secret.