← Actions Artifact Poisoning Check
Actions Artifact Poisoning Check vs. reading the workflow YAML by hand
This page names no company or product — reading a workflow file before trusting it is a habit most maintainers already have, not a competing tool. The gap is not carelessness: the vulnerable pattern this checker looks for is a relationship between two steps that can sit many lines apart, neither one suspicious on its own.
What a manual read catches reliably
An obviously dangerous single line — a run: step that pipes curl output
straight into bash, or a trigger that looks wrong on its face. Those stand out to a reviewer
scanning top to bottom, and this tool does not replace that kind of reading.
What a manual read struggles with, and why
| Case | Reading by hand | This checker |
|---|---|---|
| A download-artifact step and the run step that later executes its contents | Can sit many unrelated-looking steps apart in the same job; neither line looks suspicious read on its own | Tracks the download across every subsequent step in the job body and flags the pair together, with both line numbers |
The workflow_run trigger's significance | Easy to read past, especially listed alongside other triggers on one line -- its meaning here is about who controls the artifact's bytes, not the keyword itself | Checks for the trigger explicitly before treating a download-and-execute pair as a finding, so the connection is made whether or not a reader consciously registered the trigger |
A download-artifact step with no name: input | An absence, not a suspicious presence -- there is no unusual text to catch a reader's eye, only a missing one | Flags every download-artifact step with no name: regardless of what else looks ordinary around it |
| Whether execution "looks like" execution | chmod +x, ./script, bash, source, python, node, and eval all read as ordinary build or test commands out of context | Matches the same execution-shaped patterns specifically in a run step that follows a download in the same job, not in isolation |
None of this is about attention or diligence. The finding lives in a relationship between two steps, not in either step alone -- which is exactly the shape of thing a linear read is worst at holding onto across a long job body.
When to still read the file yourself
This tool looks for exactly two patterns: a workflow_run job that downloads an artifact and
later runs something that looks like execution, and an unscoped download-artifact step. It reports only
those. It does not evaluate anything else in the workflow, does not know whether a matched run:
step is actually dangerous in context versus a false positive, and makes no network call to check anything
beyond the pasted text. Judging the surrounding job logic, and everything outside these two patterns, is
still a job for reading the workflow yourself.
Common questions
What does reading a workflow file by hand catch reliably?
An obviously dangerous single line: a run: step that pipes curl output straight into bash, or a workflow trigger that looks wrong on its face. Those stand out to a reviewer scanning a file top to bottom, and this tool does not replace that kind of reading.
Why is the artifact-poisoning pattern specifically hard to catch by reading?
Because the two halves of it are rarely adjacent. A download-artifact step usually appears early in a job to fetch build output; the step that executes something from disk -- a chmod +x, a ./script.sh, a bash some-file -- can appear many steps later, often looking like an unrelated build or test step on its own. Neither line is suspicious in isolation. The finding only exists in the relationship between the two, which requires holding the fact that this job downloaded a fork-influenced artifact in mind while reading every subsequent step until one of them executes something.
Why does the workflow_run trigger matter, and is it obvious from the file?
workflow_run is what makes the downloaded artifact's contents attacker-influenced in the first place: the job runs with the base repository's permissions and secrets, but the artifact it downloads was uploaded by the fork-triggered run this workflow reacts to. The trigger line itself is easy to read past, especially in a file with several triggers listed together (push, pull_request, workflow_run on one line) -- and its significance here is not that the keyword appears, but that it changes who controls the bytes a later step in the same job might execute.
Is a download-artifact step with no name: input an obvious problem to spot?
No -- it is the absence of something, which is much harder to notice while reading than a suspicious line would be. A download-artifact step with no name: pulls in every artifact the triggering run uploaded rather than one specific, expected one, widening what untrusted content enters the job. There is no unusual-looking text to catch a reader's eye; the step simply omits an input, and confirming that requires already knowing to check for it on every such step in every workflow.
Does a clean scan from this tool mean my workflow has no security issues?
No. This tool looks for exactly two patterns -- a workflow_run job that downloads an artifact and later runs something that looks like execution, and an unscoped download-artifact step -- and reports only those. A clean result means neither pattern was found in the pasted text; it says nothing about any other risk in the workflow, and it is not a security audit.