deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Student's GitHub Actions scanner finds zero exploits and 12 bugs in itself

A student-built taint-analysis scanner for GitHub Actions swept roughly 1,800 workflow files across some 200 repositories. Its author confirmed zero exploitable flaws — and twelve false-positive bugs in the scanner itself.

Student's GitHub Actions scanner finds zero exploits and 12 bugs in itself

A cybersecurity undergraduate at the University of Adelaide built a taint-analysis scanner for GitHub Actions, ran it over roughly 200 open-source repositories, and then manually reviewed every high-severity finding. The result, according to the author's write-up on dev.to: zero confirmed exploitable vulnerabilities — and twelve bugs in the scanner itself.

How the tool works

The tool, ghast, follows attacker-controlled inputs — an issue title, a comment body, a fork's branch name — from where they enter a workflow to wherever something interprets them. These chains are rarely visible within one step: a value can move through step-level environment variables, into a step output, then into a downstream job's needs context, before finally reaching a shell command four hops and two jobs later.

The author set one hard design constraint: GitHub's documented mitigation, routing untrusted values through the environment and quoting the read, must never be flagged. A scanner that reports the platform's own recommended safe pattern will not be run twice.

In batch mode, ghast read workflows over the GitHub API and ranked findings, covering about 1,800 workflow files across five language corpora plus a long-tail sweep. An early pass over 67 mid-sized repositories produced 61 medium-severity findings, 59 of them from a single rule. That lopsided concentration made the author suspect the rule described normal behaviour as dangerous, and checking GitHub's documentation proved the rule was flatly wrong about how the platform works. After hand-checking every high-severity result against the real source, the count stood at no exploitable bugs and a dozen scanner defects, sorted into four groups.

Patterns that matched too much

The first group consisted of ordinary detection errors. In rtk-ai/rtk, a line that correctly passed a pull request title through an environment variable and quoted it was reported as high severity, because a detector that collapsed "inside eval" and "in command position" into a single boolean mistook a quoted variable assignment for a command name — those are two different questions with different answers depending on quoting.

A generated workflow in elastic/kibana that installs a pinned CLI globally after checking out a pull request head was flagged because a substring match on "npm install" assumed the checked-out tree would execute. As the author notes, npm ci runs lifecycle scripts from the repository's package., while installing a named pinned package from a registry never reads the tree — identical prefixes, opposite security properties.

Two more examples: vercel/next.js, whose hardened pipeline was flagged high because the artifact name "preview-tarballs" substring-matched a script filename, and ant-design/ant-design, whose artifact download into a ./tmp subdirectory was called unsafe by an isolation check that rejected every relative path.

Answers that live in other files

The second group is more instructive: cases where the analysis logic was doing exactly what was written, but no single-file view could ever be correct.

ruvnet/RuView, a 93,000-star repository, scored a critical 10.0 via a six-hop chain across three jobs ending in a kubectl image update. The data flow was real and the injection shape was real — git permits dollar signs and parentheses in refnames — but workflow_run reachability depends on the triggers of a different, named workflow, and all of those triggers require write access. The fix made ghast two-pass: parse every workflow in a repository before analysing any of them.

huggingface/transformers was reported as a critical "pwn request" when it is close to the opposite. Its comment-triggered CI gates on an author allow-list and runs a timestamps check that refuses execution when the merge commit is newer than the triggering comment. Because GitHub skips a job whenever anything in its needs chain was skipped, the upstream gate protects every downstream job — something the scanner missed by reading only each job's own condition.

Why it matters

Injection through untrusted GitHub Actions contexts is an active supply-chain attack class, and usable open-source scanners for it are genuinely needed. This write-up shows why they are hard to build: workflow security semantics span files and jobs, so a single-file analyser is structurally incapable of being right, and false positives against well-hardened projects train maintainers to ignore tooling.

There is a positive reading too. Every false positive here is a project where the defences — quoted environment routing, pinned actions, sparse checkouts, isolated artifact paths, gated dependency chains — actually held. And the method of hand-verifying every finding, and treating one rule producing 59 of 61 results as a smell, is a model for how security tooling should be evaluated before anyone trusts it.

  • #github-actions
  • #supply-chain-security
  • #static-analysis
  • #ci-cd
  • #open-source

Related posts