Skip to content

CI and Build Systems

Continuous Integration and Build Pipelines

The pipelines that build, sign and ship the app, such as GitHub Actions.

  • Build

About

Continuous integration (CI) means building and testing the code automatically every time it changes. A build pipeline turns that source code into what ships, installing dependencies, running tests, building images and publishing releases. Services such as GitHub Actions and GitLab CI run it, often with shared steps written by strangers. It holds the keys to sign and publish, so whoever controls the build controls what customers receive.

A step runs every time its workflow does, on the build runner, with whatever secrets and tokens the workflow is given. Anything it changes ends up in what ships.

If It Is Malicious

If a build step or GitHub Action is malicious, it runs in the build pipeline with the build's secrets and signing keys, in every repository whose builds use it, and it can change what they ship.

Its code runs inside these technologies.

See It in the Stack

Examples

These are well-known names from CI and Build Systems, written as you would meet them in an inventory, an alert or a lockfile, the file that records every version a build installed.

actions/checkout
GitHub's own step that fetches the code, in almost every workflow.
actions/setup-node
Installs Node.js on the build runner.
docker/build-push-action
Builds a container image and pushes it to a registry.
tj-actions/changed-files
Lists the files a change touched. It was compromised in March 2025.
Jenkins
A self-hosted build server, extended with community plugins.

How Bad Code Gets In

These are the ways bad code can reach your systems through CI and Build Systems. None of them needs a flaw in your own code, so outside code needs checking as much as your own.

  • A shared action, a reusable step published by someone else, has its tags moved to new code, so every workflow that uses the tag runs it.
  • A workflow that runs strangers' pull requests with access to the project's secrets can leak them.
  • A tool the build downloads and runs, such as an uploader script, is changed at its source.
  • A build server is broken into and changes the product before it is signed.

How to Check

These checks show what came from CI and Build Systems, whether a known bad version is in use, and how to keep one out next time.

  • Pin third-party actions to a full commit hash, not a tag, and let Dependabot propose updates.
  • Give each workflow only the token permissions it needs, and keep secrets out of builds of pull requests from forks.
  • Rotate every secret a compromised step could have seen, and check old build logs for leaks.
  • Record build provenance, such as SLSA attestations, so each release can be traced to the run that built it.
  • Check workflow files with OpenSSF Scorecard.

Well-Known Incidents

Each incident below is a real case, told at the level of its public advisory. The tag says how the bad code got in, and the link opens the primary source.

  • Compromised Build or Release2021

    Codecov Bash Uploader

    Attackers changed Codecov's Bash Uploader script, which many build pipelines downloaded and ran, so that it sent the pipelines' secrets to them.

    Reach For about two months it took credentials, tokens and keys from customers' build systems, and customers had to rotate everything their builds held.

    Source: about.codecov.io (opens in a new tab)
  • Hijacked AccountCVE-2025-300662025

    tj-actions/changed-files

    Attackers used a stolen token to point the version tags of a popular GitHub Action at a malicious commit that printed the build's secrets into its logs.

    Reach Thousands of repositories used it. Those whose builds ran it in that window with public logs exposed their secrets to anyone who read them.

    Affectedtj-actions/changed-files

    Source: stepsecurity.io (opens in a new tab)
  • Hijacked Account2025

    Nx (s1ngularity)

    A flaw in the Nx project's own GitHub Actions workflow let attackers take its npm publishing token and release versions of the build tool that searched machines for secrets.

    Reach Machines that installed them ran the script, which posted what it found to a public GitHub repository through the GitHub CLI.

    Affectednx

    Source: nx.dev (opens in a new tab)

In Common Stacks

These common stacks take code from CI and Build Systems. Open one to see where that code sits among its layers and who runs each one.

Sources

Product names and logos are trademarks of their owners, shown only to name each product, with no endorsement implied. npm is a registered trademark of npm, Inc.

Apache, Apache HTTP Server, Tomcat and Log4j are trademarks of the Apache Software Foundation. Node.js® is a registered trademark of the OpenJS Foundation, and Kubernetes® of LF Projects, LLC.

Linux® is the registered trademark of Linus Torvalds in the U.S. and other countries. Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.

MongoDB® is a registered trademark of MongoDB, Inc. Redis® is a registered trademark of Redis Ltd. Any rights therein are reserved to Redis Ltd.

PHP logo by Colin Viebrock, CC BY-SA 4.0 (opens in a new tab). Ruby logo © 2006 Yukihiro Matsumoto, CC BY-SA 2.5 (opens in a new tab). Tux by Larry Ewing, with Garrett LeSage and IFo Hancroft.

Puma logo © Evan Phoenix and contributors, CC BY-ND 3.0 (opens in a new tab). NuGet logo from NuGet's Media repository, CC BY 2.0 (opens in a new tab).

Gunicorn logo © Benoît Chesneau and Paul J. Davis, MIT licence for the Gunicorn logo (opens in a new tab). Packagist logo © 2011 Jordi Boggiano and Nils Adermann, MIT licence for the Packagist logo (opens in a new tab).

MIT Licence Notices for the Gunicorn and Packagist Logos

Gunicorn logo

2009-2024 (c) Benoît Chesneau <benoitc@gunicorn.org> 2009-2015 (c) Paul J. Davis <paul.joseph.davis@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Packagist logo

Copyright (c) 2011 Jordi Boggiano, Nils Adermann

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.