env: @claude inherits PAGER and neither editor variable (closes #530) - #569
Merged
Merged
Conversation
git's exec class reaches a sandbox by fallback, both chains measured: GIT_EDITOR -> core.editor -> VISUAL -> EDITOR and GIT_PAGER -> core.pager -> PAGER. So `inherit EDITOR` hands git the host's own command string, and refusing only the GIT_* spellings would close the invisible half of a class rather than the class. All six spellings stay legal at `set` and `inherit` for any profile and stay ANNOTATED at both verbs; nothing in the table refuses. What changes is what the one shipped profile that touches the environment asks for, and it changes on a measurement rather than on the composability argument. Inside @claude's sandbox: command -v vi vim nano -> nothing command -v less more -> /usr/bin/less, /usr/bin/more An inherited EDITOR names a program that is not in there (the host's vim or code is not bound), so `git commit` fails identically with the variable and without it. An inherited PAGER=less names one that is. EDITOR and VISUAL leave [profile.claude.environ.inherit]; PAGER and ANTHROPIC_BASE_URL and NO_COLOR stay. Invariant 2's corollary: a grant wider than the need shrinks rather than acquiring a rule. No escape closes — profiles are the trusted layer and nothing untrusted chose these values. Golden diff is the review artifact: one --setenv line and two annotated `inherit` rows leave @claude's argv, environment block and profile show. Tests. TestNoBuiltinInheritsAnEditorVariable (internal/profile) is the named regression, and asserts PAGER PRESENT in the same test because "no builtin inherits an editor" is satisfied by a profile that inherits nothing at all. Negative control run both ways: re-adding EDITOR = true to base.toml fails it, removing it passes. Three tests were riding on @claude's inherit EDITOR and are repointed: the env golden fixture's host value (PAGER=less), the unchecked-sink negative control (PAGER), and the forging-rune sweep, whose ESC/CR and bidi probes now arrive through a fixture-defined profile that inherits EDITOR and VISUAL. That sweep was MEASURED unfailable for those two probes: with both fixtures disconnected from any profile it still passed, because $SNUG_PODMAN satisfies the ESC assertion and the graft fixture the bidi one. It now carries a per-probe positive control, and cutting the route fails it. VERIFY.md §6d-bis is the by-hand equivalent; §4's DEL check moves from EDITOR to PAGER because the EDITOR route no longer reaches the document. ENVIRONMENT-VARIABLES.md §3.2 rewritten: legality and what a shipped profile carries are two decisions, stated as two. No redteam round: this withdraws a grant, touches no mount generation, no seccomp filter and no host-integration surface. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #530.
What
@claudestops inheritingEDITORandVISUAL.PAGERstays, so doANTHROPIC_BASE_URLandNO_COLOR.Why, measured
git exec class reaches sandbox by fallback, both chains measured:
GIT_EDITOR -> core.editor -> VISUAL -> EDITOR,GIT_PAGER -> core.pager -> PAGER.inherithands host's own command string to git inside. Refusing onlyGIT_*spellings closes invisible half of class, not class — all six stay legal atset/inheritfor any profile, all six stay annotated. Table refuses nothing.Decision is per shipped profile, and rests on measurement not on composability argument. Inside
@claudesandbox:Inherited
EDITORnames program not there (host vim/code not bound), sogit commitfails identically with variable and without. InheritedPAGER=lessnames one that is. Invariant 2 corollary: grant wider than need shrinks, no rule added. Closes no escape — profiles are trusted layer, nothing untrusted chose these values.Golden diff (review artifact)
Tests
TestNoBuiltinInheritsAnEditorVariable(internal/profile) — named regression. Asserts PAGER PRESENT in same test: "no builtin inherits an editor" is satisfied by a profile inheriting nothing at all. Negative control both directions:EDITOR = trueback in base.toml fails it, removed passes.Three tests rode on
@claude'sinherit EDITORand are repointed — env golden fixture host value (PAGER=less), unchecked-sink negative control (PAGER), forging-rune sweep.Forging-rune sweep was MEASURED unfailable for two of its probes: with the ESC/CR and bidi fixtures disconnected from any profile the test still passed, because
$SNUG_PODMANsatisfies the\x1bassertion and the graft fixture the U+202E one. Now carries a per-probe positive control; cutting the route fails it. Probes arrive through a fixture-defined profile inheriting EDITOR/VISUAL — samepolicy.Resolvepath, sameVerbInheritentries.Also removed from
TestAnnotatedEnvPairsAShippedProfileWritesArePinnedinventory: a stale row pre-approves the next grant, and that test says so itself.Docs
VERIFY.md§6d-bis — by-hand equivalent, both commands and their real output.VERIFY.mdDEL check moved EDITOR -> PAGER: the EDITOR route no longer reaches the document. Re-run, output pinned (less�twice,lossyfalse, 0 raw DEL bytes).ENVIRONMENT-VARIABLES.md§3.2 rewritten. Legality (both verbs, for anybody) and what a shipped profile carries are two decisions, now stated as two. Stale prose deleted: it claimedGIT_*"are refused (§4.4's list)", namedforbiddenEnv(gone), and ended "Still open, still Tracker: git-config adapter leftovers (commit signing, fixed catalogue, the exec class) #35" for a closed issue.dryrun.goandvisible_test.goheader comments naming@claude's shippedinherit EDITORcorrected in place.Verification
make gategreen.SNUG_REQUIRE_SANDBOX=1 SNUG_SANDBOX_TIMEOUT=25m make integrationon this host: 209 PASS, 2 SKIP,engine tests: 46 ran, floor 46 — podman version 6.0.2. Two runs, one failure each, a DIFFERENT test each time and both in network paths this change does not touch:TestSSHReachParityBetweenAPlainRunAndNet(pasta:Couldn't set IPv6 address(es) in namespace: File exists, host load ~21) andTestContainerEgressFollowsNetProfile(connect: network is unreachable). Both pass in isolation, 2.25s and 8.80s. Suite bound raised to 25m because the default 8m does not fit this host, which predates this branch.No redteam round owed: this withdraws a grant. No mount generation, no seccomp filter, no host-integration surface.
🤖 Generated with Claude Code