Safeguard
Software Supply Chain Security

Unapproved Change Risk in the Software Supply Chain

How unreviewed code, dependency, and pipeline changes create supply chain breaches like SolarWinds and XZ Utils - and how to detect them before attackers do.

Safeguard Research Team
Research
8 min read

On December 13, 2020, FireEye disclosed that a malicious backdoor called SUNBURST had been shipping inside SolarWinds' Orion platform for months, eventually reaching roughly 18,000 customers. The most striking detail wasn't the code itself — it was how it got there. Attackers didn't submit a pull request that a reviewer rubber-stamped. They planted a second implant, SUNSPOT, directly on the build server, which watched for Orion compile jobs and silently swapped in a tampered source file before compilation. No commit history, no code review, no approval ticket. The published source code stayed clean; the shipped binary did not. That gap — between what a team approved and what actually reached production — is the essence of unapproved change supply chain risk, and it is one of the hardest categories of software supply chain risk to catch because most security tooling is still watching the repository, not the path between the repository and the customer.

What Counts as an Unapproved Change in a Software Supply Chain?

An unapproved change is any modification to code, build configuration, infrastructure, or a dependency that reaches production without passing through the review and approval process an organization has defined for it — regardless of whether the modification is "malicious." That includes a developer merging directly to a protected branch after disabling a required check, a CI job pulling a dependency version that was never pinned or reviewed, an infrastructure-as-code apply that drifted from the last approved plan, or a build server injecting code between source and artifact, as in SUNSPOT. SOC 2's CC8.1 criterion and NIST SP 800-218 (the Secure Software Development Framework) both treat change authorization as a control objective distinct from vulnerability management, precisely because a perfectly scanned, vulnerability-free artifact can still be the wrong artifact if it didn't go through the approved path. Sonatype's 2023 State of the Software Supply Chain report counted over 245,000 malicious packages published to open source registries that year alone — more than the combined total of the previous four years — and the common thread across nearly all of them is that they entered downstream projects without any human approving that specific change.

How Did SolarWinds Show the Danger of Build-Time Unapproved Changes?

SolarWinds showed that a build system with no independent verification of its own output is a blind spot no code review can close. SUNSPOT was designed to detect when MsBuild.exe was compiling the Orion.Improvement.Business.Layer project, then replace one source file with a backdoored version for the duration of that single build, restoring the original file afterward to avoid leaving evidence in the working directory. Every commit an engineer reviewed in source control was legitimate. Every diff in the pull request history was clean. The unapproved change existed only in the compiled binary, for the seconds it took to build it, which is why it went undetected for an estimated 14 months before FireEye's incident responders traced their own breach back to a compromised Orion update from March 2020. The lesson generalizes well beyond this one incident: any environment where build infrastructure has write access to what actually ships — self-hosted runners, unmonitored build agents, artifact repositories with broad publish permissions — is a place where "approved code" and "shipped code" can silently diverge.

Why Do Unapproved Dependency Updates Create So Much Exposure?

Unapproved dependency updates create outsized exposure because most build pipelines auto-pull the latest version of a transitive dependency without anyone on the team ever seeing, let alone approving, that specific code change. The event-stream incident in November 2018 is the clearest early example: a new maintainer, who had gained publish rights to the popular npm package through a plausible-looking offer to help, added a dependency called flatmap-stream containing code that specifically targeted the Copay Bitcoin wallet to steal private keys. It sat live on npm for over two months and was pulled by an estimated 8 million downloads before removal. The pattern repeated with ua-parser-js in October 2021, when a compromised npm account was used to publish three malicious versions carrying a cryptominer and password stealer to a package with roughly 7 million weekly downloads at the time. Most recently, the XZ Utils backdoor (CVE-2024-3094), discovered by Andres Freund on March 29, 2024, was inserted over roughly two years of patient social engineering in which a contributor calling themselves "Jia Tan" built maintainer trust and then merged obfuscated build-script changes that a normal review process was never positioned to catch, because the person merging the change was, by that point, the approver. In each case, the code that reached production had a commit and a version number — but no one on the consuming side had actually approved the specific bytes they were running.

What Role Does CI/CD Pipeline Drift Play in Unapproved Change Risk?

CI/CD pipeline drift matters because pipelines are themselves a form of code, and changes to them routinely bypass the review process applied to application code. The Codecov Bash Uploader compromise, disclosed in April 2021, is the reference case: attackers modified the publicly hosted upload script — used by thousands of customer CI pipelines to send test coverage data — to exfiltrate environment variables, including credentials and API keys, from every build that ran it. The tampered script was live for roughly two months, from late January to April 1, 2021, silently harvesting secrets from CI runners across an unknown number of downstream organizations before a customer noticed a checksum mismatch. CircleCI's January 2023 breach followed a related pattern: a single engineer's laptop was compromised with session-token-stealing malware, giving attackers access to production systems and enough reach that CircleCI told every customer to rotate all stored secrets. Neither incident involved a pull request anyone reviewed. Both involved infrastructure — a hosted script, a CI vendor's internal access — that sat outside the change-approval workflow teams thought they had, which is exactly why pipeline configuration, third-party CI scripts, and runner permissions deserve the same "who approved this" scrutiny as application code.

How Does Unapproved Change Risk Map to Compliance Frameworks?

Unapproved change risk maps directly onto the change-management controls that SOC 2, ISO 27001, and SLSA already require, which means most organizations are being audited on exactly this gap without necessarily having tooling that closes it. SOC 2's CC8.1 asks whether an entity authorizes, designs, develops, configures, documents, tests, approves, and implements changes to infrastructure, data, and software in a controlled manner — language that covers infrastructure-as-code and pipeline configuration, not just application pull requests. SLSA's provenance requirements, developed in the aftermath of the SolarWinds and Codecov incidents, exist specifically to let a consumer verify that an artifact was built from a specific, reviewed source revision using a specific, unmodified build process — the exact chain of custody SUNSPOT broke. In practice, this means an auditor asking "show me that every production change was approved" is really asking whether an organization can produce evidence linking a running artifact back to an approved commit, an approved pipeline definition, and an approved dependency set — a question most teams can only partially answer today, because their approval evidence lives in a pull request tool while their deployment evidence lives somewhere else entirely.

How Safeguard Helps

Safeguard closes the gap between "what was approved" and "what actually shipped" by continuously reconciling the two instead of trusting that code review coverage implies deployment integrity. It maps every artifact that reaches a production environment back to its source commit, build definition, and dependency manifest, then flags any deviation — a pipeline config edited outside its normal review path, a dependency version that changed without a corresponding approved commit, a build running on infrastructure that isn't the one CI history says it should be. That reconciliation is what would have surfaced SUNSPOT's build-time file swap, a Codecov-style script tampering, or an XZ-style late-stage maintainer commit that skipped meaningful independent review, because in each case the divergence between the approved state and the shipped state was detectable even though the individual commit looked legitimate. Safeguard also generates the evidence auditors actually ask for under SOC 2 CC8.1 and SLSA provenance requirements — a continuous, artifact-level record of which changes were approved, by whom, and whether the thing that shipped matches — so compliance teams aren't reconstructing change history from scattered CI logs during an audit. For engineering teams, that means unapproved changes get caught at the point they diverge from policy, not months later when an incident responder is tracing a breach back through build logs, the way FireEye had to in December 2020.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.