Skip to content

Validate docker image references before grype and syft scans - #50847

Merged
pelikhan merged 7 commits into
mainfrom
copilot/sighthound-fix-security-findings
Aug 6, 2026
Merged

Validate docker image references before grype and syft scans#50847
pelikhan merged 7 commits into
mainfrom
copilot/sighthound-fix-security-findings

Conversation

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

A Sighthound scan flagged five exec.Command call sites as command-injection risks. Auditing them showed all five already canonicalize and validate their inputs — but two sibling scanners that the scan did not rank in the top 5 (grype, syft) do pass manifest-derived image references to docker run unvalidated.

Audit of the flagged call sites (no changes needed)

Site Existing controls
runner_guard.go:115 gitRoot validated absolute; scan path via filepath.Rel + IsLocal + ./ prefix (blocks option injection); mount via buildDockerVolumeMount; docker path resolved
grant.go:176 imageRef allow-listed via validateDockerImageRef; policy mount via buildDockerReadonlyFileMount; container path is a constant
poutine.go:109, :229 gitRoot validated; mount validated; all other args literal
upgrade_command.go:507 Extra flag allow-listed; exe run through fileutil.ValidateExecutablePath; argv rejected on control characters

All use arg slices — no shell interpolation anywhere.

The actual gap

grypeRunOnImage and runSyftOnImage took imageRef straight from lock-file manifests into docker run, using the bare binary name docker. A reference starting with - is parsed as a docker/grype/syft option, not an image:

// before
cmd := exec.Command("docker", "run", "--rm", GrypeImage, imageRef, "-o", "json")
// imageRef = "--entrypoint=/bin/sh" → interpreted as a flag

Changes

  • grype.go, syft.go — validate imageRef with the existing validateDockerImageRef allow-list and resolve the binary via fileutil.ResolveExecutablePath("docker"), aligning with grant/poutine/runner-guard.
  • grype.go, syft.go — verbose "run directly" hints now go through shellJoinArgs instead of fmt.Sprintf, so the printed command is correctly quoted.
  • docker_args_validation.go — error messages generalized from "grant image reference" to "docker image reference" now that three scanners share the validator.
  • Tests — assert option-injection, embedded-newline, and metacharacter references are rejected before docker is invoked.

Run: https://github.com/github/gh-aw/actions/runs/31113572782> Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.2 AIC · ⊞ 8.3K ·

Comment /souschef to run again

Copilot AI linked an issue Aug 6, 2026 that may be closed by this pull request
Copilot AI and others added 2 commits August 6, 2026 12:32
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix security findings in github/gh-aw Validate docker image references before grype and syft scans Aug 6, 2026
Copilot AI requested a review from pelikhan August 6, 2026 12:38
@pelikhan
pelikhan marked this pull request as ready for review August 6, 2026 12:43
Copilot AI balanced review requested due to automatic review settings August 6, 2026 12:43
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Test Quality Sentinel analysis complete: Score 100/100 (Excellent). Both new tests (TestGrypeRunOnImage_RejectsUnsafeImageRef and TestRunSyftOnImage_RejectsUnsafeImageRef) enforce critical security design invariants for docker image reference validation. 3 attack vectors tested per function, no mock violations, 1.05:1 test:prod ratio. Ready for approval. (safeoutputs write disabled in this environment)

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (91 new lines).

@github-actions github-actions Bot 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.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Review: Validate docker image references before grype and syft scans

The security improvements are correct and well-structured. The PR properly:

  • Validates image refs via validateDockerImageRef before any exec.Command call in both grypeRunOnImage and runSyftOnImage
  • Resolves docker via fileutil.ResolveExecutablePath to avoid PATH-based injection
  • Fixes stale "grant image reference" → "docker image reference" in error messages
  • Uses shellJoinArgs for verbose logging (safe quoting)
  • Adds targeted tests covering flag-injection, newline injection, and semicolon injection

No blocking issues found.> 🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 15.9 AIC · ⊞ 5.3K

@github-actions github-actions Bot 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.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Verdict: no blocking issues

This is a solid, well-scoped fix. Both grype.go and syft.go now validate imageRef via the shared validateDockerImageRef allow-list before it reaches docker run, and resolve the docker binary via fileutil.ResolveExecutablePath, matching the pattern already used in grant.go, poutine.go, and runner_guard.go. The verbose-mode command string now uses shellJoinArgs instead of fmt.Sprintf, avoiding a printed-command quoting bug. Error message rename (grant image referencedocker image reference) correctly reflects the validator now being shared across three scanners, and no test asserts on the old string. New tests cover option-injection (--entrypoint=...), embedded-newline, and metacharacter (;) image refs for both scanners, and pass locally.

💡 Review notes
  • Verified dockerImageNamePattern/dockerImageTagPattern regex and containsControlCharacters are unchanged and still correctly reject --prefixed and control-character refs.
  • Confirmed no other call site still embeds the stale grant image reference string.
  • Ran go test ./pkg/cli/... -run 'TestGrypeRunOnImage_RejectsUnsafeImageRef|TestRunSyftOnImage_RejectsUnsafeImageRef|TestValidateDockerImageRef|TestPoutine' — all pass.
  • No correctness, concurrency, or security regressions found in the changed lines. Nothing rises to a blocking comment.
