Skip to content

threat-detection job's AWF sandbox images ignore sandbox.agent.images / container_pins (always resolve to ghcr.io) #62971

Closed

Description

@alirezamirian

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 by sandbox.agent.images)
  • ContainerPinMappings (populated by aw.json's container_pins)

As a result:

  1. gh-aw's own compiled "Download container images" step for the detection job (generated by collectDockerImages in pkg/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-managed ActionCache.ContainerPins cache (which is copied over) — but this is an internal, undocumented cache not meant for direct editing, and isn't populated for gh-aw-firewall images unless they were previously resolved through a path that writes to it.
  2. Even when (1) is worked around, AWF's own runtime image resolution (src/image-resolver.ts → resolveRuntimeImage()) is a completely separate code path inside the awf CLI itself. It only consults the container.images map or container.imageRegistry field of the awf-config.json gh-aw writes for that job. For the detection job, gh-aw only ever writes container.imageTag (tag + per-role digests, no registry), so resolveRuntimeImage() always falls back to its hardcoded default:
    const DEFAULT_IMAGE_REGISTRY = 'ghcr.io/github/gh-aw-firewall';
    This means the detection job's actual sandbox container startup always targets ghcr.io/github/gh-aw-firewall/<role>:<tag>@<digest>, even when the parent workflow has fully overridden sandbox.agent.images for 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.io is not directly reachable).
  • safe-outputs.threat-detection enabled with sandbox.agent.images set in frontmatter (and/or container_pins configured in aw.json) to redirect AWF's infrastructure images to the internal mirror.
  • Main agent job's sandbox starts successfully using the mirrored images. The detection job's "Download container images" step and/or AWF sandbox startup fail because they reference unmirrored ghcr.io/github/gh-aw-firewall/* images directly.

Suggested fix

In buildThreatDetectionWorkflowData, alongside the existing:

detectionData.SandboxConfig.Agent.Version = firewallCopy.Version

also 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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions