Basic security steps
Level 1 means the supply chain is documented, there’s infrastructure to generate provenance, and systems are prepared for higher SLSA levels.
SLSA is a set of standards and technical controls you can adopt to improve artifact integrity, and build towards completely resilient systems. It’s not a single tool, but a step-by-step outline to prevent artifacts being tampered with and tampered artifacts from being used, and at the higher levels, hardening up the platforms that make up a supply chain. These requirements are explained below, along with the rest of the essential specifications.
Protecting against key threats
Supply chain attacks are an ever-present threat, exploiting weakpoints to interfere with software. The SLSA framework establishes three trust boundaries encouraging the right standards, attestation and technical controls, so you can harden a system from these threats and risks.
This means automatic capability to analyse artifacts, guarantee the original source code, protect against interference that can happen in the build and distribution processes, isolate any hidden vulnerabilities and knowing with certainty which system components might be affected.
Real world examples
High profile attacks like SolarWinds, Codecov or Linux hypocrite commits exploit the kind of supply chain integrity vulnerabilities which may go unnoticed or be underdeveloped, and quickly become extremely public, disruptive and costly in today’s environment.
Attacks can occur at every link in a typical software supply chain, which puts an extra burden on anyone involved in complex critical systems. SLSA's designed with these examples in mind to make sure they’re common knowledge, and easier to protect against.
How it fits into the security ecosystem
There’s more to security than just protection against tampering. From vulnerability management and fuzzing to testing and developer trust, many of these require solutions where effort’s focused on the source. That’s where SLSA complements your wider security efforts, giving you confidence that the code you run is the code you analyzed.
SLSA’s requirements look at the three general main areas involved in a software artifact’s creation, and where vulnerabilities target - the build, the source, and the dependencies. As the levels scale up, they show that work’s been done to assure there’s more integrity in each area, which can be helpful in scenario planning.
Build integrity
SLSA starts at the build, the last step before an artifact’s released. This makes sure software’s built from the correct sources and dependencies, and hasn’t been modified. More resilient build integrity means protection from modifying code after source control, compromised build platforms or bypassing CI/CD.
Source integrity
Requirements can then focus on the source. All source code should reflect the intent of the software producer, that code and change history stay available for investigation. More resilient source integrity means better protection from bad code submitted without review or compromised source control systems.
Dependencies
Any other software artifacts fetched during the build process. Once the earlier security checks have been put into place, applying SLSA checks recursively to any dependencies in the system can then be followed up, which helps protect potentially massive attack surfaces against dependency confusion attacks.
Basic security steps
Level 1 means the supply chain is documented, there’s infrastructure to generate provenance, and systems are prepared for higher SLSA levels.
After the build
Level 2 shows more trustworthiness in the build, builders are source-aware, and signatures are used to prevent provenance being tampered with.
Back to source
Level 3 shows that a system’s builds are fully trustworthy, build definitions come from the source and a system has more hardened CI.
Across the chain
Level 4 means the build environment is fully accounted for, dependencies are tracked in provenance and insider threats are ruled out.
SLSA is a set of standards and technical controls you can adopt to improve artifact integrity, and build towards completely resilient systems. It’s not a single tool, but a step-by-step outline to prevent artifacts being tampered with and tampered artifacts from being used, and at the higher levels, hardening up the platforms that make up a supply chain. These requirements are explained below, along with the rest of the essential specifications.
Protecting against key threats
Supply chain attacks are an ever-present threat, exploiting weakpoints to interfere with software. The SLSA framework establishes three trust boundaries encouraging the right standards, attestation and technical controls, so you can harden a system from these threats and risks.
This means automatic capability to analyse artifacts, guarantee the original source code, protect against interference that can happen in the build and distribution processes, isolate any hidden vulnerabilities and knowing with certainty which system components might be affected.
Real world examples
High profile attacks like SolarWinds, Codecov or Linux hypocrite commits exploit the kind of supply chain integrity vulnerabilities which may go unnoticed or be underdeveloped, and quickly become extremely public, disruptive and costly in today’s environment.
Attacks can occur at every link in a typical software supply chain, which puts an extra burden on anyone involved in complex critical systems. SLSA's designed with these examples in mind to make sure they’re common knowledge, and easier to protect against.
How it fits into the security ecosystem
There’s more to security than just protection against tampering. From vulnerability management and fuzzing to testing and developer trust, many of these require solutions where effort’s focused on the source. That’s where SLSA complements your wider security efforts, giving you confidence that the code you run is the code you analyzed.
SLSA’s requirements look at the three general main areas involved in a software artifact’s creation, and where vulnerabilities target - the build, the source, and the dependencies. As the levels scale up, they show that work’s been done to assure there’s more integrity in each area, which can be helpful in scenario planning.
Build integrity
SLSA starts at the build, the last step before an artifact’s released. This makes sure software’s built from the correct sources and dependencies, and hasn’t been modified. More resilient build integrity means protection from modifying code after source control, compromised build platforms or bypassing CI/CD.
Source integrity
Requirements can then focus on the source. All source code should reflect the intent of the software producer, that code and change history stay available for investigation. More resilient source integrity means better protection from bad code submitted without review or compromised source control systems.
Dependencies
Any other software artifacts fetched during the build process. Once the earlier security checks have been put into place, applying SLSA checks recursively to any dependencies in the system can then be followed up, which helps protect potentially massive attack surfaces against dependency confusion attacks.
Basic security steps
Level 1 means the supply chain is documented, there’s infrastructure to generate provenance, and systems are prepared for higher SLSA levels.
After the build
Level 2 shows more trustworthiness in the build, builders are source-aware, and signatures are used to prevent provenance being tampered with.
Back to source
Level 3 shows that a system’s builds are fully trustworthy, build definitions come from the source and a system has more hardened CI.
Across the chain
Level 4 means the build environment is fully accounted for, dependencies are tracked in provenance and insider threats are ruled out.
Before diving into the SLSA Levels, we need to establish a core set of terminology and models to describe what we’re protecting.
SLSA’s framework addresses every step of the software supply chain - the sequence of steps resulting in the creation of an artifact. We represent a supply chain as a directed acyclic graph of sources, builds, dependencies, and packages. One artifact’s supply chain is a combination of its dependencies’ supply chains plus its own sources and builds.
| Term | Description | Example |
|---|---|---|
| Artifact | An immutable blob of data; primarily refers to software, but SLSA can be used for any artifact. | A file, a git commit, a directory of files (serialized in some way), a container image, a firmware image. |
| Source | Artifact that was directly authored or reviewed by persons, without modification. It is the beginning of the supply chain; we do not trace the provenance back any further. | Git commit (source) hosted on GitHub (platform). |
| Build | Process that transforms a set of input artifacts into a set of output artifacts. The inputs may be sources, dependencies, or ephemeral build outputs. | .travis.yml (process) run by Travis CI (platform). |
| Package | Artifact that is “published” for use by others. In the model, it is always the output of a build process, though that build process can be a no-op. | Docker image (package) distributed on DockerHub (platform). A ZIP file containing source code is a package, not a source, because it is built from some other source, such as a git commit. |
| Dependency | Artifact that is an input to a build process but that is not a source. In the model, it is always a package. | Alpine package (package) distributed on Alpine Linux (platform). |
We model a build as running on a multi-tenant platform, where each execution is independent. A tenant defines the build, including the input source artifact and the steps to execute. In response to an external trigger, the platform runs the build by initializing the environment, fetching the source and possibly some dependencies, and then starting execution inside the environment. The build then performs arbitrary steps, possibly fetching additional dependencies, and outputs one or more artifacts.
| Term | Description |
|---|---|
| Platform | System that allows tenants to run build. Technically, it is the transitive closure of software and services that must be trusted to faithfully execute the build. |
| Service | A platform that is hosted, not a developer’s machine. (Term used in requirements.) |
| Build | Process that converts input sources and dependencies into output artifacts, defined by the tenant and executed within a single environment. |
| Steps | The set of actions that comprise a build, defined by the tenant. |
| Environment | Machine, container, VM, or similar in which the build runs, initialized by the platform. In the case of a distributed build, this is the collection of all such machines/containers/VMs that run steps. |
| Trigger | External event or request causing the platform to run the build. |
| Source | Top-level input artifact required by the build. |
| Dependencies | Additional input artifacts required by the build. |
| Outputs | Collection of artifacts produced by the build. |
| Admin | Person with administrative access to the platform, potentially allowing them to tamper with the build process or access secret material. |
| Term | Example |
|---|---|
| Platform | GitHub Actions + runner + runner’s dependent services |
| Build | Workflow or job (either would be OK) |
| Steps | steps |
| Environment | runs-on |
| Trigger | workflow trigger |
| Source | git commit defining the workflow |
| Dependencies | any other artifacts fetched during execution |
| Admin | GitHub personnel |
Suppose a Bazel build runs on GitHub Actions using Bazel’s remote execution
feature. Some steps (namely bazel itself) run on a GitHub Actions runner while
other steps (Bazel actions) run on a remote execution service.
In this case, the build’s environment is the union of the GitHub Actions runner environment plus the remote execution environment.
The model can still work for the case of a developer building on their local workstation, though this does not meet SLSA 2+.
| Term | Example |
|---|---|
| Platform | developer’s workstation |
| Build | whatever they ran |
| Steps | whatever they ran |
| Environment | developer’s workstation |
| Trigger | commands that the developer ran |
| Admin | developer |
SLSA protects against tampering during the software supply chain, but how? The answer depends on the use case in which SLSA is applied. Below describe the three main use cases for SLSA.
Reducing risk within an organization from insiders and compromised accounts
In its simplest form, SLSA can be used entirely within an organization to reduce risk from internal sources. This is the easiest case in which to apply SLSA because there is no need to transfer trust across organizational boundaries.
Example ways an organization might use SLSA internally:
Case study: Google (Binary Authorization for Borg)
Reducing risk from consuming open source software
SLSA can also be used to reduce risk for consumers of open source software. The focus here is to map built packages back to their canonical sources and dependencies. In this way, consumers need only trust a small number of secure build systems rather than the many thousands of developers with upload permissions across various packages.
Example ways an open source ecosystem might use SLSA to protect users:
Case study: SUSE
Reducing risk from consuming vendor provided software and services
Finally, SLSA can be used to reduce risk for consumers of vendor provided software and services. Unlike open source, there is no canonical source repository to map to, so instead the focus is on trustworthiness of claims made by the vendor.
Example ways a consumer might use SLSA for vendor provided software:
For a look at how SLSA might be applied to open source in the future, see the hypothetical curl example.
SLSA is organized into a series of levels that provide increasing integrity guarantees. This gives you confidence that software hasn’t been tampered with and can be securely traced back to its source.
This section is an informative overview of the SLSA levels, describing their purpose and guarantees. For the normative requirements at each level, see Requirements.
SLSA is a set of incrementally adoptable security guidelines, established by industry consensus. The standards set by SLSA are guiding principles for both software producers and consumers: producers can follow the guidelines to make their software more secure, and consumers can make decisions based on a software package’s security posture. SLSA’s four levels are designed to be incremental and actionable, and to protect against specific integrity attacks. SLSA 4 represents the ideal end state, and the lower levels represent milestones with corresponding integrity guarantees.
| Level | Description | Example |
|---|---|---|
| 1 | Documentation of the build process | Unsigned provenance |
| 2 | Tamper resistance of the build service | Hosted source/build, signed provenance |
| 3 | Extra resistance to specific threats | Security controls on host, non-falsifiable provenance |
| 4 | Highest levels of confidence and trust | Two-party review + hermetic builds |
It can take years to achieve the ideal security state - intermediate milestones are important. SLSA guides you through gradually improving the security of your software. Artifacts used in critical infrastructure or vital business operations may want to attain a higher level of security, whereas software that poses a low risk can stop when they’re comfortable.
| Level | Requirements |
|---|---|
| 0 | No guarantees. SLSA 0 represents the lack of any SLSA level. |
| 1 | The build process must be fully scripted/automated and generate provenance. Provenance is metadata about how an artifact was built, including the build process, top-level source, and dependencies. Knowing the provenance allows software consumers to make risk-based security decisions. Provenance at SLSA 1 does not protect against tampering, but it offers a basic level of code source identification and can aid in vulnerability management. |
| 2 | Requires using version control and a hosted build service that generates authenticated provenance. These additional requirements give the software consumer greater confidence in the origin of the software. At this level, the provenance prevents tampering to the extent that the build service is trusted. SLSA 2 also provides an easy upgrade path to SLSA 3. |
| 3 | The source and build platforms meet specific standards to guarantee the auditability of the source and the integrity of the provenance respectively. We envision an accreditation process whereby auditors certify that platforms meet the requirements, which consumers can then rely on. SLSA 3 provides much stronger protections against tampering than earlier levels by preventing specific classes of threats, such as cross-build contamination. |
| 4 | Requires two-person review of all changes and a hermetic, reproducible build process. Two-person review is an industry best practice for catching mistakes and deterring bad behavior. Hermetic builds guarantee that the provenance’s list of dependencies is complete. Reproducible builds, though not strictly required, provide many auditability and reliability benefits. Overall, SLSA 4 gives the consumer a high degree of confidence that the software has not been tampered with. |
The SLSA level is not transitive (see our FAQs). This makes each artifact’s SLSA rating independent from one another, allowing parallel progress and prioritization based on risk. The level describes the integrity protections of an artifact’s build process and top-level source, but nothing about the artifact’s dependencies. Dependencies have their own SLSA ratings, and it is possible for a SLSA 4 artifact to be built from SLSA 0 dependencies.
SLSA can help reduce supply chain threats in a software artifact, but there are limitations.
As part of our roadmap, we’ll explore how to identify important components, how to determine aggregate risk throughout a supply chain, and the role of accreditation.
This section covers all of the technical requirements for an artifact to meet the SLSA Levels.
For background, see Introduction and Terminology. To better understand the reasoning behind the requirements, see Threats and mitigations.
Reminder: SLSA is in
alpha. The definitions below are not yet finalized and subject to change, particularly SLSA 3-4.
| Requirement | SLSA 1 | SLSA 2 | SLSA 3 | SLSA 4 |
|---|---|---|---|---|
| Source - Version controlled | ✓ | ✓ | ✓ | |
| Source - Verified history | ✓ | ✓ | ||
| Source - Retained indefinitely | 18 mo. | ✓ | ||
| Source - Two-person reviewed | ✓ | |||
| Build - Scripted build | ✓ | ✓ | ✓ | ✓ |
| Build - Build service | ✓ | ✓ | ✓ | |
| Build - Build as code | ✓ | ✓ | ||
| Build - Ephemeral environment | ✓ | ✓ | ||
| Build - Isolated | ✓ | ✓ | ||
| Build - Parameterless | ✓ | |||
| Build - Hermetic | ✓ | |||
| Build - Reproducible | ○ | |||
| Provenance - Available | ✓ | ✓ | ✓ | ✓ |
| Provenance - Authenticated | ✓ | ✓ | ✓ | |
| Provenance - Service generated | ✓ | ✓ | ✓ | |
| Provenance - Non-falsifiable | ✓ | ✓ | ||
| Provenance - Dependencies complete | ✓ | |||
| Common - Security | ✓ | |||
| Common - Access | ✓ | |||
| Common - Superusers | ✓ |
○ = REQUIRED unless there is a justification
See also Terminology for general SLSA concepts. The definitions below are only used in this document.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Immutable reference: An identifier that is guaranteed to always point to the same, immutable artifact. This MUST allow the consumer to locate the artifact and SHOULD include a cryptographic hash of the artifact’s contents to ensure integrity. Examples: git URL + branch/tag/ref + commit ID; cloud storage bucket ID + SHA-256 hash; Subversion URL (no hash).
Provenance: Metadata about how an artifact was produced.
Revision: An immutable, coherent state of a source. In Git, for example, a revision is a commit in the history reachable from a specific branch in a specific repository. Different revisions within one repo MAY have different levels. Example: the most recent revision on a branch meets SLSA 4 but very old historical revisions before the cutoff do not.
Strong authentication: Authentication that maps back to a specific person using an authentication mechanism which is resistant to account and credential compromise. For example, 2-factor authentication (2FA) where one factor is a hardware security key (i.e. YubiKey).
Trusted persons: Set of persons who are granted the authority to maintain a software project. For example, https://github.com/MarkLodato/dotfiles has just one trusted person (MarkLodato), while https://hg.mozilla.org/mozilla-central has a set of trusted persons with write access to the mozilla-central repository.
| Requirement | Description | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
| Version controlled |
Every change to the source is tracked in a version control system that meets the following requirements:
Most popular version control system meet this requirement, such as git, Mercurial, Subversion, or Perforce. NOTE: This does NOT require that the code, uploader/reviewer identities, or change history be made public. Rather, some organization MUST attest to the fact that these requirements are met, and it is up to the consumer whether this attestation is sufficient. “○” = RECOMMENDED. | ○ | ✓ | ✓ | ✓ |
| Verified history |
Every change in the revision’s history has at least one strongly authenticated actor identity (author, uploader, reviewer, etc.) and timestamp. It MUST be clear which identities were verified, and those identities MUST use two-step verification or similar. (Exceptions noted below.)
| ✓ | ✓ | ||
| Retained indefinitely |
The revision and its change history are preserved indefinitely and cannot be deleted, except when subject to an established and transparent policy for obliteration, such as a legal or policy requirement.
| 18 mo. | ✓ | ||
| Two-person reviewed |
Every change in the revision’s history was agreed to by two trusted persons prior to submission, and both of these trusted persons were strongly authenticated. (Exceptions from Verified History apply here as well.)
| ✓ |
Requirements on build process:
| Requirement | Description | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
| Scripted build |
All build steps were fully defined in some sort of “build script”. The only manual command, if any, was to invoke the build script. Examples:
| ✓ | ✓ | ✓ | ✓ |
| Build service |
All build steps ran using some build service, not on a developer’s workstation. Examples: GitHub Actions, Google Cloud Build, Travis CI. | ✓ | ✓ | ✓ | |
| Build as code |
The build definition and configuration executed by the build service is verifiably derived from text file definitions stored in a version control system. Verifiably derived can mean either fetched directly through a trusted channel, or that the derived definition has some trustworthy provenance chain linking back to version control. Examples:
| ✓ | ✓ | ||
| Ephemeral environment |
The build service ensured that the build steps ran in an ephemeral environment, such as a container or VM, provisioned solely for this build, and not reused from a prior build. | ✓ | ✓ | ||
| Isolated |
The build service ensured that the build steps ran in an isolated environment free of influence from other build instances, whether prior or concurrent.
| ✓ | ✓ | ||
| Parameterless |
The build output cannot be affected by user parameters other than the build entry point and the top-level source location. In other words, the build is fully defined through the build script and nothing else. Examples:
| ✓ | |||
| Hermetic |
All transitive build steps, sources, and dependencies were fully declared up front with immutable references, and the build steps ran with no network access. The user-defined build script:
The build service:
| ✓ | |||
| Reproducible |
Re-running the build steps with identical input artifacts results in bit-for-bit identical output. Builds that cannot meet this MUST provide a justification why the build cannot be made reproducible. “○” means that this requirement is “best effort”. The user-provided build script SHOULD declare whether the build is intended to be reproducible or a justification why not. The build service MAY blindly propagate this intent without verifying reproducibility. A consumer MAY reject the build if it does not reproduce. | ○ |
Requirements on the process by which provenance is generated and consumed:
| Requirement | Description | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
| Available |
The provenance is available to the consumer in a format that the consumer accepts. The format SHOULD be in-toto SLSA Provenance, but another format MAY be used if both producer and consumer agree and it meets all the other requirements. | ✓ | ✓ | ✓ | ✓ |
| Authenticated |
The provenance’s authenticity and integrity can be verified by the consumer. This SHOULD be through a digital signature from a private key accessible only to the service generating the provenance. | ✓ | ✓ | ✓ | |
| Service generated |
The data in the provenance MUST be obtained from the build service (either because the generator is the build service or because the provenance generator reads the data directly from the build service). Regular users of the service MUST NOT be able to inject or alter the contents, except as noted below. The following provenance fields MAY be generated by the user-controlled build steps:
| ✓ | ✓ | ✓ | |
| Non-falsifiable |
Provenance cannot be falsified by the build service’s users. NOTE: This requirement is a stricter version of Service Generated.
The following provenance fields MAY be generated by the user-controlled build steps without the build service verifying their correctness:
| ✓ | ✓ | ||
| Dependencies complete |
Provenance records all build dependencies that were available while running the build steps. This includes the initial state of the machine, VM, or container of the build worker.
| ✓ |
Requirements on the contents of the provenance:
| Requirement | Description | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
| Identifies artifact |
The provenance MUST identify the output artifact via at least one cryptographic hash. The provenance MAY provide multiple identifying cryptographic hashes using different algorithms. When only one hash is provided, the RECOMMENDED algorithm is SHA-256 for cross-system compatibility. If another algorithm is used, it SHOULD be resistant to collisions and second preimages. | ✓ | ✓ | ✓ | ✓ |
| Identifies builder |
The provenance identifies the entity that performed the build and generated the
provenance. This represents the entity that the consumer MUST trust. Examples:
“GitHub Actions with a GitHub-hosted worker”, “ | ✓ | ✓ | ✓ | ✓ |
| Identifies build instructions |
The provenance identifies the top-level instructions used to execute the build. The identified instructions SHOULD be at the highest level available to the build (e.g. if the build is told to run build.sh it SHOULD list build.sh and NOT the individual instructions in build.sh). If build-as-code is used, this SHOULD be the source repo and entry point of the build config (as in the GitHub Actions example). If the build isn’t defined in code it MAY list the details of what it was asked to do (as in the Google Cloud Build RPC example or the Explicitly Run Commands example). | ✓ | ✓ | ✓ | ✓ |
| Identifies source code |
The provenance identifies the repository origin(s) for the source code used in the build. The identified repositories SHOULD only include source used directly in the build. The source of dependencies SHOULD NOT be included. At level 2 this information MAY come from users and DOES NOT need to be authenticated by the builder. At level 3+ this information MUST be authenticated by the builder (i.e. the builder either needs to have fetched the source itself or observed the fetch). At level 4 this information MUST be complete (i.e. all source repositories used in the build are listed). | ✓ | ✓ (Authenticated) | ✓ (Complete) | |
| Identifies entry point |
The provenance identifies the “entry point” of the build definition (see build-as-code) used to drive the build including what source repo the configuration was read from. Example:
| ✓ | ✓ | ||
| Includes all build parameters |
The provenance includes all build parameters under a user’s control. See Parameterless for details. (At L3, the parameters MUST be listed; at L4, they MUST be empty.) | ✓ | ✓ | ||
| Includes all transitive dependencies |
The provenance includes all transitive dependencies listed in Dependencies Complete. | ✓ | |||
| Includes reproducible info |
The provenance includes a boolean indicating whether build is intended to be reproducible and, if so, all information necessary to reproduce the build. See Reproducible for more details. | ✓ | |||
| Includes metadata |
The provenance includes metadata to aid debugging and investigations. This SHOULD at least include start and end timestamps and a unique identifier to allow finding detailed debug logs. “○” = RECOMMENDED. | ○ | ○ | ○ | ○ |
Common requirements for every trusted system involved in the supply chain (source, build, distribution, etc.)
| Requirement | Description | L1 | L2 | L3 | L4 |
|---|---|---|---|---|---|
| Security |
The system meets some TBD baseline security standard to prevent compromise. (Patching, vulnerability scanning, user isolation, transport security, secure boot, machine identity, etc. Perhaps NIST 800-53 or a subset thereof.) | ✓ | |||
| Access |
All physical and remote access MUST be rare, logged, and gated behind multi-party approval. | ✓ | |||
| Superusers |
Only a small number of platform admins MAY override the guarantees listed here. Doing so MUST require approval of a second platform admin. | ✓ |
Attacks can occur at every link in a typical software supply chain, and these kinds of attacks are increasingly public, disruptive, and costly in today’s environment.
SLSA’s levels are designed to mitigate the risk of these attacks. This section enumerates possible attacks throughout the supply chain and shows how SLSA can help. For a background, see Terminology.
SLSA’s primary focus is supply chain integrity, with a secondary focus on availability. Integrity means protection against tampering or unauthorized modification at any stage of the software lifecycle. Within SLSA, we divide integrity into source integrity vs build integrity.
Source integrity: Ensure that all changes to the source code reflect the intent of the software producer. Intent of an organization is difficult to define, so SLSA approximates this as approval from two authorized representatives.
Build integrity: Ensure that the package is built from the correct, unmodified sources and dependencies according to the build recipe defined by the software producer, and that artifacts are not modified as they pass between development stages.
Availability: Ensure that the package can continue to be built and maintained in the future, and that all code and change history is available for investigations and incident response.
Many recent high-profile attacks were consequences of supply-chain integrity vulnerabilities, and could have been prevented by SLSA’s framework. For example:
| Integrity threat | Known example | How SLSA can help | |
|---|---|---|---|
| A | Submit unauthorized change (to source repo) | Linux hypocrite commits: Researcher attempted to intentionally introduce vulnerabilities into the Linux kernel via patches on the mailing list. | Two-person review caught most, but not all, of the vulnerabilities. |
| B | Compromise source repo | PHP: Attacker compromised PHP's self-hosted git server and injected two malicious commits. | A better-protected source code platform would have been a much harder target for the attackers. |
| C | Build from modified source (not matching source repo) | Webmin: Attacker modified the build infrastructure to use source files not matching source control. | A SLSA-compliant build server would have produced provenance identifying the actual sources used, allowing consumers to detect such tampering. |
| D | Compromise build process | SolarWinds: Attacker compromised the build platform and installed an implant that injected malicious behavior during each build. | Higher SLSA levels require stronger security controls for the build platform, making it more difficult to compromise and gain persistence. |
| E | Use compromised dependency (i.e. A-H, recursively) | event-stream: Attacker added an innocuous dependency and then later updated the dependency to add malicious behavior. The update did not match the code submitted to GitHub (i.e. attack F). | Applying SLSA recursively to all dependencies would have prevented this particular vector, because the provenance would have indicated that it either wasn't built from a proper builder or that the source did not come from GitHub. |
| F | Upload modified package (not matching build process) | CodeCov: Attacker used leaked credentials to upload a malicious artifact to a GCS bucket, from which users download directly. | Provenance of the artifact in the GCS bucket would have shown that the artifact was not built in the expected manner from the expected source repo. |
| G | Compromise package repo | Attacks on Package Mirrors: Researcher ran mirrors for several popular package repositories, which could have been used to serve malicious packages. | Similar to above (F), provenance of the malicious artifacts would have shown that they were not built as expected or from the expected source repo. |
| H | Use compromised package | Browserify typosquatting: Attacker uploaded a malicious package with a similar name as the original. | SLSA does not directly address this threat, but provenance linking back to source control can enable and enhance other solutions. |
| Availability threat | Known example | How SLSA can help | |
|---|---|---|---|
| E | Dependency becomes unavailable | Mimemagic: Maintainer intentionally removes package or version of package from repository with no warning. Network errors or service outages may also make packages unavailable temporarily. | SLSA does not directly address this threat. |
A SLSA level helps give consumers confidence that software has not been tampered with and can be securely traced back to source—something that is difficult, if not impossible, to do with most software today.
IMPORTANT: This is a work in progress.
What follows is a comprehensive technical analysis of supply chain threats and their corresponding mitigations in SLSA. The goals are to:
A source integrity threat is a potential for an adversary to introduce a change to the source code that does not reflect the intent of the software producer. This includes the threat of an authorized developer introducing an unauthorized change—in other words, an insider threat.
An adversary introduces a change through the official source control management interface without any special administrator privileges.
Threat: Submit bad code to the source repository without another person reviewing.
Mitigation: Source repository requires two-person approval for all changes. [Two-person reviewed @ SLSA 4]
Example: Adversary directly pushes a change to a GitHub repo’s main branch.
Solution: Configure GitHub’s “branch protection” feature to require pull request
reviews on the main branch.
Threat: Propose a change using one account and then approve it using another account.
Mitigation: Source repository requires approval from two different, trusted persons. If the proposer is trusted, only one approval is needed; otherwise two approvals are needed. The software producer maps accounts to trusted persons. [Two-person reviewed → Different persons @ SLSA 4]
Example: Adversary creates a pull request using a secondary account and then approves and merges the pull request using their primary account. Solution: Configure branch protection to require two approvals and ensure that all repository contributors and owners map to unique persons.
Threat: Exploit a robot account that has the ability to submit changes without two-person review.
Mitigation: All changes require two-person review, even changes authored by robots. [Two-person reviewed @ SLSA 4]
Example: A file within the source repository is automatically generated by a robot, which is allowed to submit without review. Adversary compromises the robot and submits a malicious change without review. Solution: Require human review for these changes.
TODO(#196) This solution may not be practical. Should there be an exception for locked down robot accounts?
Threat: Exploit a review exception to submit a bad change without review.
Mitigation: All changes require two-person review without exception. [Two-person reviewed @ SLSA 4]
Example: Source repository requires two-person review on all changes except
for “documentation changes,” defined as only touching files ending with .md or
.html. Adversary submits a malicious executable named evil.md without review
using this exception, and then builds a malicious package containing this
executable. This would pass the policy because the source repository is correct,
and the source repository does require two-person review. Solution: Do not allow
such exceptions.
TODO This solution may not be practical in all circumstances. Are there any valid exceptions? If so, how do we ensure they cannot be exploited?
Threat: Modify the code after it has been reviewed but before submission.
Mitigation: Source control platform invalidates approvals whenever the proposed change is modified. [NOT REQUIRED FOR SLSA]
Example: Source repository requires two-person review on all changes. Adversary sends a “good” pull request to a peer, who approves it. Adversary then modifies it to contain “bad” code before submitting. Solution: Configure branch protection to dismiss stale approvals when new changes are pushed.
Note: This is not currently a SLSA requirement because the productivity hit is considered too great to outweigh the security benefit. The cost of code review is already too high for most projects, given current code review tooling, so making code review even costlier would not further our goals. However, this should be considered for future SLSA revisions once the state-of-the-art for code review has improved and the cost can be minimized.
Threat: Send a change that is meaningless for a human to review that looks benign but is actually malicious.
Mitigation: Code review system ensures that all reviews are informed and meaningful. [Two-person reviewed → Informed review @ SLSA 4]
Example: A proposed change updates a file, but the reviewer is only presented with a diff of the cryptographic hash, not of the file contents. Thus, the reviewer does not have enough context to provide a meaningful review. Solution: the code review system should present the reviewer with a content diff or some other information to make an informed decision.
Threat: Get a change reviewed in one context and then transfer it to a different context.
Mitigation: Approvals are context-specific. [Two-person reviewed -> Context-specific approvals @ SLSA 4]
Example: MyPackage’s source repository requires two-person review. Adversary forks the repo, submits a change in the fork with review from a colluding colleague (who is not trusted by MyPackage), then merges the change back into the upstream repo. Solution: The merge should still require review, even though the fork was reviewed.
Threat: Compromise one or more trusted accounts and use those to submit and review own changes.
Mitigation: Source control platform verifies two-factor authentication, which increases the difficulty of compromising accounts. [Verified history → strong authentication @ SLSA 3]
Example: Trusted person uses a weak password on GitHub. Adversary guesses the weak password, logs in, and pushes changes to a GitHub repo. Solution: Configure GitHub organization to requires 2FA for all trusted persons. This would increase the difficulty of using the compromised password to log in to GitHub.
Threat: Request review for a series of two commits, X and Y, where X is bad and Y is good. Reviewer thinks they are approving only the final Y state whereas they are also implicitly approving X.
Mitigation: Only the version that is actually reviewed is the one that is approved. Any intermediate revisions don’t count as being reviewed. [Two-person reviewed @ SLSA 4]
Example: Adversary sends a pull request containing malicious commit X and benign commit Y that undoes X. In the pull request UI, reviewer only reviews and approves “changes from all commits”, which is a delta from HEAD to Y; they don’t see X. Adversary then builds from the malicious revision X. Solution: Policy does not accept this because the version X is not considered reviewed.
TODO This is implicit but not clearly spelled out in the requirements. We should consider clarifying if there is confusion or incorrect implementations.
Threat: Software producer intentionally submits “bad” code, following all proper processes.
Mitigation: Outside the scope of SLSA. Trust of the software producer is an important but separate property from integrity.
Example: A popular extension author sells the rights to a new owner, who then modifies the code to secretly mine bitcoin at the users’ expense. SLSA does not protect against this, though if the extension were open source, regular auditing may discourage this from happening.
Threat: Two trusted persons collude to author and approve a bad change.
Mitigation: Outside the scope of SLSA. We use “two trusted persons” as a proxy for “intent of the software producer”.
Threat: Construct a change that looks benign but is actually malicious, a.k.a. “bugdoor.”
Mitigation: Outside the scope of SLSA.
Threat: Reviewer approves changes without actually reviewing, a.k.a. “rubber stamping.”
Mitigation: Outside the scope of SLSA.
An adversary introduces a change to the source control repository through an administrative interface, or through a compromise of the underlying infrastructure.
Threat: Trusted person with “admin” privileges in a repository submits “bad” code bypassing existing controls.
Mitigation: All persons are subject to same controls, whether or not they have administrator privileges. Disabling the controls requires two-person review (and maybe notifies other trusted persons?) [Two-person reviewed @ SLSA 4]
Example 1: GitHub project owner pushes a change without review, even though GitHub branch protection is enabled. Solution: Enable the “Include Administrators” option for the branch protection.
Example 2: GitHub project owner disables “Include Administrators”, pushes a change without review, then re-enables “Include Administrators”. This currently has no solution on GitHub.
TODO This is implicit but not clearly spelled out in the requirements. We should consider clarifying since most if not all existing platforms do not properly address this threat.
Threat: Platform administrator abuses their privileges to bypass controls or to push a malicious version of the software.
Mitigation: TBD [Common requirements @ SLSA 4]
Example 1: GitHostingService employee uses an internal tool to push changes to the MyPackage source repo.
Example 2: GitHostingService employee uses an internal tool to push a malicious version of the server to serve malicious versions of MyPackage sources to a specific CI/CD client but the regular version to everyone else, in order to hide tracks.
Example 3: GitHostingService employee uses an internal tool to push a malicious version of the server that includes a backdoor allowing specific users to bypass branch protections. Adversary then uses this backdoor to submit a change to MyPackage without review.
Threat: Exploit a vulnerability in the implementation of the source code management system to bypass controls.
Mitigation: Outside the scope of SLSA.
A build integrity threat is a potential for an adversary to introduce behavior to a package that is not reflected in the source code, or to build from a source, dependency, and/or process that is not intended by the software producer.
An adversary builds from a version of the source code that does not match the official source control repository.
Threat: Build using the expected CI/CD process but from an unofficial fork of the code that may contain unauthorized changes.
Mitigation: Policy requires the provenance’s source location to match an expected value.
Example: MyPackage is supposed to be built from GitHub repo good/my-package.
Instead, it is built from evilfork/my-package. Solution: Policy rejects
because the source location does not match.
Threat: Build using the expected CI/CD process and source location, but checking out an “experimental” branch or similar that may contain code not intended for release.
Mitigation: Policy requires that the provenance’s source branch/tag matches an expected value, or that the source revision is reachable from an expected branch.
Example: MyPackage’s releases are tagged from the main branch, which has
branch protections. Adversary builds from the unprotected experimental branch
containing unofficial changes. Solution: Policy rejects because the source
revision is not reachable from main.
Threat: Build the package using the proper CI/CD platform but with unofficial build steps.
Mitigation: Policy requires that the provenance’s build configuration source matches an expected value.
Example: MyPackage is expected to be built by Google Cloud Build using the
build steps defined in the source’s cloudbuild.yaml file. Adversary builds
with Google Cloud Build, but using custom build steps provided over RPC.
Solution: Policy rejects because the build steps did not come from the expected
source.
Threat: Build using the expected CI/CD process, source location, and branch/tag, but using a target or entry point that is not intended for release.
Mitigation: Policy requires that the provenance’s build entry point matches an expected value.
Example: MyPackage is supposed to be built from the release workflow.
Adversary builds from the debug workflow. Solution: Policy rejects because the
entry point does not match.
Threat: Build from a version of the code that includes modifications after checkout.
Mitigation: Build service pulls directly from the source repository and accurately records the source location in provenance. [Identifies source code @ SLSA 3]
Example: Adversary fetches from MyPackage’s source repo, makes a local commit, then requests a build from that local commit. Builder records the fact that it did not pull from the official source repo. Solution: Policy rejects because the source repo is not as expected.
An adversary introduces an unauthorized change to a build output through tampering of the build process; or introduces false information into the provenance.
Threat: Build using the expected CI/CD process, source location, branch/tag, and entry point, but adding a build parameter that injects bad behavior into the output.
Mitigation: Policy only allows known-safe parameters. At SLSA 4, no parameters are allowed. [Parameterless @ SLSA 4]
Example: MyPackage’s GitHub Actions Workflow uses github.event.inputs to
allow users to specify custom compiler flags per invocation. Adversary sets a
compiler flag that overrides a macro to inject malicious behavior into the
output binary. Solution: Policy rejects because it does not allow any inputs.
Threat: Perform a “bad” build that persists a change in the build environment, then run a subsequent “good” build using that environment.
Mitigation: Builder ensures that each build environment is ephemeral, with no way to persist changes between subsequent builds. [Ephemeral environment @ SLSA 3]
Example: Build service uses the same machine for subsequent builds. Adversary
first runs a build that replaces the make binary with a malicious version,
then runs a subsequent build that otherwise would pass the policy. Solution:
Builder changes architecture to start each build with a clean machine image.
Threat: Perform a “bad” build that alters the behavior of another “good” build running in parallel.
Mitigation: Builds are isolated from one another, with no way for one to affect the other. [Isolated @ SLSA 3]
Example: Build service runs all builds for project MyPackage on the same machine as the same Linux user. Adversary starts a “bad” build that listens for the “good” build and swaps out source files, then starts a “good” build that would otherwise pass the policy. Solution: Builder changes architecture to isolate each build in a separate VM or similar.
Threat: Use or exfiltrate the provenance signing key or some other cryptographic secret that should only be available to the build service.
Mitigation: Builds are isolated from the trusted build service control plane, and only the control plane has access to cryptographic secrets. [Isolated @ SLSA 3]
Example: Provenance is signed on the build worker, which the adversary has control over. Adversary uses a malicious process that generates false provenance and signs it using the provenance signing key. Solution: Builder generates and signs provenance in the trusted control plane; the worker has no access to the key.
Threat: Generate false provenance and get the trusted control plane to sign it.
Mitigation: Trusted control plane generates all information that goes in the provenance, except (optionally) the output artifact hash. [Service generated @ SLSA 2]
Example: Provenance is generated on the build worker, which the adversary has
control over. Adversary uses a malicious process to get the build service to
claim that it was built from source repo good/my-package when it was really
built from evil/my-package. Solution: Builder generates and signs the
provenance in the trusted control plane; the worker reports the output artifacts
but otherwise has no influence over the provenance.
Threat: Add a “bad” artifact to a build cache that is later picked up by a “good” build process.
Mitigation: TBD
Example: Build system uses a build cache across builds, keyed by the hash of the source file. Adversary runs a malicious build that creates a “poisoned” cache entry with a falsified key, meaning that the value wasn’t really produced from that source. A subsequent build then picks up that poisoned cache entry.
TODO: similar to Source (do the same threats apply here?)
TODO: similar to Source
TODO: What exactly is this about? Is it about compromising the build process through a bad build tool, and/or is it about compromising the output package through a bad library? Does it involve all upstream threats to the dependency, or is it just about this particular use of the package (e.g. tampering on input, or choosing a bad dependency)?
TODO: Fill this out to give more examples of threats from compromised dependencies.
An adversary uploads a package not built from the proper build process.
Threat: Build using an unofficial CI/CD pipeline that does not build in the correct way.
Mitigation: Policy requires provenance showing that the builder matched an expected value.
Example: MyPackage is expected to be built on Google Cloud Build, which is trusted up to SLSA 4. Adversary builds on SomeOtherBuildService, which is only trusted up to SLSA 2, and then exploits SomeOtherBuildService to inject bad behavior. Solution: Policy rejects because builder is not as expected.
Threat: Upload a package without provenance.
Mitigation: Policy requires provenance showing that the package came from the expected CI/CD pipeline.
Example: Adversary uploads a malicious version of MyPackage to the package repository without provenance. Solution: Policy rejects because provenance is missing.
Threat: Take a good version of the package, modify it in some way, then re-upload it using the original provenance.
Mitigation: Policy requires provenance with a subject matching the hash of
the package.
Example: Adversary performs a proper build, modifies the artifact, then
uploads the modified version of the package to the repository along with the
provenance. Solution: Policy rejects because the hash of the artifact does not
match the subject found within the provenance.
Threat: Perform a build that would not otherwise pass the policy, then modify the provenance to make the policy checks pass.
Mitigation: Policy only accepts provenance that was cryptographically signed by the public key corresponding to an acceptable builder.
Example: MyPackage is expected to be built by GitHub Actions from the
good/my-package repo. Adversary builds with GitHub Actions from the
evil/my-package repo and then modifies the provenance so that the source looks
like it came from good/my-package. Solution: Policy rejects because the
cryptographic signature is no longer valid.
An adversary modifies the package on the package repository using an administrative interface or through a compromise of the infrastructure.
TODO: fill this out
An adversary modifies the package after it has left the package repository, or tricks the user into using an unintended package.
Threat: Register a package name that is similar looking to a popular package and get users to use your malicious package instead of the benign one.
Mitigation: Mostly outside the scope of SLSA. That said, the requirement to make the source available can be a mild deterrent, can aid investigation or ad-hoc analysis, and can complement source-based typosquatting solutions. [Verified history and Retained indefinitely @ SLSA 3]
An availability threat is a potential for an adversary to deny someone from reading a source and its associated change history, or from building a package.
Threat: Perform a build from a particular source revision and then delete that revision or cause it to get garbage collected, preventing anyone from inspecting the code.
Mitigation: Some system retains the revision and its version control history, making it available for inspection indefinitely. Users cannot delete the revision except as part of a transparent legal or privacy process. [Retained indefinitely @ SLSA 3-4]
Example: Adversary submits bad code to the MyPackage GitHub repo, builds from that revision, then does a force push to erase that revision from history (or requests GitHub to delete the repo.) This would make the revision unavailable for inspection. Solution: Policy prevents this by requiring a positive attestation showing that some system, such as GitHub, ensures retention and availability.
Threat: Unable to perform a build with the intended dependencies.
Mitigation: Outside the scope of SLSA. That said, some solutions to support Hermetic and Reproducible builds may also reduce the impact of this threat. [Hermetic Reproducible @ SLSA 4]
Threats that can compromise the ability to prevent or detect the supply chain security threats above but that do not fall cleanly into any one category.
Threat: Modify the policy to accept something that would not otherwise be accepted.
Mitigation: Policies themselves must meet SLSA 4, including two-party review.
Example: Policy for MyPackage only allows source repo good/my-package.
Adversary modifies the policy to also accept evil/my-package, then builds from
that repo and uploads a bad version of the package. Solution: Policy changes
require two-party review.
Threat: Forge the change metadata to alter attribution, timestamp, or discoverability of a change.
Mitigation: Source control platform strongly authenticates actor identity, timestamp, and parent revisions. [Verified history @ SLSA 3]
Example: Adversary submits a git commit with a falsified author and timestamp, and then rewrites history with a non-fast-forward update to make it appear to have been made long ago. Solution: Consumer detects this by seeing that such changes are not strongly authenticated and thus not trustworthy.
Threat: Exploit a cryptographic hash collision weakness to bypass one of the other controls.
Mitigation: Require cryptographically secure hash functions for code review and provenance, such as SHA-256.
Examples: Construct a “good” file and a “bad” file with the same SHA-1 hash. Get the “good” file reviewed and then submit the “bad” file, or get the “good” file reviewed and submitted and then build from the “bad” file. Solution: Only accept cryptographic hashes with strong collision resistance.
SLSA is not transitive in order to make the problem tractable. If SLSA 4 required dependencies to be SLSA 4, then reaching SLSA 4 would require starting at the very beginning of the supply chain and working forward. This is backwards, forcing us to work on the least risky component first and blocking any progress further downstream. By making each artifact’s SLSA rating independent from one another, it allows parallel progress and prioritization based on risk. (This is a lesson we learned when deploying other security controls at scale throughout Google.) We expect SLSA ratings to be composed to describe a supply chain’s overall security stance, as described in the case study vision.
When talking about reproducible builds, there are two related but distinct concepts: “reproducible” and “verified reproducible.”
“Reproducible” means that repeating the build with the same inputs results in bit-for-bit identical output. This property provides many benefits, including easier debugging, more confident cherry-pick releases, better build caching and storage efficiency, and accurate dependency tracking.
For these reasons, SLSA 4 requires reproducible builds unless there is a justification why the build cannot be made reproducible. Example justifications include profile-guided optimizations or code signing that invalidates hashes. Note that there is no actual reproduction, just a claim that reproduction is possible.
“Verified reproducible” means using two or more independent build systems to corroborate the provenance of a build. In this way, one can create an overall system that is more trustworthy than any of the individual components. This is often suggested as a solution to supply chain integrity. Indeed, this is one option to secure build steps of a supply chain. When designed correctly, such a system can satisfy all of the SLSA build requirements.
That said, verified reproducible builds are not a complete solution to supply chain integrity, nor are they practical in all cases:
Therefore, SLSA does not require verified reproducible builds directly. Instead, verified reproducible builds are one option for implementing the requirements.
For more on reproducibility, see Hermetic, Reproducible, or Verifiable?
in-toto is a framework to secure software supply chains hosted at the Cloud Native Computing Foundation. The in-toto specification provides a generalized workflow to secure different steps in a software supply chain. The SLSA specification recommends in-toto attestations as the vehicle to express Provenance and other attributes of software supply chains. Thus, in-toto can be thought of as the unopinionated layer to express information pertaining to a software supply chain, and SLSA as the opinionated layer specifying exactly what information must be captured in in-toto metadata to achieve the guarantees of a particular level.
in-toto’s official implementations written in Go, Java, and Rust include support for generating SLSA Provenance metadata. These APIs are used in other tools generating SLSA Provenance such as Sigstore’s cosign, the SLSA GitHub Generator, and the in-toto Jenkins plugin.
A software attestation is an authenticated statement (metadata) about a software artifact or collection of software artifacts. The primary intended use case is to feed into automated policy engines, such as in-toto and Binary Authorization.
This section provides a high-level overview of the attestation model, including standardized terminology, data model, layers, conventions for software attestations, and formats for different use cases.
A software attestation, not to be confused with a remote attestation in the trusted computing world, is an authenticated statement (metadata) about a software artifact or collection of software artifacts. Software attestations are a generalization of raw artifact/code signing.
With raw signing, a signature is directly over the artifact (or a hash of the artifact) and implies a single bit of metadata about the artifact, based on the public key. The exact meaning MUST be negotiated between signer and verifier, and a new keyset MUST be provisioned for each bit of information. For example, a signature might denote who produced an artifact, or it might denote fitness for some purpose, or something else entirely.
With an attestation, the metadata is explicit and the signature only denotes who created the attestation (authenticity). A single keyset can express an arbitrary amount of information, including things that are not possible with raw signing. For example, an attestation might state exactly how an artifact was produced, including the build command that was run and all of its dependencies (as in the case of SLSA Provenance).
This subsection explains how to choose the attestation format that’s best suited for your situation by considering factors such as intended use and who will be consuming the attestation.
Producers of first party code might consider the following questions:
If these are the main considerations, the organization can choose any format for internal use. To make an external claim of meeting a SLSA level, however, there needs to be a way for external users to consume and verify your provenance. Currently, SLSA recommends using the SLSA Provenance format for SLSA attestations since it is easy to verify using the Generic SLSA Verifier.
Producers of open source code might consider these questions:
In these situations, we encourage you to use the SLSA Provenance format. The SLSA Provenance format offers a path towards interoperability and cohesion across the open source ecosystem. Users can verify any provenance statement in this format using the Generic SLSA Verifier.
Producers of closed source code that is consumed by others might consider the following questions:
In these situations, you might not want to make all the details of your provenance available externally. Consider using Verification Summary Attestations (VSAs) to summarize provenance information in a sanitized way that’s safe for external consumption. For more about VSAs, see the Verification Summary Attestation section.
We define the following model to represent any software attestations, regardless of format. Not all formats will have all fields or all layers, but to be called a “software attestation” it MUST fit this general model.
The key words MUST, SHOULD, and MAY are to be interpreted as described in RFC 2119.
An example of an attestation in English follows with the components of the attestation mapped to the component names (and colors from the model diagram above):
Components:
We recommend a single suite of formats and conventions that work well together and have desirable security properties. Our hope is to align the industry around this particular suite because it makes everything easier. That said, we recognize that other choices MAY be necessary in various cases.
| Component | Recommendation |
|---|---|
| Envelope | DSSE (ECDSA over NIST P-256 (or stronger) and SHA-256.) |
| Statement | in-toto attestations |
| Predicate | Choose as appropriate, i.e.; Provenance, SPDX, other predicates defined by third-parties. If none are a good fit, invent a new one |
| Bundle | JSON Lines, see attestation bundle |
| Storage/Lookup | TBD |
To trace software back to the source and define the moving parts in a complex supply chain, provenance needs to be there from the very beginning. It’s the verifiable information about software artifacts describing where, when and how something was produced. For higher SLSA levels and more resilient integrity guarantees, provenance requirements are stricter and need a deeper, more technical understanding of the predicate.
This document defines the following predicate type within the in-toto attestation framework:
"predicateType": "https://slsa.dev/provenance/v0.1"
Important: Always use the above string for
predicateTyperather than what is in the URL bar. ThepredicateTypeURI will always resolve to the latest minor version of this specification. See parsing rules for more information.
Describe how an artifact or set of artifacts was produced.
This predicate is the recommended way to satisfy the SLSA provenance requirements.
Provenance is a claim that some entity (builder) produced one or more software
artifacts (Statement’s subject) by executing some recipe, using some other
artifacts as input (materials). The builder is trusted to have faithfully
recorded the provenance; there is no option but to trust the builder. However,
the builder may have performed this operation at the request of some external,
possibly untrusted entity. These untrusted parameters are captured in the
recipe’s entryPoint, arguments, and some of the materials. Finally, the
build may have depended on various environmental parameters (environment) that
are needed for reproducing the build but that are not under
external control.
See Example for a concrete example.
{
// Standard attestation fields:
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [{ ... }],
// Predicate:
“predicateType”: “https://slsa.dev/provenance/v0.1”,
“predicate”: {
“builder”: {
“id”: ”<URI>”
},
“recipe”: {
“type”: ”<URI>”,
“definedInMaterial”: /* integer /,
“entryPoint”: ”<STRING>”,
“arguments”: { / object / },
“environment”: { / object / }
},
“metadata”: {
“buildInvocationId”: ”<STRING>”,
“buildStartedOn”: ”<TIMESTAMP>”,
“buildFinishedOn”: ”<TIMESTAMP>”,
“completeness”: {
“arguments”: true/false,
“environment”: true/false,
“materials”: true/false
},
“reproducible”: true/false
},
“materials”: [
{
“uri”: ”<URI>”,
“digest”: { / DigestSet */ }
}
]
}
}
This predicate follows the in-toto attestation parsing rules. Summary:
predicateType URI includes the major version number and will always
change whenever there is a backwards incompatible change.predicateType.NOTE: This subsection describes the fields within predicate. For a description
of the other top-level fields, such as subject, see Statement.
Identifies the entity that executed the recipe, which is trusted to have correctly performed the operation and populated this provenance.
The identity MUST reflect the trust base that consumers care about. How detailed to be is a judgement call. For example, GitHub Actions supports both GitHub-hosted runners and self-hosted runners. The GitHub-hosted runner might be a single identity because, it’s all GitHub from the consumer’s perspective. Meanwhile, each self-hosted runner might have its own identity because not all runners are trusted by all consumers.
Consumers MUST accept only specific (signer, builder) pairs. For example, the “GitHub” can sign provenance for the “GitHub Actions” builder, and “Google” can sign provenance for the “Google Cloud Build” builder, but “GitHub” cannot sign for the “Google Cloud Build” builder.
Design rationale: The builder is distinct from the signer because one signer may generate attestations for more than one builder, as in the GitHub Actions example above. The field is required, even if it is implicit from the signer, to aid readability and debugging. It is an object to allow additional fields in the future, in case one URI is not sufficient.
builder.id string (TypeURI), required
URI indicating the builder’s identity.
Identifies the configuration used for the build. When combined with
materials, this SHOULD fully describe the build, such that re-running this recipe results in bit-for-bit identical output (if the build is reproducible).MAY be unset/null if unknown, but this is DISCOURAGED.
recipe.type string (TypeURI), required
URI indicating what type of recipe was performed. It determines the meaning of
recipe.entryPoint,recipe.arguments,recipe.environment, andmaterials.
recipe.definedInMaterial integer, optional
Index in
materialscontaining the recipe steps that are not implied byrecipe.type. For example, if the recipe type were “make”, then this would point to the source containing the Makefile, not themakeprogram itself.Omit this field (or use null) if the recipe doesn’t come from a material.
TODO: What if there is more than one material?
recipe.entryPoint string, optional
String identifying the entry point into the build. This is often a path to a configuration file and/or a target label within that file. The syntax and meaning are defined by
recipe.type. For example, if the recipe type were “make”, then this would reference the directory in which to runmakeas well as which target to use.Consumers SHOULD accept only specific
recipe.entryPointvalues. For example, a policy might only allow the “release” entry point but not the “debug” entry point.MAY be omitted if the recipe type specifies a default value.
Design rationale: The
entryPointis distinct fromargumentsto make it easier to write secure policies without having to parsearguments.
recipe.arguments object, optional
Collection of all external inputs that influenced the build on top of
recipe.definedInMaterialandrecipe.entryPoint. For example, if the recipe type were “make”, then this might be the flags passed tomakeaside from the target, which is captured inrecipe.entryPoint.Consumers SHOULD accept only “safe”
recipe.arguments. The simplest and safest way to achieve this is to disallow anyargumentsaltogether.This is an arbitrary JSON object with a schema is defined by
recipe.type.This is considered to be incomplete unless
metadata.completeness.argumentsis true. Unset or null is equivalent to empty.
recipe.environment object, optional
Any other builder-controlled inputs necessary for correctly evaluating the recipe. Usually only needed for reproducing the build but not evaluated as part of policy.
This SHOULD be minimized to only include things that are part of the public API, that cannot be recomputed from other values in the provenance, and that actually affect the evaluation of the recipe. For example, this might include variables that are referenced in the workflow definition, but it SHOULD NOT include a dump of all environment variables or include things like the hostname (assuming hostname is not part of the public API).
This is an arbitrary JSON object with a schema is defined by
recipe.type.This is considered to be incomplete unless
metadata.completeness.environmentis true. Unset or null is equivalent to empty.
Other properties of the build.
metadata.buildInvocationId string, optional
Identifies this particular build invocation, which can be useful for finding associated logs or other ad-hoc analysis. The exact meaning and format is defined by
builder.id; by default it is treated as opaque and case-sensitive. The value SHOULD be globally unique.
metadata.buildStartedOn string (Timestamp), optional
The timestamp of when the build started.
metadata.buildFinishedOn string (Timestamp), optional
The timestamp of when the build completed.
metadata.completeness object, optional
Indicates that the
builderclaims certain fields in this message to be complete.
metadata.completeness.arguments boolean, optional
If true, the
builderclaims thatrecipe.argumentsis complete, meaning that all external inputs are properly captured inrecipe.
metadata.completeness.environment boolean, optional
If true, the
builderclaims thatrecipe.environmentis claimed to be complete.
metadata.completeness.materials boolean, optional
If true, the
builderclaims thatmaterialsis complete, usually through some controls to prevent network access. Sometimes called “hermetic”.
metadata.reproducible boolean, optional
If true, the
builderclaims that runningrecipeonmaterialswill produce bit-for-bit identical output.
materials array of objects, optional
The collection of artifacts that influenced the build including sources, dependencies, build tools, base images, and so on.
This is considered to be incomplete unless
metadata.completeness.materialsis true. Unset or null is equivalent to empty.
materials[*].uri string (ResourceURI), optional
The method by which this artifact was referenced during the build.
TODO: Should we differentiate between the “referenced” URI and the “resolved” URI, e.g. “latest” vs “3.4.1”?
TODO: Should wrap in a
locatorobject to allow for extensibility, in case we add other types of URIs or other non-URI locators?
materials[*].digest object (DigestSet), optional
Collection of cryptographic digests for the contents of this artifact.
WARNING: This is just for demonstration purposes.
Suppose the builder downloaded example-1.2.3.tar.gz, extracted it, and ran
make -C src foo CFLAGS=-O3, resulting in a file with hash 5678.... Then the
provenance might look like this:
{
"_type": "https://in-toto.io/Statement/v0.1",
// Output file; name is "_" to indicate "not important".
"subject": [{"name": "_", "digest": {"sha256": "5678..."}}],
"predicateType": "https://slsa.dev/provenance/v0.1",
"predicate": {
"builder": { "id": "mailto:person@example.com" },
"recipe": {
"type": "https://example.com/Makefile",
"definedInMaterial": 0, // material containing the Makefile
"entryPoint": "src:foo", // target "foo" in directory "src"
"arguments": {"CFLAGS": "-O3"} // extra args to `make`
},
"materials": [{
"uri": "https://example.com/example-1.2.3.tar.gz",
"digest": {"sha256": "1234..."}
}]
}
}
WARNING: This is only for demonstration purposes. The GitHub Actions team has not yet reviewed or approved this design, and it is not yet implemented. Details are subject to change!
If GitHub is the one to generate provenance, and the runner is GitHub-hosted, then the builder would be as follows:
"builder": {
"id": "https://github.com/Attestations/GitHubHostedActions@v1"
}
Self-hosted runner: Not yet supported. We need to figure out a URI scheme that
represents what system hosted the runner, or perhaps add additional properties
in builder.
"recipe": {
// Build steps were defined in a GitHub Actions Workflow file ...
"type": "https://github.com/Attestations/GitHubActionsWorkflow@v1",
// ... in the git repo described by `materials[0]` ...
"definedInMaterial": 0,
// ... at the path .github/workflows/build.yaml, using the job "build".
"entryPoint": "build.yaml:build",
// The only possible user-defined parameters that can affect the build are the
// "inputs" to a workflow_dispatch event. This is unset/null for all other
// events.
"arguments": {
"inputs": { ... }
},
// Other variables that are required to reproduce the build and that cannot be
// recomputed using existing information. (Documentation would explain how to
// recompute the rest of the fields.)
"environment": {
// The architecture of the runner.
"arch": "amd64",
// Environment variables. These are always set because it is not possible
// to know whether they were referenced or not.
"env": {
"GITHUB_RUN_ID": "1234",
"GITHUB_RUN_NUMBER": "5678",
"GITHUB_EVENT_NAME": "push"
},
// The context values that were referenced in the workflow definition.
// Secrets are set to the empty string.
"context": {
"github": {
"run_id": "abcd1234"
},
"runner": {
"os": "Linux",
"temp": "/tmp/tmp.iizj8l0XhS",
}
}
}
}
"materials": [{
// The git repo that contains the build.yaml referenced above.
"uri": "git+https://github.com/foo/bar.git",
// The resolved git commit hash reflecting the version of the repo used
// for this build.
"digest": {"sha1": "abc..."}
}]
WARNING: This is only for demonstration purposes. The Google Cloud Build team has not yet reviewed or approved this design, and it is not yet implemented. Details are subject to change!
If Google is the one to generate provenance, and the worker is Google-hosted, then the builder would be as follows:
"builder": {
"id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v1"
}
Custom worker: Not yet supported. We need to figure out a URI scheme that
represents what system hosted the worker, or perhaps add additional properties
in builder.
Here entryPoint references the filename from the CloudBuild
BuildTrigger.
"recipe": {
// Build steps were defined in a cloudbuild.yaml file ...
"type": "https://cloudbuild.googleapis.com/CloudBuildYaml@v1",
// ... in the git repo described by `materials[0]` ...
"definedInMaterial": 0,
// ... at the path path/to/cloudbuild.yaml.
"entryPoint": "path/to/cloudbuild.yaml",
// The only possible user-defined parameters that can affect a BuildTrigger
// are the substitutions in the BuildTrigger.
"arguments": {
"substitutions": {...}
}
}
"materials": [{
// The git repo that contains the cloudbuild.yaml referenced above.
"uri": "git+https://source.developers.google.com/p/foo/r/bar",
// The resolved git commit hash reflecting the version of the repo used
// for this build.
"digest": {"sha1": "abc..."}
}]
Here we list the steps defined in a trigger or over RPC:
"recipe": {
// Build steps were provided as an argument. No `definedInMaterial` or
// `entryPoint`.
"type": "https://cloudbuild.googleapis.com/CloudBuildSteps@v1",
"arguments": {
// The steps that were performed. (Format TBD.)
"steps": [...],
// The substitutions in the build trigger.
"substitutions": {...}
// TODO: Any other arguments?
}
}
WARNING: This is just a proof-of-concept. It is not yet standardized.
Execution of arbitrary commands:
"recipe": {
// There was no entry point, and the commands were run in an ad-hoc fashion.
// There is no `definedInMaterial` or `entryPoint`.
"type": "https://example.com/ManuallyRunCommands@v1",
"arguments": {
// The list of commands that were executed.
"commands": [
"tar xvf foo-1.2.3.tar.gz",
"cd foo-1.2.3",
"./configure --enable-some-feature",
"make foo.zip"
],
// Indicates how to parse the strings in `commands`.
"shell": "bash"
}
}
metadata.buildInvocationId.Verification summary attestations communicate that an artifact has been verified at a specific SLSA level and details about that verification.
This document defines the following predicate type within the in-toto attestation framework:
"predicateType": "https://slsa.dev/verification_summary/v0.1"
Important: Always use the above string for
predicateTyperather than what is in the URL bar. ThepredicateTypeURI will always resolve to the latest minor version of this specification. See parsing rules for more information.
Describe what SLSA level an artifact or set of artifacts was verified at and other details about the verification process including what SLSA level the dependencies were verified at.
This allows software consumers to make a decision about the validity of an artifact without needing to have access to all of the attestations about the artifact or all of its transitive dependencies. They can use it to delegate complex policy decisions to some trusted party and then simply trust that party’s decision regarding the artifact.
It also allows software publishers to keep the details of their build pipeline confidential while still communicating that some verification has taken place. This might be necessary for legal reasons (keeping a software supplier confidential) or for security reasons (not revealing that an embargoed patch has been included).
A Verification Summary Attestation (VSA) is an attestation that some entity
(verifier) verified one or more software artifacts (the subject of an
in-toto attestation Statement) by evaluating the artifact and a bundle
of attestations against some policy. Users who trust the verifier may
assume that the artifacts met the indicated SLSA level without themselves
needing to evaluate the artifact or to have access to the attestations the
verifier used to make its determination.
The VSA also allows consumers to determine the verified levels of
all of an artifact’s transitive dependencies. The verifier does this by
either a) verifying the provenance of each non-source dependency listed in
the materials of the artifact
being verified (recursively) or b) matching the non-source dependency
listed in materials (by subject.digest == materials.digest and, ideally,
subject.name == materials.uri) to a VSA for that dependency and using
vsa.policy_level and vsa.dependency_levels. Policy verifiers wishing
to establish minimum requirements on dependencies SLSA levels may use
vsa.dependency_levels to do so.
// Standard attestation fields:
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [{
"name": <artifact-URI-in-request>,
"digest": { <digest-in-request> }
}],
// Predicate
“predicateType”: “https://slsa.dev/verification_summary/v0.1”,
“predicate”: {
// Required
“verifier”: {
“id”: ”<URI>”
},
“time_verified”: <TIMESTAMP>,
“policy”: {
“uri”: ”<URI>”,
“digest”: { /* DigestSet */ }
}
“verification_result”: ”<PASSED|FAILED>”,
“policy_level”: ”<SlsaResult>”,
“dependency_levels”: {
”<SlsaResult>”: <Int>,
”<SlsaResult>”: <Int>,
…
}
}
This predicate follows the in-toto attestation parsing rules. Summary:
predicateType URI includes the major version number and will always
change whenever there is a backwards incompatible change.predicateType.NOTE: This subsection describes the fields within predicate. For a description
of the other top-level fields, such as subject, see Statement.
Identifies the entity that performed the verification.
The identity MUST reflect the trust base that consumers care about. How detailed to be is a judgment call.
Consumers MUST accept only specific (signer, verifier) pairs. For example, “GitHub” can sign provenance for the “GitHub Actions” verifier, and “Google” can sign provenance for the “Google Cloud Deploy” verifier, but “GitHub” cannot sign for the “Google Cloud Deploy” verifier.
The field is required, even if it is implicit from the signer, to aid readability and debugging. It is an object to allow additional fields in the future, in case one URI is not sufficient.
verifier.id string (TypeURI), required
URI indicating the verifier’s identity.
time_verified string (Timestamp), required
Timestamp indicating what time the verification occurred.
Describes the policy that was used to verify this artifact.
policy.uri string (ResourceURI), required
The URI of the policy used to perform verification.
policy.digest object (DigestSet), optional
Collection of cryptographic digests for the contents of the policy used to perform verification.
verification_result string, required
Either “PASSED” or “FAILED” to indicate if the artifact passed or failed the policy verification.
policy_level string (SlsaResult), required
Indicates what SLSA level the artifact itself (and not its dependencies) was verified at or “FAILED” if policy verification failed.
dependency_levels object, required
A count of the dependencies at each SLSA level.
Map from SlsaResult to the number of the artifact’s transitive dependencies that were verified at the indicated level. Absence of a given level of SlsaResult MUST be interpreted as reporting 0 dependencies at that level.
WARNING: This is just for demonstration purposes.
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [{
"name": "https://example.com/example-1.2.3.tar.gz",
"digest": {"sha256": "5678..."}
}],
// Predicate
“predicateType”: “https://slsa.dev/verification_summary/v0.1”,
“predicate”: {
“verifier”: {
“id”: “https://example.com/publication_verifier”
},
“time_verified”: “1985-04-12T23:20:50.52Z”,
“policy”: {
“uri”: “https://example.com/example_tarball.policy”,
“digest”: {“sha256”: “1234…”}
},
“verification_result”: “PASSED”,
“policy_level”: “SLSA_LEVEL_3”,
“dependency_levels”: {
“SLSA_LEVEL_4”: 1,
“SLSA_LEVEL_3”: 5,
“SLSA_LEVEL_2”: 7,
“SLSA_LEVEL_1”: 1,
}
}
The result of evaluating an artifact (or set of artifacts) against SLSA. Must be one of these values: