# Supply Chain Attacks and the SLSA Framework

## Why this matters

Track 11 module 01 taught you to *scan* an image and to hold a mental model:
"scanning = is it safe, signing = is it authentic." That module deliberately
stopped at the preview. This track picks up exactly there and goes the whole
distance. But before any tool, you need the threat model that makes signing,
SBOMs, and provenance worth the effort: a supply-chain attack doesn't break
into your running container — it corrupts the *artifact* or the *process that
built it*, so the malicious thing is already inside the image you scanned,
signed, and trusted. SLSA is the industry's maturity model for defending
against exactly that, and it frames every remaining module in this track.

## Concepts

### What a supply-chain attack actually is

Track 11 module 00's STRIDE model had a **Tampering** category, and module 01
said signing addresses it — but *where* does the tampering happen? A
supply-chain attack targets one of the steps *before* your image ever runs:

- **A compromised dependency.** You `pip install`/`npm install` a package that
  a maintainer (or an attacker who took over the maintainer's account) laced
  with malicious code. The most famous real cases — `event-stream`,
  `xz-utils`/CVE-2024-3094 — shipped through the normal, trusted update path.
  Your build pulled it in automatically. Nothing you wrote is malicious; your
  *bill of materials* is.
- **A poisoned base image.** Your Dockerfile says `FROM someorg/base:1.4`. If
  that tag is repointed (or was malicious from the start), every image you
  build inherits the payload. This is the base-image hygiene lesson from track
  11 module 01, now framed as an *attack*, not just a CVE-count nuisance.
- **A tampered build step.** An attacker who can influence your CI — a
  malicious GitHub Action, a compromised runner, a modified build script —
  injects code *during* the build, so the image you ship differs from the
  source you reviewed. SolarWinds is the canonical example: the source was
  clean, the *build* was subverted.

The through-line: in every case the resulting image can pass a vulnerability
scan clean, because the malicious code isn't a *known, published* CVE — it's a
freshly-planted payload. Scanning (track 11) cannot catch this. That's the gap
this track closes.

### Why scanning is necessary but not sufficient

Track 11 module 01 was explicit that a clean scan tells you nothing about
tampering, zero-days, or your own logic bugs. Reframe that here as the precise
reason this track exists:

- Scanning answers **"does this image contain known-vulnerable packages?"**
- Supply-chain security answers **"is this the exact artifact my trusted
  pipeline produced from source I reviewed, built by a process I trust, with a
  known and attested set of contents?"**

Those are orthogonal. A validly signed image can still be vulnerable; a
freshly-scanned image can still be a tampered impostor or built from a poisoned
dependency. You need *both* axes, and this track is the second axis in full:
**provenance and integrity**.

### The three questions this track answers

Every remaining module maps to one of three questions an image should be able
to answer for itself:

1. **What is in it?** — the **SBOM** (module 01). A cryptographically
   attestable inventory, going far past the throwaway `trivy image --format
   cyclonedx` you generated in track 11.
2. **Is it authentic and unmodified?** — the **signature** (modules 02, 05).
   Cosign/Sigstore signing plus admission control that *refuses to run
   anything unsigned*.
3. **How was it built?** — the **provenance attestation** (module 03).
   A signed statement of *which* CI job built it, from *which* commit, with
   *which* builder — so a subverted build is detectable.

Hold these three questions; they're the spine of the whole track.

### SLSA: a maturity model, not a product

**SLSA** (Supply-chain Levels for Software Artifacts, pronounced "salsa") is an
OpenSSF framework that grades *how trustworthy an artifact's build process is*.
It's a maturity ladder, exactly like the way track 11 layered controls — you
don't jump to the top, you climb. The current levels (v1.0) are about
**build integrity** specifically:

- **Level 0** — no guarantees. A build with no provenance at all. Most images
  in the world.
- **Level 1** — provenance *exists*. The build produces a provenance document
  describing how it was made. It's not tamper-proof yet, but it's there and
  consumers can inspect it. This is roughly "you generate a provenance
  attestation" (module 03).
- **Level 2** — provenance is *signed* and generated by a hosted build service,
  so it can't be forged by whoever ran the build. This is "your CI platform
  signs the provenance for you" — GitHub Actions' generator, for example.
- **Level 3** — the build runs in a *hardened, isolated* environment where even
  the build's own scripts can't tamper with the provenance (non-falsifiable
  provenance, isolated build). This is the SolarWinds-resistant tier.

The point of naming the levels: they give you a *target* and a shared
vocabulary. "We're at SLSA 2 and want to reach 3" is a concrete, auditable
goal. Each level constrains a specific class of attack from the first concept
above — provenance (L1) makes a tampered build *visible*, signed provenance
(L2) makes it *unforgeable*, isolation (L3) stops the build from lying about
itself.

### Where SLSA meets the rest of this track

SLSA is the *why*; cosign, Syft, and admission control are the *how*. The map:

- Reaching **L1/L2** is modules 01–03 (SBOM, signing, provenance attestation)
  wired into the track-10 pipeline you already know.
- **Enforcing** that consumers only run L2+ artifacts is module 05 — the same
  Gatekeeper admission-control mechanism from track 11 module 04, now checking
  for a valid signature and attestation instead of a label.
- Keeping the whole thing *honest over time* — dependencies drift, CVEs land in
  already-signed images — is modules 04 (ongoing vuln management), 06
  (dependency/base hygiene at scale), and 07 (what to do when a signed image
  turns out to be compromised anyway).

You are, in effect, building a SLSA-graded pipeline across this track and then
enforcing it at the cluster door.

## Command reference

This module is conceptual; the tools arrive in module 01. These are the
inspection commands that let you *see* the supply-chain surface you're about to
defend.

| Command | What it does | Example |
|---|---|---|
| `docker history <image>` | Shows each layer/instruction, exposing base image + build steps | `docker history myapp:1.0` |
| `docker inspect <image>` | Full image metadata incl. config, env, entrypoint | `docker inspect myapp:1.0` |
| `docker buildx imagetools inspect <ref>` | Shows an image's manifest, incl. any attached attestations/SBOMs | `docker buildx imagetools inspect myreg.azurecr.io/app:1.0` |
| `crane manifest <ref>` | Prints the raw OCI manifest of a remote image (no pull) | `crane manifest myreg.azurecr.io/app:1.0` |
| `crane digest <ref>` | Resolves a tag to its immutable content digest | `crane digest myapp:1.0` |

Flag/field breakdown for `docker buildx imagetools inspect myreg.azurecr.io/app:1.0 --format '{{json .}}'`:

- `buildx imagetools inspect` — reads the image's manifest *from the registry*
  without pulling the whole image; the tool this track uses to see whether an
  image has SBOM/provenance attachments.
- `<ref>` — the image reference. Prefer a **digest** (`app@sha256:...`) over a
  **tag** (`app:1.0`) whenever integrity matters: a tag is mutable (the
  poisoned-base-image attack), a digest is content-addressed and cannot be
  silently repointed.
- `--format '{{json .}}'` — emit machine-readable JSON so you can grep for
  `SLSA`/`SPDX`/`in-toto` attestation predicate types (you'll do this for real
  in module 03).

## Hands-on exercises

No new tools yet — these build the *threat intuition* using tools you already
have from tracks 02/11, so the defenses in later modules land on something
concrete.

1. **(WSL2) See a base image you're inheriting blind.** Pull an image and read
   its ancestry:
   ```bash
   docker pull python:3.12-slim
   docker history python:3.12-slim --no-trunc | head -20
   ```
   Every one of those layers is code you didn't write and are trusting. Note
   you have *no* way, from this output alone, to know *who* built them or
   *whether* they match any published source — that missing information is
   exactly what provenance (module 03) supplies.

2. **(WSL2) Prove a tag is mutable but a digest isn't.** Resolve a tag to its
   digest, then reason about the difference:
   ```bash
   docker pull nginx:1.27
   docker inspect --format '{{index .RepoDigests 0}}' nginx:1.27
   ```
   Expect something like `nginx@sha256:...`. The tag `1.27` can be repointed by
   whoever controls the repo; the `sha256:` digest is the content itself and
   cannot change without changing the digest. Write one sentence on why
   `FROM nginx:1.27` is a weaker supply-chain statement than
   `FROM nginx@sha256:<digest>` (this is the pinning lesson module 06 revisits).

3. **(WSL2) Map a real attack to a SLSA level.** For each of the three attack
   types in Concepts (compromised dependency, poisoned base, tampered build),
   write down: *which SLSA level's guarantee would have made it detectable, and
   why.* (Hint: a tampered build is caught by provenance existing at all — L1 —
   and made unforgeable at L2; a poisoned base is a dependency-hygiene problem
   more than a build-integrity one.)

4. **(WSL2) Inventory your trust surface for one image.** Take any image you
   built in track 02 (or pull one), and list on paper every distinct *source of
   code* in it: base image, each OS package layer, each language dependency
   from your lockfile, your own code. This list *is* the SBOM you'll generate
   mechanically in module 01 — doing it by hand once shows you why an automated,
   attestable SBOM matters.

5. **(WSL2) Find whether an image already ships provenance.** Pick a
   well-known image built by a modern pipeline and inspect for attestations:
   ```bash
   docker buildx imagetools inspect docker.io/library/alpine:latest --format '{{json .}}' | grep -io 'provenance\|sbom\|slsa\|in-toto' | sort -u
   ```
   Some images will show attestation references, some won't. The presence or
   absence *is the SLSA-level signal* — an image with no provenance is Level 0
   by definition, no matter how clean its scan.

6. **(WSL2) Reason about the "clean scan, still compromised" case.** Re-run a
   scan from track 11 on a current base:
   ```bash
   trivy image --severity HIGH,CRITICAL --ignore-unfixed python:3.12-slim
   ```
   Now argue in two sentences: if an attacker had planted a backdoor in one of
   these packages *before* it was published (a supply-chain compromise, not a
   known CVE), would this scan show it? Why not? This is the exact gap modules
   02–03 close.

7. **Diagnose: which control stops which attack.** You're handed three
   incidents: (a) an image whose base tag was silently repointed to a malicious
   layer overnight; (b) a dependency update that added a credential-stealing
   payload; (c) a CI runner that was compromised and injected a miner into the
   build. For each, name — from this track's roadmap — the *primary* control
   that would have prevented or detected it (digest-pinning + admission
   signature check; SBOM + dependency review + continuous scanning; signed
   provenance from a trusted builder, SLSA L2+). There's overlap; justify your
   primary choice.

## Independent challenge

No commands given. Drawing on track 11 module 00's threat-modeling method and
this module's SLSA ladder, write a one-page **supply-chain threat model** for a
single service you've built earlier in this curriculum (reuse the track 02 or
track 10 app). Enumerate every point between "a developer types code" and "a
Pod runs the image" where an attacker could inject something — source, each
dependency source, the base image, the CI system, the registry, the deployment
step. For each injection point, state the STRIDE category (module 00), the SLSA
level or specific control from this track's roadmap that would defend it, and
whether that defense is *preventive* (blocks it) or *detective* (reveals it
after the fact). Finish by naming your current honest SLSA level for that
service today and the single highest-leverage step to raise it one level.

<details>
<summary>Stuck? One hint</summary>

Draw the pipeline as a left-to-right line: `source → deps → base image → build
(CI) → image → registry → admission → running Pod`. Put an arrow *into* the
line at each point an attacker could inject, and label each arrow with a STRIDE
category from module 00. Then, under the line, write the control from this
track's roadmap that guards that arrow: pinning/digests and SBOM review guard
the dep/base arrows; signed provenance from a hosted builder (SLSA L2) guards
the build arrow; a signature-checking admission policy (module 05) guards the
admission arrow. Your "current level" is honestly Level 0 until you're
generating provenance at all.

</details>

## Common mistakes & troubleshooting

- **Thinking a passing scan means "supply-chain secure."** Track 11's warning,
  sharpened: scanning finds *known, published* CVEs. A supply-chain attack
  plants code that is not yet a CVE, so it scans clean. Different axis, both
  required.
- **Treating SLSA as a product to install.** SLSA is a *maturity model* and a
  vocabulary for build-integrity guarantees. You reach levels by adopting
  practices (provenance, signing, isolated builds) — there's nothing named
  "SLSA" to `apt install`.
- **Confusing the SBOM (contents) with provenance (build process).** "What's in
  it" and "how it was made" are different attestations answering different
  questions. Module 01 does the first, module 03 the second.
- **Pinning base images by tag and calling it pinned.** A tag is mutable; only a
  `sha256:` digest is truly pinned. "Pinned to `:1.4`" still lets the
  poisoned-base attack through if the tag is repointed (module 06).
- **Assuming the attack targets the running container.** Supply-chain attacks
  target the *artifact and the build*, so the malicious code is already inside
  the trusted image before it ever runs — runtime defenses (track 11) are too
  late for this class.
- **Aiming straight for SLSA 3.** Like every rollout in track 11, you climb:
  get provenance existing (L1), then signed by a hosted builder (L2), then
  isolated (L3). Skipping levels usually means none of them are real.

## Checkpoint quiz

Write down your answer to each question before expanding it — checking without attempting first is the single easiest way to fool yourself into thinking you've learned this.

<details>
<summary>Show questions</summary>

1. Name the three canonical shapes of a supply-chain attack from this module,
   with a one-line example of each.
2. Why can an image that passes a Trivy scan clean still be the product of a
   supply-chain compromise?
3. State the three questions an image should be able to answer for itself, and
   which artifact answers each.
4. What does SLSA measure, and is it a tool you install? Explain in one
   sentence.
5. Describe SLSA Levels 1, 2, and 3 in terms of *provenance*, and which class of
   attack each additional level defends against.
