7ed60e1fc2 renamed the Assumption 10 heading in security.md, and the
link in sandbox.md still used the short #assumption-10 fragment, which
no heading slug matches. It now carries the full slug of the new
heading, the form 4efa892706 on feature/store-i18n uses for the old
one, so that branch's merge resolves the line by keeping main's side.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An external report flagged the Bash(*) allow in the committed
.claude/settings.json. Bash(*) stays: every command still runs inside
the OS sandbox, and deny and ask take precedence over it. Reviewing the
file against its own documentation turned up two real gaps instead.
security.md and settings.md cite an rm -rf* deny rule as the guard
against `rm -rf .` wiping the worktree and .git/, but the rule was lost
in ee30f6d446 when the deny list was rewritten. It is back.
denyRead covered ~/.aws, ~/.config/gcloud, ~/.gnupg and ~/.kube only.
~/.docker/config.json holds registry logins inline, ~/.git-credentials
and ~/.netrc hold plaintext credentials, and the default ~/.ssh/id_*
keys were readable because Assumption 10 kept ~/.ssh open for an
ask-gated git push that has since moved to deny. All nine paths are
denied now. The rest of ~/.ssh stays readable, so SSH fetches keep
known_hosts and authenticate through the agent socket. Sandboxed docker
calls pull anonymously and print a config warning on stderr.
Thanks to Hanno Horrmeyer for the report.
Reported-by: Hanno Horrmeyer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An agent cannot see whether the operator committed on top of its last
commit in the meantime. An amend then rewrites a commit it never
authored: it happened to a signed, tagged release commit, which only a
soft reset onto the original recovered. Follow-up changes land as a new
commit instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CLAUDE.md and GEMINI.md existed as per-runtime extensions of AGENTS.md, and
only one of them carried anything at all: GEMINI.md stated that it had no
additions, while CLAUDE.md held rules that bind every runtime anyway — the
permission announcement, the make-over-raw preference, the loop, chain and
heredoc bans, the stream-the-log-to-/tmp rule, the CI-evidence rule, the push
ban and the comment rule. A rule that binds every agent does not belong in one
agent's file. AGENTS.md absorbs them and now states that no tool-specific
extension file exists; agents whose runtime auto-loads a different filename
reach it through their own configuration instead.
The filenames were machinery as much as prose. The PR templates, the branch
and pull-request scope tables, the settings and security docs that justified
the loop deny by "the textual CLAUDE.md rule", six requirement documents whose
push instruction linked ../../CLAUDE.md, and a test docstring citing the
shell-loop ban all named them, and now name AGENTS.md.
scripts/meta/resolve/pr/scope.sh deliberately keeps matching CLAUDE.md and
GEMINI.md: this change deletes them, and without those patterns the change
itself would not classify as agent-scope.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RFC 6761 reserves .test for testing and .example for documentation, and the
project's own domain was doing both jobs under the documentation name. It is
what default.env hands every local deploy and what the test fixtures assert
against, so it is a testing name.
Rename it wherever it stands for the running machine: the environment
defaults, the development inventory, the workspace and trust scripts, the test
suites, and the four documents that describe real behaviour - the DNS mapping
to loopback, the browser CA trust, WSL2 resolution. Those documents would have
started lying the moment default.env changed.
The sandbox allowlist moves with them, since egress is matched per pattern and
nothing resolves the old name any more, and the sandbox document that mirrors
that list follows it.
Two places keep .example on purpose: the link-style sample in the
documentation guide, which is documentation about documentation, and every
generic fixture name elsewhere in the suite - a.example, foo.example and their
kin. Those are a larger sweep of their own and are not touched here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The schema lint demands a delegation block whenever a role claims to act on the
caller's behalf, pinning refresh, revocation and audience binding at one exact
version, because a rendered deployment bearer is a service account whatever the
metadata says. The lint can reject a missing block but cannot say why those
three facts and no others, so the design note carries that.
The requirement is brought up to date, and the agent cheatsheet gains the two
worktree skills.
Replaces 15 per-shape Bash entries that existed as sandbox auto-allow
workarounds with a single Bash(*) wildcard. Gating responsibility moves
entirely to ask (mutating gh/docker/git verbs) and deny (structurally
destructive shapes). Documentation updated across settings.md,
security.md, and sandbox.md to reflect the new model; em-dash clause
separators removed throughout per documentation.md style rules.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces narrow `Bash(PYTHONPATH=* python *)` with `Bash(*=* python* *)`
so test invocations carrying multiple env-var assignments
(e.g. `PYTHON=python3 PYTHONPATH=. python3 -m unittest ...`) and
`python3` binaries no longer trip the auto-allow heuristic. Sandbox
remains the primary containment layer, so widening the allow shape
adds no new blast radius.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three related settings.json changes:
1. Replace narrow `Bash(*=* make *)` with three broader rules covering
any prefix + make, plus composites with `> /tmp/*` redirect and
`2>&1` stderr-merge. Needed so multi-env invocations with quoted
multi-word values (e.g. `FOO=a BAR=b APPS="x y z" make target > /tmp/log 2>&1`)
auto-allow without prompting — the prior single-env rule and the
generic `* > /tmp/*` rules did not compose for this shape.
2. Replace inert `WebFetch(domain:*)` with explicit per-domain entries
for `github.com` and `raw.githubusercontent.com`. Tested empirically:
the wildcard form does not suppress the permission prompt, so a
per-domain allowlist is the only mechanism that works.
3. Add `Bash(git -C *)` to deny. Blocks cross-repo escapes via the
`-C <path>` flag, which otherwise lets the agent reach into other
working trees under `additionalDirectories`. Operators needing a
second repo must open a dedicated session there.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add `Bash(*=* make *)` so `FOO=bar make <target>` invocations (notably
`make act-workflow` with ACT_WORKFLOW/ACT_MATRIX overrides) auto-allow
without prompting — mirrors the existing `PYTHONPATH=* python *` entry.
Add `*.nektosact.com` to sandbox network allowedDomains so `act` (the
local GitHub Actions runner invoked by `make act-workflow`) can reach
its update/catalog endpoint.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add ~/.cache/gh to sandbox allowWrite so gh CLI no longer needs an
XDG_CACHE_HOME=/tmp/... prefix, and replace the five XDG-prefixed allow
entries with two generic `* > /tmp/*` redirect entries that cover any
command capturing output into /tmp. Sync sandbox.md / settings.md /
security.md and normalize the allowWrite path enumerations.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the one-off literal allow entry for a specific `gh run download`
invocation with the `XDG_CACHE_HOME=* gh run download *` wildcard that
mirrors the existing `view`/`list` env-prefix precedents, and move the
`Skill(update-config*)` wildcard from `allow` to `ask` so settings-file
mutations (permission rules, hooks, sandbox scope) always prompt — the
skill can otherwise self-escalate the agent's own permission surface.
Document both entries in settings.md with matching Env-Prefix and
Privilege-Escalation rationales.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Removes ~/.ssh from sandbox.filesystem.denyRead so SSH auth (keys, known_hosts, config) succeeds inside the sandbox. Bash(git push*) stays in ask, which becomes the effective control point for pushes.
Documents the trade-off:
- security.md: updates Assumption 9 and adds Assumption 10 covering why ~/.ssh is readable, the cost, and the mitigations (passphrase-protected keys, key rotation on incident, settings.local.json opt-out, SSH-agent-socket alternative).
- sandbox.md: removes ~/.ssh from the Read Restrictions table and links back to Assumption 10.
Collapse six literal XDG_CACHE_HOME=/tmp/gh-cache gh run/issue list
allow entries into XDG_CACHE_HOME=* gh run list *, XDG_CACHE_HOME=*
gh issue list *, gh run *, and gh issue *. Plain gh run * / gh issue *
are safe because every mutating verb is caught by gh * <verb>* ask or
gh issue transfer* deny. Env-prefix variants stay narrow (no broader
than list) because env prefix bypasses subcommand ask/deny patterns.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Allow read-only gh api GETs (plus XDG_CACHE_HOME=* prefix variant) so
log-fetch invocations with env prefix + redirect stop prompting. Route
every implicit-POST shape through ask: -f/-F/--field/--raw-field/--input
body flags and graphql*. Path-based deny rules keep precedence.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Centralise the full GitHub Actions workflow catalog in
docs/contributing/tools/actions.md as the single source of truth, link
pipeline.md and workflow.md to it, and introduce
docs/agents/action/push-trigger-pull.md as the authoritative procedure
for the push to draft CI to ready-for-review loop. Retire the stale
flat docs/contributing/tools/agents/claude.md and drop docs README
page-inventories that violate the readme_md.md scope rule.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors the XDG_CACHE_HOME=* gh run view * precedent: leading env-prefixes fall out of the auto-allow heuristic. Narrow namespace, sandbox boundaries still bound every write / read / network call.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Parallel to head *: tail is the right-hand-side twin that lives after && / | and would otherwise decline auto-allow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Allow Bash(docker *) under test-only host assumption; keep run/build/push/login in ask
- Catch synonyms: docker container run, docker image build, docker buildx build, docker image push
- New security.md enumerates 9 trust assumptions as single source for rationale
- Shorten settings.md rationales to link by assumption number (DRY)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add Bash(echo *) and Bash(head *) to allow list for chained stdout/read primitives.
- Move Bash(for *)/Bash(while *)/Bash(until *) to deny: hard-enforces the
CLAUDE.md loop-forbidden rule so violations fail fast and force refactor
to single-command / grep / xargs forms.
- Drop redundant /tmp/* scoping from Bash(XDG_CACHE_HOME=* gh run view *)
and Bash(tee *); sandbox.filesystem.allowWrite is the authoritative
write boundary, so path restrictions on the allow rule added only
maintenance, no safety.
- Update docs/contributing/tools/agents/claude/settings.md to match.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Why: `make` invocations and `… 2>&1 | tee /tmp/<log>` pipelines regularly
fall outside the sandbox auto-allow heuristic and trigger unnecessary
approval prompts. Adding targeted allow entries removes the friction
while keeping the write scope of `tee` bounded to /tmp.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add explicit Bash allow entries for command shapes that slip past the
sandbox auto-allow heuristic: `gh run list *` (jq pipes in --jq string
confuse the parser), `grep *` (to back the new CLAUDE.md rule against
for-loops around per-file greps), and `wc *` (read-only, any flag).
Reorder permissions.allow strictly ASC so the Bash entries live
alongside the other allow rules in sort order, matching how ask/deny
are laid out. Document the three new entries in settings.md and add
the "prefer flat commands" guidance to CLAUDE.md Code Execution.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add explicit Bash allow entries for command shapes where sandbox
auto-allow does not fire reliably (env-variable prefix, output
redirect, &&-chained subcommands). Document the rationale in the
settings reference so future additions follow the same pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Consolidate gh ask/deny entries via namespace-wildcard patterns, cover
compound subcommands (item-*, field-*, mark-template*), and add a lint
test that enforces ASCII-sorted permission and sandbox arrays in
.claude/settings.json.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sandbox network gate already bounds curl to allowedDomains, so the
interactive prompt added no real protection on top. Remove the rule
and the matching docs row.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Collapse explicit per-host entries into *.parent.tld patterns where the
sandbox only needs reach to a single domain family (Ansible, AWS, Docker,
Gitea, GitHub + githubusercontent, gstatic, hCaptcha, infinito, npm,
pythonhosted, Taiga). Apex domains stay listed bare. Sorted ASC with the
wildcard block first.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Network gate does not honor wildcards, so allowedDomains is enumerated
explicitly for every host the agent + tooling actually need. Adds
~/.cache/pip, ~/.venvs, ~/.ansible to allowWrite so the pre-commit
make test hook can install the editable package and run ansible.
Adds host shell / editor / tooling state files to .gitignore (and the
auto-generated .dockerignore mirror) so they never become part of the
working tree. Switches the dependabot coverage test to git ls-files
with an os.walk fallback that derives its skip list from the live
.gitignore, so it stays correct in container test environments
without .git/ while still ignoring untracked host artefacts.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds scripts/install/sandbox.sh, a distro-aware installer for the OS-level
dependencies the Claude Code sandbox backend needs (bubblewrap, socat),
and exposes it through a new `make agent-install` target. Documents the
target in docs/contributing/tools/make.md, scripts/install/README.md, and
the Claude sandbox guide so contributors on a fresh host can bring up a
working sandbox without hand-installing distro packages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move docs/contributing/tools/agents/claude.md to claude/settings.md
- Split sandbox explanations into claude/sandbox.md, add claude/README.md as index
- Enable OS-level sandbox ("sandbox.enabled: true") for project-wide filesystem isolation
- Fail-closed on missing backend ("sandbox.failIfUnavailable: true") to prevent false-security state when bwrap/sandbox-exec is absent
- Add deny rules: "git clean*", "git branch -D*"
- Remove redundant "Read(~/.cache/pre-commit/**)" allow entry
- Update CLAUDE.md to instruct agent to summarize live permission state from settings.json at session start
- Update AGENTS.md and common.md references to new doc paths
- Normalize .md formatting per documentation.md rules (em dash removal, headline emojis)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>