Skip to content

[vulnhunter] VulnHunter findings in github/gh-aw #47940

Description

@github-actions

VulnHunter scan (injection + navigation phases) of the repository snapshot. One finding survived falsification; the token-handling and SSRF surface was audited and found safe.


VULN-001 — Git argument injection via unvalidated ref/path in remote import fallbacks

Type: Argument injection into git subprocess (CWE-88) → arbitrary file write / DoS
Severity: Medium (High in token-less CI / unauthenticated contexts, where the fallback path is the norm)

Affected code

  • pkg/parser/remote_download_file.go
    • downloadFileViaGit L321: exec.CommandContext(ctx, "git", "archive", "--remote="+repoURL, ref, path)
    • downloadFileViaGitClone L408: exec.CommandContext(ctx, "git", "-C", tmpDir, "checkout", ref)
  • pkg/parser/remote_resolve_sha.go
    • resolveRefToSHAViaGit L41 / L46: git ls-remote <url> <ref> and git ls-remote <url> <prefix>+ref

Attacker path

  1. Source: a workflowspec owner/repo/path@<ref>. The ref (text after @) and path are attacker-controlled via a workflow's imports: frontmatter — a third-party workflow that a victim compiles/installs (gh aw add, gh aw compile). Parsed by parseRemoteOrigin (pkg/parser/import_remote.go:41), parseWorkflowSpecParts (pkg/parser/remote_workflow_spec.go:110), and parseWorkflowSpec (pkg/cli/spec.go:332).
  2. No validation: only owner/repo are validated (spec.go:374). ref and path get no leading-dash / character check.
  3. Sink reached via the auth-error fallback: when gitutil.IsAuthError is true (no GH_TOKEN, "unauthorized", "forbidden", "permission denied", "saml enforcement" — see pkg/gitutil/gitutil.go:34), the code shells out to git with ref/path in option-parsed positional slots and no -- end-of-options separator (verified absent).
  4. Impact: git archive --remote=<url> <ref> <path> — a ref/path like --output=/victim/path makes git-archive write its archive to an attacker-chosen file → arbitrary file write/overwrite. git ls-remote <url> <ref> — a ref like --upload-pack=... is option-parsed → command/behavior control / DoS.

Why it is credible after falsification

  • Sinks use exec.CommandContext (no shell), so the existing #nosec G204 note is right about shell injection but does not cover argument injection — a ref starting with - is an option, not a value.
  • IsAuthError triggers on the very common token-less / unauthorized case, so the fallback is readily reachable (private repo, GHE host, or failing raw URL).
  • SHA validation runs on the result of resolution, not the input ref; any non-SHA ref flows to git unchanged.
  • The project already defends against this exact class elsewhere, proving scope and gap: pkg/workflow/name_validation.go:70 (rejects leading - "to prevent argument injection"), pkg/workflow/npm_validation.go:93 (npm view -- pkg), pkg/cli/update_command.go:384 (--), pkg/workflow/pip_validation.go:66. The git fallbacks use none of these.

Remediation

  1. Reject ref/path beginning with - at parse time (mirror name_validation.go / pip_validation.go).
  2. Add -- end-of-options on every git call taking a user-derived positional (git ls-remote <url> -- <ref>; for archive, -- before <path> and validate ref; avoid --output=-style values).
  3. Prefer resolving refs via the GitHub API and passing only validated 40-hex SHAs to git. Centralize a git-ref allowlist (no leading -, no .., no control chars) applied in parseWorkflowSpecParts, parseRemoteOrigin, and parseWorkflowSpec.

Areas audited and found safe (no finding)

  • Generic URL import fetch / SSRF & token leak: scheme restricted to http/https (pkg/cli/spec.go:299); Authorization: Bearer attached only to an exact GitHub host allowlist (attachImportAuthHeader), and Go's default client strips auth on cross-host redirects. The imports: path never accepts an http(s):// URL, so a third-party workflow cannot redirect fetches to an attacker host.
  • Token/secret handling: tokens are read from env / gh auth token, never logged (only var names / redacted prefixes), and only sent to the GitHub host allowlist. secrets.go values are used solely for MCP env injection.

Scan scope: injection + navigation phases against the highest-risk entry points (remote import/download, ref resolution, token handling). Not an exhaustive whole-repo scan.

Generated by 🛡️ Daily VulnHunter Scan · sonnet46 · 470.6 AIC · ⌖ 38.2 AIC · ⊞ 4K ·

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions