Skip to content

fix: nested globs aren't refused as /proc reads, and other sessions' transcripts are readable in the sandbox - #455

Merged
saucam merged 2 commits into
mainfrom
fix/proc-glob-false-positive
Nov 5, 2026
Merged

saucam merged 2 commits into
mainfrom
fix/proc-glob-false-positive

Conversation

@saucam

@saucam saucam commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Ordinary commands that use a nested glob were refused in every mode as reading a process's environment. Examples: cat src/*/*.ts, and searching session transcripts with grep x ~/.claude/projects/*/*.jsonl. They now run. Real reads of a process's environment are still refused.

What was wrong

The shell reader refuses any command that could read /proc/<pid>/environ. A process's environment holds its credentials. The check treated any path component that could spell "proc" as /proc, wherever it appeared in the path. A bare * can spell anything, so the second * in src/*/*.ts counted as a possible /proc, and the command was refused with "/proc//environ holds credentials".

Why it matters

  • A user's agent couldn't search other sessions' transcripts. The message also misled it into blaming a policy against reading other sessions, and on macOS it named a /proc that doesn't exist there.
  • It hit any nested glob in any mode, including the most common shapes (src/*/*.ts).
  • It came in with feat: opencode is a codeoid backend, with every tool call approved by codeoid #450 (an opencode audit round hardened the rule against spellings like /pr?c). Every test then covered /proc-shaped paths, and none covered an ordinary nested glob.

What changed

  • /proc is a top-level directory. A component that only matches "proc" as a pattern (*, pr?c) counts as /proc only:
    • at the root;
    • after something that can lead anywhere ($d, ..).
  • A component that is literally proc still counts at any depth, since it could be reached through a link to /.
  • $HOME/… is read as the home directory, as ~/… is, on a line that doesn't name HOME as a bare word (HOME=, export HOME, unset HOME).

Also: other sessions' transcripts are readable inside the sandbox

#454 hid the whole home from sandboxed commands, ~/.claude/projects included, so an agent couldn't look up another session's transcript even with the glob fixed. The owner decided they should be readable by default, so ~/.claude/projects joins the read-only home allowlist (sandbox.homeReadable). Agents can read every session's transcript and memory but never write them, and the current project's own memory stays writable, as before. To hide them, remove the entry: a secret once pasted into a session is in its transcript.

Verification

  • New regression test: seven ordinary globs that must not be refused, and eight /proc reads that still must be. Among them: /proc/*/environ, /*/1/environ, /pr?c/1/environ, /proc/$PPID/environ, "$d"/proc/1/*, ../../../../proc/1/*, a literal proc under the home, and HOME=/; cat $HOME/proc/1/*.
  • Suites: the risk suite passes (279), and the full suite passes (5,728, 0 fail). Lint and typecheck are clean.
  • The reported commands: the transcript searches from the report now rate medium (no refusal), cat src/*/*.ts rates low, and /proc reads are still refused.

🤖 Implementation context (for agents / maintainers)
  • src/daemon/risk/shell-risk.ts
    • procSecretWord: the at search requires c === "proc", or reachesRoot(i), meaning i === 0 or an earlier component containing SHAPE_ANY or ...
    • shapeComponents: a leading SHAPE_ANY from $HOME/${HOME} is replaced by ctx.home when ctx.homeFixed.
    • assessShellCommandSync computes homeFixed = !/(^|[^$\w{])HOME\b/.test(command), and Ctx gains an optional homeFixed.
  • src/daemon/risk/shell-risk.test.ts: "a nested glob isn't taken for /proc …".

🤖 Generated with Claude Code

saucam and others added 2 commits November 5, 2026 16:18
…ronment

`cat src/*/*.ts` and `grep x ~/.claude/projects/*/*.jsonl` were refused in
every mode ("/proc/<pid>/environ holds credentials"): a bare `*` component
could spell "proc", and the rule took it for /proc at any depth. /proc is
top-level: a pattern counts as it only at the root or after what can lead
anywhere (`$d`, `..`); a component that is "proc" still counts anywhere (a
link to /). `$HOME/...` is the home on a line that doesn't set HOME.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anscripts

~/.claude/projects joins the sandbox's read-only home allowlist by default,
so an agent can look up what another session did (as before the sandbox);
never writable. Removable from sandbox.homeReadable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@saucam saucam changed the title fix: a nested glob is no longer refused as a read of a process's environment fix: nested globs aren't refused as /proc reads, and other sessions' transcripts are readable in the sandbox Nov 5, 2026
@saucam
saucam merged commit aed1afb into main Nov 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants