Skip to content

Confine provider transcript reads to each egg's recorded session - #28

Merged
ehrlich-b merged 7 commits into
mainfrom
fix/transcript-import-confinement
Oct 7, 2026
Merged

ehrlich-b merged 7 commits into
mainfrom
fix/transcript-import-confinement

Conversation

@ehrlich-b

@ehrlich-b ehrlich-b commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Security fixes for shared roosts, found by adversarial review of harden/all and reproduced on main 8cf1360 by the new tests.

1. Cross-user transcript read. On a shared roost (several users, one OS account, per-user provider homes), a caller who knew another user's Claude provider session ID could replace their own writable .claude/projects/<encoded-CWD> or hook directory with a symlink to the victim's. session_read, the conversation views and history capture then imported the victim's messages into the caller's journal.
Fix: transcript and hook discovery, imports, history capture, restore, resume and fork walk the pinned provider home component by component with O_NOFOLLOW and read through descriptors. They accept only provider IDs the egg itself recorded: the launch binding, or a later SessionStart from its own hook spool, so /clear and /resume keep working.

2. Cross-user settings read. --settings .../alias/.credentials.json, where alias links into another user's provider home, copied that file into claude-settings.json, which is readable inside the caller's sandbox.
Fix: settings reads are authorized against the compiled session policy and traversed with pinned no-follow descriptors. Directory symlinks and hardlinks are refused.

Behavior changes worth a look:

  • Sessions with no recorded provider ID (started by v0.147-era eggs) no longer guess a transcript by directory scan; archived resume still works.
  • Codex sessions resumed without hooks now record the validated resume ID, so capture continues. Fresh unbound sessions report "capture unavailable" instead of capturing nothing silently.
  • Browser resume after /clear uses the current conversation. Before this PR's own fix it would have been refused; a live fork after /resume carries the current conversation (it used the launch one on main).
  • docs/security.md: archives captured by pre-upgrade eggs are unverified, and how to remove them.

Evidence:

  • The security regression tests (internal/egg/transcript_confinement_test.go, settings and fork/resume tests) fail on 8cf1360 and pass here.
  • The three integration tests that first failed in CI pass with realistic fixtures.
  • CI is green on all nine jobs at 739af48.
  • Three adversarial review rounds; the last two passed, and their medium findings are fixed in 551333c and 739af48.

@ehrlich-b
ehrlich-b merged commit 739af48 into main Oct 7, 2026
9 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.

1 participant