Repository navigation
test(head): don't pass the runner's environment to the unprivileged child - #705
Merged
Merged
Conversation
…hild TestHeadPermissionDenied re-runs itself as UID 65534 when the parent is root and passed the parent's whole environment to that child. Under Bazel coverage, as root, the environment includes COVERAGE_OUTPUT_FILE, XML_OUTPUT_FILE and TEST_SRCDIR, all pointing at root-owned paths, so the child exits 2 trying to write its coverage profile. Give the child only PATH and its own TMPDIR. Plain `go test` is not affected (Go's coverage runtime only warns when GOCOVERDIR is unwritable); this keeps rshell in sync with the copy in datadog-agent (DataDog/datadog-agent#58502), where it broke CI. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
matt-dz
marked this pull request as ready for review
October 28, 2026 19:19
matt-dz
enabled auto-merge
October 28, 2026 20:48
julesmcrt
approved these changes
Oct 28, 2026
julesmcrt
left a comment
Collaborator
There was a problem hiding this comment.
Reviewed ce42337ba4dbadb692d01637feabb74de70d6563.
Findings first — both are non-blocking nits:
- The env isolation is enforced by construction only, not pinned by an assertion (
builtins/head/head_unix_test.go:74). If someone later reverts this line toos.Environ(), nothing fails on a non-root dev machine or a macOS CI job — only root-under-Bazel CI would catch it. A canary check (set a var in the parent, assert it's empty viaos.Getenvin the child branch) would make the property self-verifying in one line. Optional. PATHis inherited but never used: the re-exec'd child resolves no external programs (everything runs in-process), so it could be dropped for a truly minimal env. Harmless either way.
Everything else looks right. This changes no assertion abouthead's behavior — the child-side checks (open succeeds pre-chmod; exit 1 +permission deniedpost-chmod) are untouched, and child failures still propagate to the parent viaCombinedOutput. What it does change is the environment handed to the uid-65534 re-exec, and replacingos.Environ()with an explicitPATH/TMPDIRallowlist is the correct hygiene for any exec that drops credentials: it stops leaking the runner's env (including any CI tokens under Bazel) into a nobody-uid process, and it stopsLD_PRELOAD/DYLD_*from riding along into a credential-dropping exec even though the direction here is de-escalation so the practical hijack risk was low. The comment explaining the Bazel coverage/report/runfiles motivation is helpful.
Confirmed the package suite passes and the full file compiles under its//go:build unixtag (Windows keepshead_windows_test.go). Merge against main is clean, single commit, one file. I couldn't exercise the root re-exec branch locally (non-root host, no container available), but the revert failure mode is clear from reading: the nobody-uid child would inherit Bazel's root-ownedGOCOVERDIR/report paths, fail its writes, and the parent'srequire.NoErrorwould fire.
matt-dz
added a commit
to DataDog/datadog-agent
that referenced
this pull request
Oct 29, 2026
…verage The cgo-free sandbox tests re-exec themselves as restricted helpers. Under bazel coverage the helpers then failed after passing, trying to write coverage data: - the landlock helpers restrict themselves to the test paths, so they now exit directly once their checks pass; - the seccomp elevation helper drops privileges, so it no longer inherits the runner's environment and its root-owned coverage paths (as DataDog/rshell#705 did for head). Environment: Datadog workspace Co-Authored-By: Claude Code Opus 5.5 <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.
What does this PR do?
Gives the unprivileged child in
TestHeadPermissionDenied(the root re-exec path added in #688) onlyPATHand its ownTMPDIR, instead of the parent's whole environment.Motivation
Upstreams a fix from the rshell → datadog-agent migration (DataDog/datadog-agent#58502, commit
e790263d9a5). Underbazel coverageas root, the inherited environment containsCOVERAGE_OUTPUT_FILE,XML_OUTPUT_FILEandTEST_SRCDIR, all pointing at root-owned paths, so the child exited 2 ("opening coverage data output file ... permission denied") and failed the Agent'stests_linux-*jobs.To be clear about scope: this does not reproduce under rshell's own
go testCI. I triedgo test -coveras root withGOCOVERDIRpointing at a root-only directory, and the child still passes because Go's coverage runtime only warns when it can't write. So this is test hardening, and it keeps both copies identical so future syncs stay clean. It is not a fix for a failure rshell sees today.Testing
go test ./builtins/head/ ./analysis/: passsudo ./head.test -test.run '^TestHeadPermissionDenied$' -test.v. The unprivileged child runs and passes.Checklist
🤖 Generated with Claude Code