Skip to content

Add no-checkout-override option#4010

Open
petetomasik wants to merge 36 commits into
mainfrom
feat/no-checkout-override
Open

Add no-checkout-override option#4010
petetomasik wants to merge 36 commits into
mainfrom
feat/no-checkout-override

Conversation

@petetomasik

Copy link
Copy Markdown
Contributor

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 single protectedEnv map 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 bypass no-command-eval.

This PR introduces an opt-in --no-checkout-override flag (env BUILDKITE_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 undermine no-command-eval.

The protection model is split into two tiers in env/protected.go: protectedEnv for vars that are always agent-authoritative (agent/mirror infrastructure), and a new checkoutOverrideScope for 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 start and bootstrap:

--no-checkout-override   Don't allow pipeline/step env, hooks, plugins, the Job API, or secrets
                         to override the agent's checkout settings (default: false)
                         [$BUILDKITE_NO_CHECKOUT_OVERRIDE]
  • Adds NoCheckoutOverride to AgentConfiguration, AgentStartConfig, BootstrapConfig, and ExecutorConfig, and wires it through to the Job API server (WithNoCheckoutOverride).
  • Splits env/protected.go into protectedEnv (always protected) and checkoutOverrideScope (conditionally locked), adds IsCheckoutOverrideScoped, and asserts the two maps are disjoint.
  • Enforces the lock at every point a job can reach checkout config: backend job-env precedence in createEnvironment (setCheckoutEnv), config refresh in ReadFromEnvironment, hook env changes, secret-to-env mappings, and Job API patch/delete.
  • Makes mirror infrastructure (BUILDKITE_GIT_MIRRORS_PATH, BUILDKITE_GIT_MIRRORS_LOCK_TIMEOUT, BUILDKITE_GIT_MIRROR_CHECKOUT_MODE) always agent-authoritative.
  • Forces no-checkout-override on when command-eval is disabled, in both agent start and bootstrap.
  • ExecutorConfig.NoCheckoutOverride has no env tag, so a hook cannot disable the lock mid-job.
  • The Job API rejection message now names the checkout lock when it is the reason for rejection.
  • Adds unit and integration coverage across env, internal/job config, agent job 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-eval auto-lock covers the case where that would otherwise weaken a protection.

Testing

  • Tests have run locally (with go test ./...). Buildkite employees may check this if the pipeline has run automatically.
  • Code is formatted (with go tool gofumpt -extra -w .)

go test ./... passes; the only failures were internal/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.

scadu and others added 30 commits April 27, 2026 12:06
Add setCheckoutEnv helper to export checkout-related vars when
no-checkout-override=false
# 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.
@petetomasik petetomasik requested review from a team as code owners June 16, 2026 21:25
@petetomasik petetomasik added the feature New user-facing feature! label Jun 16, 2026
@DrJosh9000

Copy link
Copy Markdown
Contributor

Just to let you know I plan to continue reviewing next week

@DrJosh9000 DrJosh9000 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have questions

Comment thread agent/job_runner.go
Comment thread agent/job_runner.go
petetomasik and others added 6 commits June 24, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New user-facing feature!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants