Repository navigation
Description
Body
Summary
When safe-outputs.threat-detection is enabled on a workflow that overrides AWF's infrastructure container images — either via sandbox.agent.images in frontmatter, or via container_pins in .github/workflows/aw.json — the override is applied correctly to the main agent job, but is silently ignored for the detection job. The detection job's AWF sandbox always resolves its container images against the default ghcr.io/github/gh-aw-firewall registry, regardless of any configured override.
This makes it impossible to run threat detection at all in air-gapped / network-isolated environments where ghcr.io is not directly reachable and all container pulls must go through an internal mirror.
Root cause
buildThreatDetectionWorkflowData() (pkg/workflow/threat_detection_external.go) constructs a fresh, minimal WorkflowData for the detection job. It copies SandboxConfig.Agent.Version from the parent workflow data, but does not copy:
SandboxConfig.Agent.Images(populated bysandbox.agent.images)ContainerPinMappings(populated byaw.json'scontainer_pins)
As a result:
- gh-aw's own compiled "Download container images" step for the detection job (generated by
collectDockerImagesinpkg/workflow/docker.go) can still be redirected to a mirror if the raw image reference happens to already exist in.github/aw/actions-lock.json's auto-managedActionCache.ContainerPinscache (which is copied over) — but this is an internal, undocumented cache not meant for direct editing, and isn't populated forgh-aw-firewallimages unless they were previously resolved through a path that writes to it. - Even when (1) is worked around, AWF's own runtime image resolution (
src/image-resolver.ts→resolveRuntimeImage()) is a completely separate code path inside theawfCLI itself. It only consults thecontainer.imagesmap orcontainer.imageRegistryfield of theawf-config.jsongh-aw writes for that job. For the detection job, gh-aw only ever writescontainer.imageTag(tag + per-role digests, no registry), soresolveRuntimeImage()always falls back to its hardcoded default:This means the detection job's actual sandbox container startup always targetsconst DEFAULT_IMAGE_REGISTRY = 'ghcr.io/github/gh-aw-firewall';
ghcr.io/github/gh-aw-firewall/<role>:<tag>@<digest>, even when the parent workflow has fully overriddensandbox.agent.imagesfor the main job.
Because of (1) and (2) together, there is currently no supported configuration that lets the detection job's AWF sandbox use mirrored/overridden container images — pre-fetching a mirror-tagged reference doesn't help, because Docker treats mirror/agent:tag@digest and ghcr.io/.../agent:tag@digest as distinct references even when the digest (content) is identical, so the runtime docker compose up step still requires network access to real ghcr.io.
Expected behavior
buildThreatDetectionWorkflowData() should also copy SandboxConfig.Agent.Images and ContainerPinMappings from the parent WorkflowData into the detection job's data, so that the same image-override mechanisms that work for the main agent job also apply consistently to the threat-detection job.
Environment
- Self-hosted runners with network-isolated egress (only an internal container registry mirror is reachable;
ghcr.iois not directly reachable). safe-outputs.threat-detectionenabled withsandbox.agent.imagesset in frontmatter (and/orcontainer_pinsconfigured inaw.json) to redirect AWF's infrastructure images to the internal mirror.- Main
agentjob's sandbox starts successfully using the mirrored images. Thedetectionjob's "Download container images" step and/or AWF sandbox startup fail because they reference unmirroredghcr.io/github/gh-aw-firewall/*images directly.
Suggested fix
In buildThreatDetectionWorkflowData, alongside the existing:
detectionData.SandboxConfig.Agent.Version = firewallCopy.Versionalso copy:
detectionData.SandboxConfig.Agent.Images = firewallCopy.Images
detectionData.ContainerPinMappings = data.ContainerPinMappings(with appropriate nil-checks / deep-copy as needed, consistent with how Version is already handled).
Activity