Chapter 24 — Application and software supply chain security: methods I
Lesson map
Lesson 24 published. Map, example, EX070.
flowchart TD
N0["Learning outcomes"]
N1["24.1 Generating SBOMs"]
N2["24.2 Matching advisories"]
N3["Lab — Lab — SBOM against advisories"]
N4["24.3 Lab result"]
N5["24.4 Prioritising"]
N6["24.5 In CI"]
N7["Handling false matches"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX070"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["24.1 Generating SBOMs"]
N2["24.2 Matching advisories"]
N3["Lab — Lab — SBOM against advisories"]
N4["24.3 Lab result"]
N5["24.4 Prioritising"]
N6["24.5 In CI"]
N7["Handling false matches"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX070"]
[Learning outcomes]
|
v
[24.1 Generating SBOMs]
|
v
[24.2 Matching advisories]
|
v
[Lab — Lab — SBOM against advisories]
|
v
[24.3 Lab result]
|
v
[24.4 Prioritising]
|
v
[24.5 In CI]
|
v
[Handling false matches]
|
v
[Worked example] --> [Exercise EX070]
Learning outcomes
You will (1) generate and use an SBOM; (2) match components to advisories with version ranges; (3) prioritise affected components; (4) integrate the check into CI.
24.1 Generating SBOMs
CI generates an SBOM for every build from the lockfile and build metadata, in a standard format, stored with the artifact.
Generating the SBOM inside CI, from the same lockfile the build used, means it describes what was actually built rather than what someone remembers adding. A standard format lets the advisory check and other tools read it without custom parsing. Storing it beside the artifact, linked by digest, means that months later the team can answer exactly which components shipped in any release, even if the lockfile has changed since. An SBOM written by hand after release would drift from reality almost at once.
24.2 Matching advisories
Advisories list affected version ranges and fixed versions. Matching must compare versions numerically, not as text: "2.0.9" is lower than "2.0.10".
Text comparison works character by character, so when it reaches the third part it compares '9' with '1' and wrongly ranks the older release higher. Splitting on dots and comparing each part as a number gives the right order. Real version schemes add complications, such as pre-release labels with a beta suffix, so production tools use the ecosystem's own comparison rules. Ranges are usually written with an inclusive lower bound and an exclusive upper bound at the fixed version, which is how the lab treats them.
Lab — Lab — SBOM against advisories
"""Match SBOM components to synthetic advisories with affected version ranges."""
def v(s): return tuple(int(x) for x in s.split("."))
sbom = {"webfw": "4.1.2", "tmpl": "2.0.9", "zipx": "1.3.0", "decimal-x": "0.9.4", "imgkit": "5.2.1"}
advisories = [("ADV-101", "tmpl", "2.0.0", "2.0.10"), ("ADV-102", "zipx", "1.0.0", "1.2.5"),
("ADV-103", "imgkit", "5.0.0", "5.2.2"), ("ADV-104", "webfw", "3.0.0", "3.9.9")]
for aid, pkg, lo, fixed in advisories:
have = sbom.get(pkg)
hit = have and v(lo) <= v(have) < v(fixed)
print(f"{aid} {pkg:7} installed {have} affected [{lo}, {fixed}) -> {'AFFECTED, upgrade to ' + fixed if hit else 'not affected'}")
Run with python3 lab_sbom.py. Verified output:
ADV-101 tmpl installed 2.0.9 affected [2.0.0, 2.0.10) -> AFFECTED, upgrade to 2.0.10
ADV-102 zipx installed 1.3.0 affected [1.0.0, 1.2.5) -> not affected
ADV-103 imgkit installed 5.2.1 affected [5.0.0, 5.2.2) -> AFFECTED, upgrade to 5.2.2
ADV-104 webfw installed 4.1.2 affected [3.0.0, 3.9.9) -> not affected
24.3 Lab result
Two of four advisories apply: ADV-101 affects tmpl 2.0.9 (fixed in 2.0.10) and ADV-103 affects imgkit 5.2.1 (fixed in 5.2.2). zipx 1.3.0 is past its fixed version 1.2.5, and webfw 4.1.2 is outside ADV-104's range. Note that comparing "2.0.9" and "2.0.10" as text would wrongly suggest 2.0.9 is newer.
The lab's v() function turns each version into a tuple of integers, which Python compares part by part. A package missing from the SBOM would give None and count as not affected, so the SBOM's completeness directly limits what the check can see.
24.4 Prioritising
Not every affected component is equally urgent. Kwame considers severity, whether the vulnerable function is reachable in ticket-web, and the exploitation likelihood estimated by EPSS (Jacobs et al., 2021). imgkit is isolated (chapter 8), which reduces its urgency; tmpl renders every page, so it goes first.
EPSS gives a probability that a flaw will be exploited soon, which complements severity: a severe flaw nobody is exploiting may wait behind a moderate one under active attack. Reachability needs knowledge of the code, which the component owner provides.
24.5 In CI
The check runs on every build and nightly against the current release, because new advisories appear for unchanged code.
Running at build time catches advisories for newly added components before they ship. The nightly run against the current release covers the other case: code that was clean when built but has since had a flaw published. Without the nightly check, a release could sit in production for weeks with a known flaw until the next build happened to run. Results from both runs feed the same list, so owners see one queue. The nightly run uses the stored SBOM for the release, so it needs no rebuild and finishes in minutes.
Handling false matches
Sometimes an advisory names a component but the affected function is in an optional module Coastline does not install. The team records a short justification and marks the advisory not applicable, with a review date. These records prevent the same question being asked every night.
Reporting
Nightly results go to the team chat with links to each advisory and owner.
Worked example
The nightly run flags ADV-103 for imgkit two days after publication. The team upgrades within 6 days, inside the 30-day high-severity target, and documents that isolation limited the risk meanwhile.
Synthetic case. SBOM and advisories are synthetic.
Errors and limits
Advisory data can be late or wrong; SBOMs can miss vendored or copied code.
Exercises
- EX070 — Version comparison. Why compare versions numerically? Rubric: 3 = text order wrong: 2.0.9 vs 2.0.10; 2 = partial; 1 = vague; 0 = none.
- EX071 — Lab hits. Which advisories applied? Rubric: 3 = ADV-101 tmpl, ADV-103 imgkit; 2 = one; 1 = vague; 0 = none.
- EX072 — Priority reasoning. Why fix tmpl before imgkit? Rubric: 3 = tmpl renders every page; imgkit isolated; 2 = partial; 1 = vague; 0 = none.
Synthesis
SBOMs plus correct version matching and context-aware prioritisation make advisories actionable.
Continue studying — chapter 24 of 50
← Chapter 23 (draft) · Course chapter index · Chapter 25 (draft) →