Add no-checkout-override option#4010
Open
petetomasik wants to merge 36 commits into
Open
Conversation
Add setCheckoutEnv helper to export checkout-related vars when no-checkout-override=false
…heckout-override-in-agent-config
# Conflicts: # clicommand/env_set.go # clicommand/env_unset.go
Add skip checkout tests for `no-checkout-override`
Mirror checkout mode affects the checkout but stayed mutable from hooks, plugins, the Job API, and secrets while the lock was on. Also add a disjointness test for the two protection maps and correct the scope test's protection assertions.
AgentStartConfig and BootstrapConfig use inverted command-eval conventions, so their zero values mean opposite things; per-type tables make that legible and drop the kind/switch discriminator.
Contributor
|
Just to let you know I plan to continue reviewing next week |
DrJosh9000
reviewed
Jun 24, 2026
Co-authored-by: Josh Deprez <jd@buildkite.com>
It becomes 'git -c <value>' for submodule clones (an injection vector) with no backend customization, so move it from checkoutOverrideScope to the always-protected tier.
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.
Description
The agent exposes a number of checkout-related environment variables (git clone/fetch/checkout/clean flags, clone-mirror flags, submodules and submodule clone config, mirror skip-update, skip-fetch-existing-commits, skip-checkout, sparse-checkout paths). Historically the agent's handling of these was inconsistent and the protections were implicit: some were silently overwritten by agent config in
createEnvironment, others were overridable from the backend job env, hooks, plugins, the Job API, or secrets, and the rules lived in a singleprotectedEnvmap plus scattered logic. Because git flags are a shell-injection vector (for example--upload-pack,-c core.sshCommand,ext::transports), letting a job set them can be equivalent to arbitrary command execution and a way to bypassno-command-eval.This PR introduces an opt-in
--no-checkout-overrideflag (envBUILDKITE_NO_CHECKOUT_OVERRIDE). When enabled, the agent's checkout configuration becomes authoritative: a defined set of checkout-related vars are write-locked against the backend job env, hooks, plugins, the Job API, and secrets. The lock is also forced on automatically whenever command-eval is disabled, so git flags can no longer be used to undermineno-command-eval.The protection model is split into two tiers in
env/protected.go:protectedEnvfor vars that are always agent-authoritative (agent/mirror infrastructure), and a newcheckoutOverrideScopefor checkout vars that stay overridable by default but become locked when the flag is set. The two maps are kept disjoint (enforced by a test). The default (flag off) deliberately keeps checkout vars overridable so jobs can still tailor their own checkout; opting in is what makes the agent authoritative.Context
Linear project: https://linear.app/buildkite/project/improve-available-agent-git-checkout-features-f8ef302bd30d/overview
Changes
New CLI flag, shared by
agent startandbootstrap:NoCheckoutOverridetoAgentConfiguration,AgentStartConfig,BootstrapConfig, andExecutorConfig, and wires it through to the Job API server (WithNoCheckoutOverride).env/protected.gointoprotectedEnv(always protected) andcheckoutOverrideScope(conditionally locked), addsIsCheckoutOverrideScoped, and asserts the two maps are disjoint.createEnvironment(setCheckoutEnv), config refresh inReadFromEnvironment, hook env changes, secret-to-env mappings, and Job API patch/delete.BUILDKITE_GIT_MIRRORS_PATH,BUILDKITE_GIT_MIRRORS_LOCK_TIMEOUT,BUILDKITE_GIT_MIRROR_CHECKOUT_MODE) always agent-authoritative.no-checkout-overrideon when command-eval is disabled, in bothagent startandbootstrap.ExecutorConfig.NoCheckoutOverridehas noenvtag, so a hook cannot disable the lock mid-job.env,internal/jobconfig,agentjob runner, and the hooks/plugins/secrets/Job API paths, including an end-to-end test that command-eval off auto-locks checkout vars.Note on default behavior: with the flag off (the default), checkout-scoped vars are now consistently overridable from the backend job env and secrets. This is intentional, and the
no-command-evalauto-lock covers the case where that would otherwise weaken a protection.Testing
go test ./...). Buildkite employees may check this if the pipeline has run automatically.go tool gofumpt -extra -w .)go test ./...passes; the only failures wereinternal/shell's signal tests (TestContextCancelInterrupts,TestInterrupt), which require OS signal delivery to child processes and fail only in a sandboxed environment unrelated to this change (they pass with real signal delivery).Disclosures / Credits
Claude Code (Opus 4.8) ran a structured code review of the branch and implemented the resulting fixes: the checkout-override env scope refinements, locking sparse-checkout paths in
createEnvironment, the doc/comment clarifications, the Job API rejection message, and the end-to-end no-command-eval auto-lock test. I reviewed and verified each change. The core feature was implemented prior to that review.