← 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

Where the two diverge on this specific pattern.
CaseReading by handThis checker
A download-artifact step and the run step that later executes its contentsCan sit many unrelated-looking steps apart in the same job; neither line looks suspicious read on its ownTracks the download across every subsequent step in the job body and flags the pair together, with both line numbers
The workflow_run trigger's significanceEasy 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 itselfChecks 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: inputAn absence, not a suspicious presence -- there is no unusual text to catch a reader's eye, only a missing oneFlags every download-artifact step with no name: regardless of what else looks ordinary around it
Whether execution "looks like" executionchmod +x, ./script, bash, source, python, node, and eval all read as ordinary build or test commands out of contextMatches 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.

Run the free artifact poisoning check

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.