6. Why is `FROM base@sha256:...` a stronger supply-chain statement than
   `FROM base:1.4`?
7. Which STRIDE category (track 11 module 00) does this entire track primarily
   address, and why doesn't scanning cover it?

</details>

<details>
<summary>Show answers</summary>

1. A **compromised dependency** (a malicious `npm`/`pip` package, e.g.
   `event-stream`/`xz-utils`); a **poisoned base image** (a repointed or
   malicious `FROM` base); and a **tampered build step** (code injected during
   CI, e.g. SolarWinds). All three land inside the image before it runs.
2. Because scanning detects only *known, published* CVEs by cross-referencing
   the SBOM against vulnerability feeds. A supply-chain compromise plants
   *new*, unpublished malicious code, which no feed lists yet — so the scan
   reports clean.
3. **What is in it?** → the SBOM (module 01). **Is it authentic/unmodified?** →
   the signature (modules 02/05). **How was it built?** → the provenance
   attestation (module 03).
4. SLSA measures the *integrity of an artifact's build process* on a maturity
   ladder (Levels 0–3); it is **not** a tool you install — it's a framework and
   vocabulary you reach by adopting practices like provenance and signing.
5. **L1:** provenance *exists* (build describes how it was made) — makes a
   tampered build *visible*. **L2:** provenance is *signed by a hosted build
   service* — makes it *unforgeable* by whoever ran the build. **L3:** the build
   is *isolated/hardened* so even its own scripts can't falsify provenance —
   defends against a subverted build (SolarWinds class).
6. Because a tag is mutable — whoever controls the repo can silently repoint
   `:1.4` to a malicious layer (the poisoned-base attack) — while a `sha256:`
   digest is content-addressed and cannot change without changing the digest,
   so it names the *exact* bytes you reviewed.
7. **Tampering** (integrity/provenance). Scanning only inspects *contents* for
   known CVEs; it cannot tell you whether the artifact is the authentic,
   unmodified output of a build process you trust — which is precisely what
   signing and provenance provide.

</details>

## Next

Continue to
[01-software-bills-of-materials-sboms](../01-software-bills-of-materials-sboms/README.md)
— start answering the first of the three questions ("what's *in* it?") by
generating a real, structured SBOM with Syft, going far past the throwaway
inventory you produced in track 11.
