Skip to content

Seatbelt: unanchored file-write regexes allow creating arbitrary $HOME/.claude.json.lock* / .claude.json.tmp.* paths #70

Closed

Description

@davidxifeng

Summary

The native macOS (Seatbelt) sandbox profile includes file-write* allow rules for Claude's lock/temp paths that use regex patterns without an end anchor. As a result, a sandboxed process can create arbitrary attacker-named files and directories directly in $HOME whose names begin with .claude.json.lock or .claude.json.tmp. — paths that are not part of the intended whitelist.

Affected code

sandbox, in the native (Seatbelt) profile generation (# 6. Claude lock dirs and atomic-write temp files in $HOME):

printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.lock"))\n' "$(policy_quote "$HOME")"
printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.tmp\\."))\n' "$(policy_quote "$HOME")"

Note the contrast with the .claude.lock rules two lines above, which are correctly anchored (pair of ...lock$ + ...lock/).

PoC

Reproduced on macOS 27.0 (arm64), running inside the sandbox as an agent process:

$ printf 'test' > "$HOME/.claude.json.lockx"   # succeeds — should be denied
$ ls -la "$HOME/.claude.json.lockx"
-rw-r--r-- 1 david staff 4 Sep 28 23:18 /Users/david/.claude.json.lockx

$ printf 'test' > "$HOME/.claude.json.tmp.anything/nested"  # directory creation also allowed under the prefix
$ printf 'test' > "$HOME/just-a-test"          # correctly denied (EPERM)

Other escape routes I tested and confirmed are still blocked, for context: symlink traversal from /tmp (Seatbelt resolves the real path), launchctl submit, and AppleEvent-based file moves via Finder.

Impact

  • A sandboxed agent (or prompt-injected process) can plant arbitrarily named files/dirs in $HOME, e.g. ~/.claude.json.lockevil, ~/.claude.json.lock-backup, or nested trees under those prefixes.
  • Names closely mimic real lock/temp files (~/.claude.json.lock, ~/.claude.json.tmp.*), which is convenient for hiding state or confusing tooling/users.
  • No arbitrary $HOME write: this does not widen the sandbox to arbitrary paths. Severity is low–medium, but it violates the profile's stated intent of whitelisting only Claude's exact lock/temp paths.

Suggested fix

Anchor the regexes the same way the .claude.lock pair does, e.g.:

# directory + its contents
printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.lock(/|$)"))\n' "$(policy_quote "$HOME")"
# flat temp files only, no directories under the prefix
printf '(allow file-write* (regex #"^%s/\\.claude\\.json\\.tmp\\.[^/]+$"))\n' "$(policy_quote "$HOME")"

Happy to test a patch.

Activity

  1. nikvdp commented on Sep 29, 2026

    @nikvdp
    Owner

    Thanks, fixed in #71

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions