Software Supply Chain Security and the SLSA Framework in GitHub
A complete, beginner-friendly guide to how modern software actually gets built, why attackers love the build pipeline more than the code itself, and how SLSA and GitHub's native tooling close that gap — explained from first principles, with real incidents and production patterns.
In 2020, a routine software update from a well-known IT management vendor was quietly modified before it ever reached customers. Nobody broke into the customer’s network. Nobody guessed a password. The attackers simply got inside the build process — the invisible machinery that turns source code into the software you install — and let the vendor’s own trusted update mechanism carry the malware in for them. Roughly eighteen thousand organizations installed it without a second thought, because it came from a name they trusted, signed and shipped through a pipeline they had never once questioned. That single event reshaped how the entire software industry thinks about security, and it is the reason a framework called SLSA, and a platform called GitHub, now sit at the center of how trustworthy software gets built. This guide walks through that whole story: what a software supply chain actually is, how it gets attacked, what SLSA does about it, and exactly how GitHub turns those ideas into features you can turn on today.
AWhat Is Software Supply Chain Security?
Before SLSA makes sense, the term “supply chain” itself needs to stop feeling abstract. It is not a diagram in a slide deck — it is every single hand that touches your code between the moment a developer types it and the moment a user runs it.
A software supply chain is the complete sequence of people, systems, and processes involved in creating a piece of software: the developers who write code, the open-source libraries that code depends on, the build servers that compile it, the registries that host it, and the systems that deploy it into production. Just like a physical product — say, a car — passes through raw material suppliers, parts manufacturers, an assembly line, and a dealership before reaching a driver, software passes through source code, third-party dependencies, a build pipeline, a package registry, and a deployment target before reaching a user.
Software supply chain security is the discipline of protecting every one of those links so that the software a user finally installs is exactly the software the original developers intended — nothing added, nothing swapped, nothing tampered with along the way.
Think about ordering a sealed bottle of medicine from a pharmacy. You trust it not because you watched every step of manufacturing yourself, but because tamper-evident seals, batch tracking, and regulated factories give you confidence that nobody opened the bottle and swapped the pills between the factory and your hand. Software supply chain security tries to build that same tamper-evident seal around code — a cryptographic guarantee that what left the factory (the build system) is exactly what arrived in your hands (your production server).
The reason this discipline exploded in importance over the last several years is simple: modern applications are assembled, not written. A typical production application might contain a few thousand lines of code its own developers actually wrote, sitting on top of hundreds of thousands of lines pulled in from open-source packages, each of which was written by someone the development team has never met and will never audit line by line. Attackers noticed this imbalance long before most companies did. Instead of trying to find a bug in one company’s carefully reviewed code, it is far more efficient to poison one popular open-source package, or one shared build pipeline, and let thousands of downstream applications inherit the malicious code automatically the next time they run a routine update.
Supply chain attacks do not require breaking your defenses. They require breaking someone else’s defenses — a library maintainer, a build server, a CI/CD pipeline — and then simply waiting for your systems to trust that broken link automatically.
The Three Questions Supply Chain Security Answers
Every meaningful supply chain security control ultimately answers one of three questions about a piece of software:
Where did this come from?
Which source repository, which commit, which build system, and which exact build steps produced this artifact.
Has it been altered?
Whether the artifact you received is byte-for-byte the same thing the build system actually produced, with no tampering in transit or in storage.
Who is vouching for it?
Which organization, system, or individual cryptographically signed off on this artifact as trustworthy for use.
Every concept in this guide — provenance attestations, SLSA levels, GitHub’s build attestation feature, Sigstore signing — exists purely in service of answering those three questions with something stronger than “we assume it’s fine.”
BAnatomy of a Software Supply Chain
To defend a supply chain, you first need to see all of its links laid out — because attackers only need to find one weak one.
A modern supply chain has five broad stages, and a vulnerability introduced at any single stage can silently propagate to every stage after it.
flowchart LR
A[Developer Writes Code] --> B[Source Repository]
B --> C[Third-Party Dependencies]
C --> D[Build System / CI Pipeline]
D --> E[Build Artifacts]
E --> F[Package Registry]
F --> G[Deployment Environment]
G --> H[Production / End Users]
FIG 1 — The five stages of a software supply chain, source through production
Stage 1: Source Code
This is the code your own developers write, stored in a version control system such as a Git repository. Threats here include a compromised developer account pushing malicious commits, a stolen personal access token, or a weakly protected main branch that allows unreviewed changes to merge directly.
Stage 2: Dependencies
Almost no application is built from scratch. It pulls in open-source libraries through package managers — npm for JavaScript, pip for Python, Maven for Java, and dozens of others. Each dependency can itself depend on further dependencies, forming what is called a transitive dependency tree that can reach thousands of packages for a single application, most of which nobody on the team has ever reviewed.
Stage 3: Build System
This is the machinery — typically a continuous integration and continuous delivery (CI/CD) pipeline such as GitHub Actions, Jenkins, or CircleCI — that compiles source code and dependencies into a runnable artifact: a binary, a container image, a compiled package. The build system is one of the highest-value targets in the entire chain, because compromising it lets an attacker inject malicious code into every artifact it produces, without ever touching the source repository at all.
Stage 4: Artifact Registry
Once built, artifacts are published to a registry — npm’s registry, PyPI, Docker Hub, or a private container registry — where other systems and developers can download them. A compromised registry account, or a registry that fails to verify who is allowed to publish under a given package name, lets an attacker distribute malicious versions directly to consumers.
Stage 5: Deployment
Finally, the artifact is deployed into a running environment — a Kubernetes cluster, a set of virtual machines, a serverless platform. Deployment credentials, infrastructure-as-code templates, and the deployment pipeline itself are all part of the chain that must be protected.
Why Attackers Prefer the Build System
Compromising a single developer’s laptop yields one person’s future commits. Compromising a shared build system yields every artifact that system will ever produce, for every team that uses it, indefinitely — until someone notices. This asymmetry is exactly why build-system compromise has become the signature technique behind the largest supply chain attacks of the last decade.
CHow Supply Chain Attacks Actually Happen
Supply chain attacks fall into a handful of repeatable patterns. Recognizing the pattern is the first step toward defending against it.
Dependency Confusion
Many companies use internal, privately named packages alongside public ones. If an attacker publishes a public package with the exact same name as a company’s private internal package, and the build tooling is not configured to prioritize the private registry, developers’ machines and build servers can silently pull down the attacker’s public, malicious version instead of the trusted internal one — because higher version numbers on public registries are often preferred by default.
Typosquatting
Attackers publish packages with names deliberately similar to popular ones — a single transposed letter, a missing hyphen — betting that a developer will mistype an install command and pull in malware instead of the legitimate library.
Compromised Maintainer Accounts
Popular open-source packages are often maintained by one or two volunteers. If an attacker phishes or brute-forces a maintainer’s registry credentials, they can push a malicious update directly to a package that millions of applications already trust and auto-update from — no code review, no pull request, straight to production for everyone downstream.
Build System Compromise
Rather than touching the source code at all, an attacker who compromises the build environment can inject malicious steps into the build process itself — modifying compiler flags, injecting a malicious build script, or tampering with the output artifact after legitimate compilation finishes but before it is signed and published. Because the source code repository looks completely clean under review, this is one of the hardest attack patterns to detect through code review alone.
Malicious Third-Party CI Actions and Plugins
CI/CD platforms like GitHub Actions allow pipelines to call reusable, community-published building blocks. If a pipeline references a third-party action by a mutable tag such as a branch name rather than a fixed, immutable commit hash, the maintainer of that action — or anyone who compromises their account — can silently swap in malicious behavior that every pipeline referencing that tag will execute on its very next run.
“Why is a build-system compromise harder to detect than a source-code compromise?” A strong answer explains that source code is reviewed by humans and diffed automatically, but the build process runs unattended, often on ephemeral infrastructure, and its output is judged only by whether the artifact “works” — not by whether every step that produced it was legitimate.
DThe SLSA Framework: Origins and Purpose
SLSA did not appear out of nowhere. It is the industry’s direct, structured answer to exactly the attack patterns described above.
SLSA stands for Supply-chain Levels for Software Artifacts, pronounced “salsa.” It began as an internal framework at Google, built from years of experience hardening their own internal build systems, and was opened up as a cross-industry standard hosted by the Open Source Security Foundation (OpenSSF) so that any organization — not just Google — could adopt a shared, verifiable language for supply chain trust.
SLSA’s core insight is refreshingly simple: instead of trying to inspect every dependency’s source code by hand — an impossible task at the scale of a modern application — focus on making the build process itself trustworthy, verifiable, and tamper-resistant. If you can cryptographically prove exactly how an artifact was built, from exactly which source, on exactly which system, then you no longer need to manually re-verify every artifact’s contents — you can verify its provenance instead.
SLSA is closest in spirit to a food safety certification. A restaurant inspector does not personally taste-test every dish that leaves the kitchen. Instead, they certify that the kitchen’s process — its cleanliness, its sourcing, its handling procedures — meets a defined standard. Once the process is certified, every dish that comes out of it inherits a baseline of trust. SLSA certifies the “kitchen” — the build pipeline — rather than inspecting every individual “dish,” the artifact.
What SLSA Is Not
It is worth being precise here, because this is a common point of confusion. SLSA is not a vulnerability scanner, it does not look for bugs in your code, and it does not replace tools like static analysis or dependency vulnerability scanning. SLSA answers a narrower, foundational question: can you prove how this artifact was built, and can you prove nobody tampered with it afterward? A perfectly secure, bug-free artifact built through an untrustworthy, unverifiable process still fails SLSA’s goals, because there is no way to prove it wasn’t swapped for something else before it reached you.
ESLSA Levels Explained
SLSA is organized as a ladder of four levels, each one adding stronger guarantees than the last. You do not need to reach the top rung immediately — the whole point of a leveled framework is that any step up is measurable progress.
flowchart TD
S0["SLSA Level 0
No guarantees — status quo"] --> S1["SLSA Level 1
Build process is documented and automated"]
S1 --> S2["SLSA Level 2
Build runs on a hosted, shared build service; provenance is signed"]
S2 --> S3["SLSA Level 3
Build runs in a hardened, isolated environment; provenance is non-forgeable"]
FIG 2 — The SLSA build track levels, each one strengthening the guarantees of the level before it
SLSA Level 1: Documented Build Process
At Level 1, the build is scripted and automated rather than done manually on someone’s laptop, and it generates a provenance record — metadata describing what was built, from what source, and by what process. This provenance does not yet need to be cryptographically protected; it simply needs to exist. The value here is basic transparency: anyone can now see how the artifact claims to have been built, even if that claim isn’t yet independently verifiable.
SLSA Level 2: Hosted Build, Signed Provenance
At Level 2, the build must run on a managed, shared build platform — such as GitHub Actions’ hosted runners — rather than on an individual’s personal machine, and the resulting provenance must be cryptographically signed. This closes a major gap: at Level 1, someone could simply fabricate a fake provenance file describing a build that never actually happened. At Level 2, the provenance is generated by the build platform itself, not by the developer, which makes it meaningfully harder to forge.
SLSA Level 3: Hardened, Non-Falsifiable Builds
Level 3 requires the build platform to isolate each build so thoroughly that even a malicious build script cannot tamper with the provenance generation process itself, or influence the builds of other projects running on the same shared infrastructure. This is the level where organizations can trust provenance even against an attacker who has managed to inject malicious code into the build steps, because the provenance-signing process is isolated from — and cannot be manipulated by — the build steps it is describing.
What Higher Levels Buy You
- Provenance that survives even a compromised build script
- Verifiable proof of exact source commit and build parameters
- A basis for automated policy enforcement — “only accept artifacts built at Level 3”
What Higher Levels Cost You
- More rigid, less flexible build pipeline configurations
- Requires the underlying build platform to support isolation guarantees
- Migration effort for legacy pipelines built without provenance in mind
“What is the single biggest difference between SLSA Level 1 and Level 2?” The expected answer: Level 1 provenance can be self-reported and therefore forged, while Level 2 provenance is generated and signed by a shared build platform the developer does not fully control, which is what makes it trustworthy to a third party.
Source Track: A Second, Complementary Ladder
Alongside the build track described above, SLSA also defines a source track focused on the trustworthiness of the source repository itself — covering practices like requiring code review before merge, retaining a verifiable history of changes, and preventing history from being silently rewritten. A fully mature SLSA adoption strengthens both the source side and the build side, because a perfectly verified build of tampered source code is still a compromised artifact.
FData Flow: From Source to Verified Deployment
It helps to trace a single artifact’s journey end to end, watching exactly where provenance is created, attached, and finally checked.
Commit Is Pushed
A developer pushes a commit, or merges a reviewed pull request, into the source repository. This commit hash becomes the anchor point that provenance will later reference.
Build Is Triggered
A CI/CD pipeline is triggered on the hosted build platform. The platform, not the developer’s machine, takes control of the build from this point forward.
Artifact Is Compiled
Source code and pinned dependencies are compiled or packaged into a build artifact — a binary, a container image, or a package archive.
Provenance Is Generated
The build platform itself — not the build script — generates a provenance statement recording the source commit, the build steps, the builder’s identity, and a cryptographic hash of the resulting artifact.
Provenance Is Signed
The provenance statement is cryptographically signed, binding it permanently to the exact artifact hash it describes. Any later modification to the artifact would invalidate the signature.
Artifact and Provenance Are Published
Both the artifact and its signed provenance are published together to a registry, ready for consumers to download.
Consumer Verifies Before Trusting
Before using the artifact, a consuming system checks the signature, confirms the provenance matches an expected source and builder, and only then allows it to proceed — for example, into a production deployment.
The critical shift SLSA introduces is at step 7. Without provenance, “trust” is usually just a habit — a team downloads a package because they always have, with no actual check happening. With provenance in place, trust becomes a verifiable, automatable decision: a deployment pipeline can be configured to mechanically reject any artifact whose provenance does not check out, closing the loop that most organizations previously left wide open.
GAdvantages, Disadvantages, and Trade-offs
Like every security control, SLSA adoption is not free. Understanding the trade-offs helps teams adopt it deliberately rather than reflexively.
Automatable Trust
Provenance checks can be enforced by machines at deploy time, removing reliance on manual, error-prone human review of every dependency.
Blast Radius Containment
If a build system is compromised, higher SLSA levels make it dramatically harder for the attacker to forge convincing, signed provenance, limiting how far the compromise can spread undetected.
Common Industry Language
SLSA levels give vendors, auditors, and customers a shared vocabulary for supply chain maturity, instead of every company inventing its own ad hoc standard.
Engineering Investment
Reaching higher levels requires migrating pipelines to hosted, isolated build platforms and adjusting workflows that were previously more flexible or manual.
Ecosystem Dependency
The guarantees only hold if every link in the chain — including third-party dependencies — eventually adopts compatible provenance practices; a single unattested dependency remains a blind spot.
Not a Silver Bullet
SLSA proves how an artifact was built, not that its logic is free of bugs or vulnerabilities — it must be paired with code review, dependency scanning, and testing, not used as a replacement for them.
HGitHub’s Supply Chain Security Toolset
GitHub has built a set of native features that map almost one-to-one onto the supply chain stages from Chapter B, and onto SLSA’s own requirements. Here is how each piece fits.
| Supply Chain Stage | GitHub Feature | What It Protects Against |
|---|---|---|
| Source Code | Branch protection rules, required reviews, signed commits | Unauthorized or unreviewed changes reaching the main branch |
| Dependencies | Dependabot alerts and version updates | Known-vulnerable dependency versions being used unknowingly |
| Dependencies | Dependency graph and dependency review | Blind spots in what a project actually depends on, including transitively |
| Source / Secrets | Secret scanning and push protection | Credentials or API keys accidentally committed and exposed |
| Code Quality | CodeQL code scanning | Exploitable code-level vulnerability patterns introduced by any contributor |
| Build System | GitHub Actions hosted runners | Builds running on untrusted, unmanaged personal machines |
| Build Credentials | OpenID Connect (OIDC) federated identity | Long-lived cloud deployment credentials sitting in repository secrets |
| Provenance | Artifact attestations (build provenance) | Unverifiable claims about how and where an artifact was actually built |
Dependabot: Watching the Dependency Layer
Dependabot continuously compares a repository’s declared dependencies against a database of known vulnerabilities and automatically opens alerts, and optionally pull requests, when a fix is available. This directly addresses the dependency stage of the supply chain — the layer most teams have the least visibility into by default.
Secret Scanning and Push Protection
Secret scanning continuously inspects repository content for patterns that look like exposed credentials — API keys, tokens, private keys — and push protection goes a step further by blocking a commit from ever being pushed if it contains a detectable secret, stopping the leak before it happens rather than only alerting after the fact.
CodeQL: Semantic Code Scanning
CodeQL treats source code as queryable data, allowing automated detection of vulnerability patterns — such as unsanitized input reaching a dangerous function — across an entire codebase, on every pull request, without waiting for a human reviewer to spot the same issue manually.
OpenID Connect: Removing Long-Lived Cloud Secrets
Historically, a CI pipeline that needed to deploy to a cloud provider stored a long-lived access key as a repository secret — a juicy, permanent target if the pipeline was ever compromised. OIDC lets GitHub Actions instead request a short-lived, automatically expiring identity token at run time, which the cloud provider trusts based on a pre-configured relationship with the specific repository and workflow, removing the need for a standing secret altogether.
“Why does removing long-lived cloud credentials from CI actually matter?” Because a stolen long-lived key remains valid indefinitely until someone manually revokes it, while a stolen OIDC-issued token typically expires within minutes, sharply limiting how much damage a compromised pipeline can do before the exposure closes itself.
ISLSA in Practice: GitHub Actions Build Provenance
This is where the theory of SLSA levels turns into an actual feature a repository maintainer can switch on.
flowchart LR
Trigger["Push / Release Tag
Triggers Workflow"] --> Runner["GitHub-Hosted Runner
Executes Build Job"]
Runner --> Build["Source Compiled Into
Build Artifact"]
Build --> OIDC["Runner Requests
OIDC Identity Token"]
OIDC --> Attest["Build Provenance
Attestation Generated"]
Attest --> Sign["Keyless Signing via
Sigstore / Fulcio / Rekor"]
Sign --> Store["Attestation Attached
to the Artifact"]
Store --> Verify["Consumer Runs
Attestation Verification"]
FIG 3 — How a GitHub Actions build produces a signed, SLSA-aligned provenance attestation
Artifact Attestations
GitHub Actions can generate a build provenance attestation for any artifact produced by a workflow — describing the exact source repository, commit, workflow file, and build environment that produced it. This attestation is generated by the GitHub Actions platform itself at build time, not self-reported by the workflow script, which is exactly the property SLSA Level 2 requires: provenance the developer cannot simply fabricate.
Keyless Signing with Sigstore
Rather than requiring every project to generate, store, and rotate its own private signing key — historically one of the biggest practical barriers to widespread artifact signing — GitHub Actions integrates with Sigstore’s keyless signing model. A short-lived certificate is issued for the specific workflow run, tied to the workflow’s verified OIDC identity, used once to sign the artifact, and then discarded, while a public transparency log permanently and publicly records that the signing event occurred, so it can be independently audited later even after the certificate itself has expired.
Keyless signing is closer to getting a wax seal stamped by a notary who checks your ID on the spot, rather than owning a personal signet ring you must keep locked away forever. The notary’s stamp is trusted because of who verified you in that moment, and a public ledger records that the stamping happened — not because you are personally safeguarding a precious physical object that could be stolen.
Verifying an Artifact’s Provenance
Once an artifact has an attached attestation, anyone downloading it can verify that the artifact’s cryptographic hash matches the one recorded in the signed provenance, that the signature is valid, and that the provenance describes the expected source repository and workflow — turning “I trust this because I downloaded it from the right-looking URL” into “I verified this was built from the exact source I expect, by the exact pipeline I expect.”
Reusable Workflows and Pinned Actions
Two closely related GitHub practices strengthen the build stage further. Reusable workflows let an organization define one hardened, centrally maintained build process and have every project call into it, rather than each team reinventing — and potentially weakening — their own pipeline. Pinning third-party actions to an immutable commit hash, instead of a mutable branch or version tag, ensures that a pipeline always executes the exact code that was reviewed and approved, even if the action’s maintainer later publishes a malicious update under the same tag.
JSecurity Considerations and Threat Models
Provenance and attestations are powerful, but only when they are actually checked, and only when the trust boundaries around them are drawn correctly.
Attestation Without Enforcement Is Just Documentation
Generating a signed provenance attestation accomplishes nothing on its own if nothing downstream ever checks it. Real security value only appears once deployment pipelines or admission controllers are configured to actively reject artifacts that lack valid, expected provenance — turning an available fact into an enforced policy.
The GITHUB_TOKEN and Least Privilege
Every GitHub Actions workflow run is automatically issued a short-lived token scoped to that repository, used to interact with the GitHub API during the run. A workflow that requests broader permissions than it actually needs — write access to other repositories, for example — expands what an attacker could do if that specific workflow run were ever compromised through a malicious dependency or a poisoned third-party action. Scoping permissions down to the minimum a workflow genuinely requires is one of the simplest, highest-value hardening steps available.
Pull Request Workflow Risks
Workflows that automatically run in response to pull requests from external contributors deserve particular care, because an untrusted contributor’s branch can contain code designed specifically to abuse the workflow’s permissions or exfiltrate secrets if the workflow is not carefully scoped and reviewed before it runs with elevated access.
Transitive Trust
An organization’s supply chain security is only as strong as the weakest attestation anywhere in its dependency tree. A perfectly SLSA Level 3 build that pulls in one unattested, unverified third-party dependency still inherits that dependency’s risk — provenance does not automatically propagate backward through a supply chain unless every layer participates.
Signed provenance proves how an artifact was built — it does not prove the artifact is free of vulnerabilities or malicious logic that was present in the source code from the start. A malicious commit, once merged, will produce a perfectly valid, perfectly signed attestation for the malicious build.
KMonitoring, Logging, and Auditability
Prevention alone is never enough; every mature supply chain program assumes something will eventually slip through, and builds the ability to detect and investigate it quickly.
The Transparency Log
Every keyless signing event through Sigstore is recorded in a public, append-only transparency log. This means that even long after a certificate has expired, anyone — including the original developer — can later audit exactly when and under what identity a given artifact was signed, and detect if a signing event occurred that nobody on the team actually initiated.
Workflow Run Logs and Audit Logs
GitHub retains detailed logs of every workflow run, including exactly which steps executed and what permissions were granted, alongside organization-level audit logs covering security-relevant events such as changes to branch protection rules or secret access. Together these give security teams the raw material needed to reconstruct exactly what happened during an incident.
Dependency and Vulnerability Alerts as Continuous Monitoring
Because Dependabot continuously re-checks dependencies against an evolving vulnerability database, a dependency that was safe on the day it was added can later trigger an alert the moment a new vulnerability is disclosed against it — effectively giving every historical build a form of retroactive, ongoing monitoring rather than a one-time check performed only at build time.
Why Immutable Logs Matter
If an attacker who compromises a build system could also quietly edit the logs describing what that build system did, logging alone would offer little real protection. This is precisely why transparency logs are designed to be append-only and independently verifiable — an entry, once written, cannot be silently altered or deleted without the tampering itself becoming detectable.
LDesign Patterns and Anti-patterns
Teams tend to repeat the same handful of good and bad decisions when building out their supply chain security posture. Recognizing them by name speeds up both design reviews and troubleshooting.
Pattern: Provenance-Gated Deployment
A deployment pipeline that automatically refuses to deploy any artifact lacking a valid, expected provenance attestation — turning provenance from a nice-to-have record into an enforced gate that no artifact can bypass, regardless of who or what tries to deploy it.
Pattern: Golden, Centrally Maintained Build Pipeline
A single, security-reviewed reusable workflow, maintained by a central platform team, that every product team calls into rather than assembling their own build steps from scratch — concentrating security investment into one place instead of spreading thin, inconsistent effort across dozens of independently maintained pipelines.
Pattern: Least-Privilege, Just-in-Time Credentials
Using OIDC-issued, short-lived tokens scoped narrowly to exactly what a specific workflow run needs, rather than broad, standing credentials shared across every pipeline in an organization.
Anti-pattern: Trusting Mutable References
Referencing a third-party GitHub Action by a branch name or a mutable version tag rather than a fixed commit hash, which silently allows the code your pipeline executes to change without any review on your side, the moment the action’s maintainer — or anyone who compromises their account — pushes a new commit under that same tag.
Pattern
Sharing one broad, long-lived cloud deployment credential across every repository and workflow in an organization for convenience.
Why It Feels Reasonable
It avoids the setup overhead of configuring a separate, scoped identity for every individual pipeline.
Why It Fails
A single compromised workflow — through a malicious dependency, a poisoned action, or a leaked log — now grants an attacker the same broad access every other pipeline in the organization enjoys, turning one small breach into an organization-wide one.
Anti-pattern: “Set and Forget” Security Scanning
Enabling scanning tools such as Dependabot or CodeQL once and never actually triaging, prioritizing, or acting on the alerts they generate — which produces the appearance of security maturity without any of its actual benefit, and often trains teams to start ignoring alerts altogether once the backlog grows large enough.
MBest Practices and Common Mistakes
Most of the guidance below is inexpensive to implement and disproportionately effective — the kind of foundational hygiene that closes off the majority of realistic attack paths before more advanced controls are even needed.
Pin Every Third-Party Action to a Commit Hash
Never trust a mutable tag for code that runs with access to your repository and secrets.
Scope Workflow Permissions Explicitly
Set the minimum required permissions for the automatically issued token on every workflow, rather than relying on broad defaults.
Require Reviewed, Protected Branches
Enforce mandatory code review and status checks before any change reaches the branch your builds are triggered from.
Generate and Verify Build Attestations
Turn on artifact attestations for release builds, and actually verify them at deployment time rather than only generating them.
Replace Long-Lived Secrets with OIDC
Wherever a cloud provider supports federated identity, prefer short-lived, automatically expiring tokens over standing credentials.
Triage Dependency Alerts on a Cadence
Treat vulnerability alerts as a recurring operational task with clear ownership, not a dashboard nobody is assigned to check.
Common Mistakes
- Assuming a private repository is automatically a secure build environment, and skipping supply chain controls entirely because “nobody can see our code.”
- Granting workflows write access to unrelated repositories “just in case,” rather than scoping access to exactly what each workflow needs.
- Treating a passing vulnerability scan as proof of safety, when the scan only covers known, previously disclosed vulnerabilities and says nothing about novel or intentionally hidden malicious code.
- Allowing workflows triggered by external pull requests to run with the same elevated permissions as workflows triggered by trusted, internal changes.
NReal-World Examples
Every concept in this guide traces back to an incident that made it necessary. Studying a few of them makes the abstract framework feel concrete.
The event-stream Incident (2018)
A widely used, small JavaScript utility package was handed off to a new “volunteer” maintainer, who then quietly added a malicious dependency designed to steal cryptocurrency wallet credentials from downstream applications — illustrating how maintainer trust itself can be socially engineered and exploited.
The SolarWinds Build Compromise (2020)
Attackers gained access to the build environment of a widely used IT management platform and injected malicious code directly into the build process, so that the vendor’s own official, digitally signed software updates carried the malware — an attack that specifically targeted trust in the build system rather than the source code.
The Codecov Bash Uploader Compromise (2021)
A widely used CI script, embedded directly into thousands of organizations’ build pipelines, was modified by attackers to exfiltrate environment variables and secrets from every pipeline that executed it — demonstrating how a single popular CI helper script functions as a high-leverage supply chain target.
The XZ Utils Backdoor Attempt (2024)
An individual who had spent years building trust as a co-maintainer of a widely depended-upon compression library used that earned trust to introduce a sophisticated, deliberately obfuscated backdoor into the build process — caught unusually early, and widely cited afterward as a case study in exactly why build-process integrity, not just code review, has to be independently verifiable.
OFrequently Asked Questions
PSummary and Key Takeaways
Key Takeaways
- A software supply chain spans every stage from source code through dependencies, the build system, the registry, and finally deployment — and a compromise at any single stage can silently propagate to everything after it.
- SLSA (“Supply-chain Levels for Software Artifacts”) makes trust in software verifiable by focusing on the integrity of the build process rather than attempting to manually re-inspect every artifact’s contents.
- SLSA’s four levels progress from a documented build process, to a hosted build with signed provenance, to a fully hardened build environment where even a compromised build script cannot forge convincing provenance.
- GitHub’s native tooling — Dependabot, secret scanning, CodeQL, OIDC federated credentials, and build provenance attestations — maps directly onto both the supply chain stages and SLSA’s own requirements, making meaningful adoption achievable without custom infrastructure.
- Keyless signing via Sigstore removes the historical burden of long-term private key management by issuing short-lived, identity-bound certificates for each signing event, recorded permanently in a public transparency log.
- Provenance without enforcement is just documentation. The real security value only appears once deployment pipelines are configured to actively verify attestations and reject artifacts that fail the check.
- Every landmark supply chain incident — from event-stream to SolarWinds to the XZ Utils backdoor — targeted trust in a process rather than a flaw in a single line of code, which is precisely the gap SLSA and GitHub’s attestation tooling were built to close.
