The issue is not only bad workflows#
Trail of Bits spent three months hardening zizmor, a static analyzer for GitHub Actions workflows. The work focused on a narrow but important problem: whether a security tool can correctly parse and reason about the YAML that real projects actually use.
That matters because GitHub Actions is now core infrastructure for open-source software. A weak workflow can expose secrets, run attacker-controlled code, or publish compromised artifacts. The March 2026 compromise involving aquasecurity/trivy-action is the kind of incident zizmor is meant to help prevent: attackers abused CI behavior to exfiltrate organization and repository secrets, then used those credentials to backdoor downstream activity.
But a static analyzer is only useful if it can read the workflow correctly. If it crashes, silently misinterprets YAML, or reports the wrong location, maintainers may miss the exact class of misconfiguration they were trying to catch.
Trail of Bits’ work found and fixed several of those failure modes in zizmor. The immediate trigger was GitHub Actions support for YAML anchors, added in September 2025. Anchors let workflow authors define a value once and reuse it elsewhere through aliases. That can reduce duplication. It also makes analysis harder, because the meaning of one part of a workflow may depend on a definition somewhere else in the same file.
What Trail of Bits tested#
Trail of Bits built a real-world test corpus instead of relying only on synthetic examples. Using BigQuery’s GitHub dataset, the team identified the 10,000 most-starred repositories created between 2022 and 2025. They then filtered that set to the 6,612 repositories using GitHub Actions and downloaded every workflow file.
The final corpus contained 41,253 YAML workflow files.
When zizmor was run against that corpus, it crashed on 45 workflows. That is a low crash rate in percentage terms. It is still meaningful. Each crash marks an input the tool could not analyze cleanly, and in security tooling, unsupported edge cases tend to collect in the same places where attackers and complex projects operate.
The anchor subset was small. Trail of Bits found only 43 workflows using YAML anchors, roughly 0.1% of the corpus. That should not be read as unimportant. Some of those 43 workflows belonged to foundational open-source projects. The feature is also now supported by GitHub Actions, so usage may grow as maintainers discover it and start refactoring large CI files.
Two patterns stood out.
Some projects used anchors to reuse steps across jobs. Bitcoin Core’s CI was cited as one example. Others used anchors to pin an action reference once, including a SHA hash, and then reuse that same action definition later in the workflow.
Both patterns are reasonable. Both require the analyzer to resolve non-local YAML behavior correctly.
The anchor bugs were small and high-value#
Trail of Bits described four anchor-handling bugs that broke zizmor on real workflows.
First, aliases inside sequences were handled incorrectly. When a YAML alias appeared inside a list, such as a list of steps, zizmor’s internal path representation treated the alias contents as if they had been spread into the list. The result could be a crash or findings that pointed to the wrong location in the file.
Second, anchor prefixes leaked into resolved values in YAML flow sequences. In some cases, syntax used to define an anchor was not stripped from the value that zizmor later inspected. A step consuming that node could fail because the analyzer was not seeing the value as GitHub Actions would.
Third, duplicate anchors caused a crash. YAML allows an anchor name to be redefined, with the last definition winning. zizmor’s YAML layer assumed anchor names were unique and panicked when that assumption was violated.
Fourth, scalar aliases were not expected in one audit path. When an alias was used as a scalar value, the audit failed because it did not handle that extra layer of indirection.
These are not dramatic bugs in the usual exploit-writeup sense. They are parser and model bugs. But that is exactly why they matter. Static analysis depends on a faithful model of the input language. If the model diverges from what the platform accepts, the analyzer can create blind spots.
Trail of Bits said the issues were fixed, and integration tests were added for anchor patterns found in real workflows. The anchor documentation was also updated.
The corpus found more than anchor problems#
The broader workflow corpus also exposed bugs unrelated to YAML anchors.
One class involved deserialization edge cases. GitHub Actions accepts YAML constructs that zizmor’s workflow model did not anticipate. Trail of Bits cited examples such as an integer where a string was expected, a float where an integer was expected, and a string where a mapping was expected. Each case caused zizmor to reject the entire workflow.
That is a practical problem. CI platforms often accept loose or surprising YAML. A security tool cannot only support the clean subset a developer would prefer. It has to handle the subset the platform actually accepts.
Another class involved the GitHub Actions expression evaluator. zizmor evaluates expressions to decide whether user-controlled data can flow into dangerous sinks. Trail of Bits validated the evaluator against GitHub’s own behavior and helped align zizmor with the official test suite.
The team also traced some crashes to an upstream dependency and filed issues and pull requests there. One useful detail from the writeup: even the YAML 1.2 test suite does not cover every edge case the specification permits. Formal tests help, but they do not replace real inputs.
The final output of the project was 20 filed issues and 15 merged pull requests.
Why this matters for CI security#
The obvious lesson is that zizmor is now better at analyzing GitHub Actions workflows. The broader lesson is about how CI security tools should be tested.
GitHub Actions workflows are not just configuration files. They are execution plans with access to secrets, tokens, build systems, release jobs, package registries, and production-adjacent automation. A misread workflow can mean a missed finding. A missed finding can leave a repository exposed to a supply-chain attack path.
YAML also has a long history of surprising behavior. Anchors, aliases, implicit typing, duplicate definitions, flow sequences, and platform-specific interpretation all create distance between what a human sees and what a tool must evaluate.
For maintainers, this does not mean YAML anchors are inherently unsafe. It means that using advanced YAML features in security-sensitive CI can raise the cost of review and tooling. If a project uses anchors to reduce duplication, it should make sure its linters, security scanners, and reviewer workflow can resolve them correctly.
For tool authors, the method is the important part: gather real inputs, run the analyzer, triage every failure, and turn failures into tests. Synthetic fixtures are necessary. They are not enough.
What teams can check now#
Teams using GitHub Actions should treat workflow analysis as part of supply-chain defense, not as a cosmetic lint step.
A few checks are worth making:
- Run zizmor or a comparable GitHub Actions analyzer on active workflow files, especially release and publishing workflows.
- Pay attention to analyzer crashes and parse failures. Do not treat them as harmless noise.
- If workflows use YAML anchors or aliases, confirm that the tools in your CI review path support them correctly.
- Prefer pinned action references where practical, and make sure reuse patterns do not obscure what version is actually being executed.
- Review expression-heavy workflows carefully, especially where pull request data, issue data, or other user-controlled inputs can affect shell commands or privileged steps.
The Trail of Bits work is not a claim that every GitHub Actions workflow is suddenly safe. It is a narrower and more useful result: one important analyzer now handles more of the workflows that exist in the wild, and the work shows how to harden similar tools.
In CI security, the parser is part of the perimeter.