> 🔎 *Code quality review by [PR Code Quality Reviewer](https://github.com/github/gh-aw/actions/runs/31102623868)* · auto · 19.4 AIC · ⊞ 7.8K > Comment /review to run again

@github-actions github-actions Bot 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.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — the fix is correct and well-targeted; two minor test-quality issues noted.

📋 Key Themes & Highlights

Key Themes

  • Test subtest naming: both grype_test.go and syft_test.go use the raw imageRef as the subtest name, which embeds a literal newline for the alpine:latest\n--privileged case, garbling go test -v output and breaking -run filters.
  • Missing positive-path coverage: no test confirms a well-formed image ref passes validation without a false rejection.

Positive Highlights

  • ✅ Root cause correctly addressed — validateDockerImageRef applied at the call sites, not deeper, keeping the fix minimal and auditable.
  • fileutil.ResolveExecutablePath("docker") aligns grype/syft with the existing grant/poutine/runner-guard pattern — consistent architecture.
  • shellJoinArgs for the verbose hint is a nice improvement over fmt.Sprintf.
  • ✅ Error message generalization in docker_args_validation.go is clean and backward-compatible.
  • #nosec G204 comment updated with accurate justification referencing the new validation.
> 🧠 *Reviewed using Matt Pocock's skills by [Matt Pocock Skills Reviewer](https://github.com/github/gh-aw/actions/runs/31102623145)* · sonnet46 · 29.5 AIC · ⊞ 7.1K > Comment /matt to run again

Comment thread pkg/cli/grype_test.go Outdated
}

for _, imageRef := range unsafeRefs {
t.Run(imageRef, func(t *testing.T) {

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.

[/tdd] Subtest name is the raw imageRef — the embedded-newline case (alpine:latest\n--privileged) embeds a literal newline in the test name, making output garbled and breaking -run filters.

💡 Suggested fix

Use a named struct:

tests := []struct{ name, imageRef string }{
    {"option flag",      "--entrypoint=/bin/sh"},
    {"embedded newline", "alpine:latest\n--privileged"},
    {"semicolon",        "ghcr.io/org/im;age:latest"},
}
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        _, err := grypeRunOnImage(tt.imageRef, false)
        ...
    })
}

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 0ad7a0c47c: grype_test.go now uses stable named subtests instead of raw image refs, so the embedded-newline case no longer affects test names or -run filters.

Comment thread pkg/cli/syft_test.go Outdated
"ghcr.io/org/im;age:latest",
}

for _, imageRef := range unsafeRefs {

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.

[/tdd] Same subtest naming issue as in grype_test.go: using the raw imageRef as a subtest name embeds a literal newline for the second case.

💡 Suggested fix

Apply the same named-struct approach as suggested for grype_test.go to keep test output readable.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 0ad7a0c47c: syft_test.go now uses stable named subtests instead of raw image refs, matching the Grype test cleanup.

Comment thread pkg/cli/grype_test.go
t.Fatalf("Expected error for unsafe image reference %q", imageRef)
}
if !strings.Contains(err.Error(), "docker image reference") {
t.Errorf("Expected image reference validation error, got: %v", err)

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.

[/tdd] Missing positive-path test: there's no assertion that a well-formed image ref (e.g. ghcr.io/anchore/grype:v0.80.0) is not rejected. Without it, an over-strict regex change could silently break valid scans.

💡 Suggested addition
func TestGrypeRunOnImage_AcceptsValidImageRef(t *testing.T) {
    // Should reach docker (not fail validation); we expect a docker-not-found
    // or network error, NOT a validation error.
    _, err := grypeRunOnImage("ghcr.io/anchore/grype:v0.80.0", false)
    if err != nil && strings.Contains(err.Error(), "docker image reference") {
        t.Fatalf("Valid image ref was rejected: %v", err)
    }
}

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 0ad7a0c47c: added valid image-ref coverage using a fake docker executable, so the test verifies validation allows well-formed refs without contacting real Docker or the network.

Copilot AI 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.

Pull request overview

Hardens Grype and Syft scans against unsafe manifest-derived image references.

Changes:

  • Validates image references and resolves the Docker executable securely.
  • Safely quotes verbose command hints and generalizes validation errors.
  • Adds rejection tests and updates the workflow skill index.
Show a summary per file
File Description
pkg/cli/grype.go Secures Grype invocation.
pkg/cli/grype_test.go Tests unsafe reference rejection.
pkg/cli/syft.go Secures Syft invocation.
pkg/cli/syft_test.go Tests unsafe reference rejection.
pkg/cli/docker_args_validation.go Generalizes validation errors.
.github/skills/agentic-workflows/SKILL.md Adds the designer mappings reference.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 6/6 changed files
  • Comments generated: 0
  • Review effort level: Balanced

@pelikhan

pelikhan commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher still

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Maintainer triage for this PR:

Run: https://github.com/github/gh-aw/actions/runs/31113572782

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 17.2 AIC · ⊞ 8.3K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher still

Ran pr-finisher and pushed follow-up commit 0ad7a0c47c. Local gates completed; CI on the new agent-pushed HEAD needs a maintainer re-trigger before merge.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan
pelikhan merged commit ac9a4b6 into main Aug 6, 2026
27 of 29 checks passed
@pelikhan
pelikhan deleted the copilot/sighthound-fix-security-findings branch August 6, 2026 15:36
Copilot stopped work on behalf of gh-aw-bot due to an error August 6, 2026 15:46
Copilot AI requested a review from gh-aw-bot August 6, 2026 15:46
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.86.0

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[sighthound] Security findings in github/gh-aw

4 participants