Request
Please add an opt-in, fail-closed local-image mode to actions/setup/sh/download_docker_images.sh. This follows the community contribution process in CONTRIBUTING.md (an implementation-plan issue rather than a direct PR).
Use case: a separate trusted acquisition job verifies images, then passes credential-free image content to an isolated agent/detection job. The consuming job must not contact a registry or receive registry credentials. Preloading images alone does not currently prevent the generated download step from pulling.
Root cause and scope
Inspected v0.88.2 (8e30bcd8897f5047051fa3971188e1dd4cdb23cf), v0.89.21 (c35393777e5604a63721d09512263b1383301d4f), and current main bec92a97e46fbdb5ba924470401725b92b18bd4e. The helper still unconditionally invokes timeout 5m docker pull --quiet $image through its retry function. AWF's later --skip-pull cannot suppress that earlier pull.
Relevant pinned sources:
Existing authored jobs.agent.pre-steps and jobs.detection.pre-steps were checked with a compile-only v0.88.2 fixture: a selector can precede downloads in both jobs, and both can depend on a separate acquisition job. This is ordering evidence, not an executed transport or security boundary.
Bounded implementation plan
- Add a selector such as
GH_AW_DOCKER_IMAGE_PULL_POLICY: unset/always retains the original download path; never selects local-only validation; empty/unknown values reject. The name/interface is a proposal for maintainer review.
- In
never, require a nonempty input set of fully qualified repository[:tag]@sha256:<64 lowercase hex> references. Handle registry ports separately from image tags. Inspect each exact reference with a bounded docker image inspect; require an exact repository@sha256:... entry in RepoDigests, not a substring, mutable tag, image ID or config digest.
- Validate the entire set and reject conflicting requested aliases before any alias is created. Only then create the exact requested local aliases with bounded
docker tag. Do not create an implicit latest alias for a versioned reference; document/test required runtime alias compatibility. On failure, stop: no pull, retry or network fallback.
- Add mock-command regression tests using the existing upstream test conventions, plus documentation of the selector and both consumer hooks. Preserve default behavior. Run normal upstream development-container validation before accepting a release.
Reproducer and acceptance cases
A fake docker executable that logs its arguments shows the unmodified helper calls pull --quiet <digest-pinned reference> even when its synthetic image inspect response would contain that exact RepoDigest. The proposed branch was exercised downstream with 33 fake-command cases (no real daemon, registry requests or credentials), including:
- exact digest present -> inspection and requested alias only, zero pulls;
- missing image, inspect failure/timeout, empty/wrong repository/digest metadata -> failure, zero pulls;
- unqualified/tag-only/malformed/newline input, unknown policy and conflicting aliases -> failure;
- all images validated before aliases; port-qualified registries handled correctly;
- alias failure -> no retry/fallback; unset/
always retains successful original behavior.
A minimal selection example for a future supported helper is:
jobs:
agent:
pre-steps:
- name: Require preloaded images
run: echo 'GH_AW_DOCKER_IMAGE_PULL_POLICY=never' >> $GITHUB_ENV
detection:
pre-steps:
- name: Require preloaded images
run: echo 'GH_AW_DOCKER_IMAGE_PULL_POLICY=never' >> $GITHUB_ENV
The current helper ignores this proposed selector; the example is not a working workaround today.
Explicit limitations
This only checks local Docker metadata availability. It is not independent image-byte, provenance, scanner or policy verification. docker save/load may lose registry RepoDigests; missing metadata must deny rather than fabricate bindings or substitute config IDs. The actual descriptor-preserving handoff and every conditional startup image/helper still need independent exercise. A hostile/shared daemon or concurrent alias mutation is outside this prototype; use an isolated disposable consumer runtime. Partial alias creation is not atomic recovery.
No request to weaken sandboxing, grant registry credentials to an agent, change default pull behavior, or approve an untested end-to-end admission design. Is this narrow opt-in helper interface acceptable for upstream implementation, or is there an existing supported no-registry startup route we should use instead?
Request
Please add an opt-in, fail-closed local-image mode to
actions/setup/sh/download_docker_images.sh. This follows the community contribution process in CONTRIBUTING.md (an implementation-plan issue rather than a direct PR).Use case: a separate trusted acquisition job verifies images, then passes credential-free image content to an isolated agent/detection job. The consuming job must not contact a registry or receive registry credentials. Preloading images alone does not currently prevent the generated download step from pulling.
Root cause and scope
Inspected v0.88.2 (
8e30bcd8897f5047051fa3971188e1dd4cdb23cf), v0.89.21 (c35393777e5604a63721d09512263b1383301d4f), and current mainbec92a97e46fbdb5ba924470401725b92b18bd4e. The helper still unconditionally invokestimeout 5m docker pull --quiet $imagethrough its retry function. AWF's later--skip-pullcannot suppress that earlier pull.Relevant pinned sources:
Existing authored
jobs.agent.pre-stepsandjobs.detection.pre-stepswere checked with a compile-only v0.88.2 fixture: a selector can precede downloads in both jobs, and both can depend on a separate acquisition job. This is ordering evidence, not an executed transport or security boundary.Bounded implementation plan
GH_AW_DOCKER_IMAGE_PULL_POLICY: unset/alwaysretains the original download path;neverselects local-only validation; empty/unknown values reject. The name/interface is a proposal for maintainer review.never, require a nonempty input set of fully qualifiedrepository[:tag]@sha256:<64 lowercase hex>references. Handle registry ports separately from image tags. Inspect each exact reference with a boundeddocker image inspect; require an exactrepository@sha256:...entry inRepoDigests, not a substring, mutable tag, image ID or config digest.docker tag. Do not create an implicitlatestalias for a versioned reference; document/test required runtime alias compatibility. On failure, stop: no pull, retry or network fallback.Reproducer and acceptance cases
A fake
dockerexecutable that logs its arguments shows the unmodified helper callspull --quiet <digest-pinned reference>even when its syntheticimage inspectresponse would contain that exact RepoDigest. The proposed branch was exercised downstream with 33 fake-command cases (no real daemon, registry requests or credentials), including:alwaysretains successful original behavior.A minimal selection example for a future supported helper is:
The current helper ignores this proposed selector; the example is not a working workaround today.
Explicit limitations
This only checks local Docker metadata availability. It is not independent image-byte, provenance, scanner or policy verification.
docker save/loadmay lose registry RepoDigests; missing metadata must deny rather than fabricate bindings or substitute config IDs. The actual descriptor-preserving handoff and every conditional startup image/helper still need independent exercise. A hostile/shared daemon or concurrent alias mutation is outside this prototype; use an isolated disposable consumer runtime. Partial alias creation is not atomic recovery.No request to weaken sandboxing, grant registry credentials to an agent, change default pull behavior, or approve an untested end-to-end admission design. Is this narrow opt-in helper interface acceptable for upstream implementation, or is there an existing supported no-registry startup route we should use instead?