{"course":{"slug":"how-software-changed","title":"How Software Development Changed","url":"https://addaly.com/learn/how-software-changed","level":"intermediate","track":"work"},"lesson":{"slug":"provenance-and-inventory","title":"Knowing what you actually shipped","url":"https://addaly.com/learn/how-software-changed/provenance-and-inventory","position":75,"of":87,"minutes":9,"answer":"Component inventory and pinning are solvable and pay off on the morning of an advisory.","keyIdea":"Component inventory and pinning are solvable and pay off on the morning of an advisory, while identifying which lines were generated is not solvable at all: watermarks do not survive code's constraints and detectors misfire on clean, conventional writing."},"body":{"format":"markdown","text":"## Two provenance questions, one answerable\n\nWhen something goes wrong, you want to know where the code came from. That splits into two questions with very different answers.\n\n**Which third-party components are in this artefact, at which exact versions?** Answerable, mechanically, today.\n\n**Which lines were generated, and from what?** Not answerable, and unlikely to become so. Take the second one off the table first, so that nobody spends a quarter building a system that cannot work.\n\n## Why generated-code provenance is not a real control\n\nThere is no watermark in code. A watermark relies on steering a choice among many equivalent outputs, and code has hard constraints (it must compile, it must pass tests) that strip the redundancy the signal would live in. Reformatting or renaming a variable destroys whatever was left.\n\nDetectors that claim to identify generated code do not work reliably, and their failures are not random: they misfire on clean, conventional, well-commented code, which describes the work of careful engineers and of people writing in a second language. Using one to police contributions punishes the wrong people.\n\nSo if you need a record of what was generated, the only honest mechanism is a declaration by the author, in the commit or the pull request. That is a process, maintained by cooperation, and it is what several organisations have adopted. It is weak. It is also the only thing on offer.\n\n## The inventory question, which is solvable\n\nA software bill of materials is a list of everything in your artefact with versions and licences. Two formats are in wide use: SPDX and CycloneDX.\n\n```bash\nsyft dir:. -o cyclonedx-json > sbom.json   # produce the inventory\ngrype sbom:sbom.json                        # check it against advisories\n```\n\nBoth tools are free and open source. Generating one takes seconds and can run on every build.\n\nWhy it matters more now: dependencies get added per unit of *human attention*, and human attention did not increase. When a serious advisory lands, the pattern established by Log4Shell, the only question that matters in the first hour is whether you are affected, and the difference between answering in ten minutes and three days is entirely whether you kept an inventory.\n\n## Pinning, which is the part people skip\n\nAn inventory of what you *intended* to install is worth little if the build resolves something else.\n\n- Lockfiles committed, and `npm ci` rather than `npm install` in CI.\n- `pip install --require-hashes -r requirements.txt`, which refuses anything whose hash differs.\n- Container images referenced by digest (`sha256:...`), not by a mutable tag. `:latest` means \"whatever was pushed most recently by anyone who can push\".\n- Actions and CI steps pinned to a commit, not a moving tag. A compromised tag on a popular action reaches thousands of pipelines in minutes, which has happened.\n\n## Signing and build provenance\n\nThe layer above inventory answers: was this artefact built from the source it claims, by the pipeline it claims?\n\n`cosign`, part of the Sigstore project, signs artefacts using short-lived certificates tied to an identity, so there is no long-lived private key to steal. SLSA is a framework of levels describing how tamper-resistant a build is, from \"there are build records\" up to \"builds are hermetic and independently verifiable\". Major CI providers can now emit signed provenance attestations for what they built.\n\nThe practical starting point for a small team is modest and still worth doing: sign your container images, verify signatures at deploy, and keep the build logs.\n\n## The honest limitations\n\n**An SBOM is an inventory, not an assessment.** It tells you what is present. It says nothing about whether it is safe, correctly configured, or actually reachable in your code path. Most organisations that produce SBOMs never query them, and an unqueried inventory is a compliance artefact rather than a control.\n\n**Inventories are incomplete in practice.** Vendored source copied into the repository, statically linked binaries, a script fetched by `curl` in a Dockerfile: none of these reliably appear.\n\n**Signing proves origin, not quality.** A signed artefact built from compromised source is a correctly signed compromised artefact. The signature narrows *who could have produced this*, which is useful and much narrower than *this is safe*.\n\n> The purpose of all of this is to make one question answerable quickly on a bad morning: what is in the thing we are running. It is not a security control. It is the thing that makes security controls actionable."},"check":{"question":"Why is watermarking generated code not a viable provenance mechanism, when watermarking generated prose is at least plausible?","options":[{"text":"Code is shorter than prose on average, so there is too little text to carry a detectable statistical signal.","correct":false,"tempting":"Length is not the barrier; long generated files have the same problem as short ones."},{"text":"Watermarks need redundancy among equivalent outputs, and compiling code has none to spare.","correct":true},{"text":"Code is usually stored in version control, which normalises whitespace and strips any embedded signal.","correct":false,"tempting":"Version control preserves bytes exactly; the erasure comes from ordinary editing, not from storage."},{"text":"Watermarking requires provider cooperation, and open-weight models can be run locally without it.","correct":false,"tempting":"True of prose watermarking too, yet that is described here as at least plausible."}]},"prev":{"slug":"threat-modelling-a-change","title":"Four questions before you merge","url":"https://addaly.com/learn/how-software-changed/threat-modelling-a-change"},"next":{"slug":"licences-and-familiar-code","title":"When the output looks like something else","url":"https://addaly.com/learn/how-software-changed/licences-and-familiar-code"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Knowing what you actually shipped', in How Software Development Changed. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/how-software-changed/provenance-and-inventory (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Knowing what you actually shipped","courseTitle":"How Software Development Changed","module":{"index":8,"slug":"provenance-security-and-law","title":"Provenance, security and the unsettled law"},"answer":null,"keyIdea":"Component inventory and pinning are solvable and pay off on the morning of an advisory, while identifying which lines were generated is not solvable at all: watermarks do not survive code’s constraints and detectors misfire on clean, conventional writing.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"two-provenance-questions-one-answerable","runs":[{"text":"Two provenance questions, one answerable"}]},{"t":"p","runs":[{"text":"When something goes wrong, you want to know where the code came from. That splits into two questions with very different answers."}]},{"t":"p","runs":[{"text":"Which third-party components are in this artefact, at which exact versions?","b":true},{"text":" Answerable, mechanically, today."}]},{"t":"p","runs":[{"text":"Which lines were generated, and from what?","b":true},{"text":" Not answerable, and unlikely to become so. Take the second one off the table first, so that nobody spends a quarter building a system that cannot work."}]},{"t":"h2","id":"why-generated-code-provenance-is-not-a-real-control","runs":[{"text":"Why generated-code provenance is not a real control"}]},{"t":"p","runs":[{"text":"There is no watermark in code. A watermark relies on steering a choice among many equivalent outputs, and code has hard constraints (it must compile, it must pass tests) that strip the redundancy the signal would live in. Reformatting or renaming a variable destroys whatever was left."}]},{"t":"p","runs":[{"text":"Detectors that claim to identify generated code do not work reliably, and their failures are not random: they misfire on clean, conventional, well-commented code, which describes the work of careful engineers and of people writing in a second language. Using one to police contributions punishes the wrong people."}]},{"t":"p","runs":[{"text":"So if you need a record of what was generated, the only honest mechanism is a declaration by the author, in the commit or the pull request. That is a process, maintained by cooperation, and it is what several organisations have adopted. It is weak. It is also the only thing on offer."}]},{"t":"h2","id":"the-inventory-question-which-is-solvable","runs":[{"text":"The inventory question, which is solvable"}]},{"t":"p","runs":[{"text":"A software bill of materials is a list of everything in your artefact with versions and licences. Two formats are in wide use: SPDX and CycloneDX."}]},{"t":"code","lang":"bash","label":"Bash","text":"syft dir:. -o cyclonedx-json > sbom.json   # produce the inventory\ngrype sbom:sbom.json                        # check it against advisories"},{"t":"p","runs":[{"text":"Both tools are free and open source. Generating one takes seconds and can run on every build."}]},{"t":"p","runs":[{"text":"Why it matters more now: dependencies get added per unit of "},{"text":"human attention","i":true},{"text":", and human attention did not increase. When a serious advisory lands, the pattern established by Log4Shell, the only question that matters in the first hour is whether you are affected, and the difference between answering in ten minutes and three days is entirely whether you kept an inventory."}]},{"t":"h2","id":"pinning-which-is-the-part-people-skip","runs":[{"text":"Pinning, which is the part people skip"}]},{"t":"p","runs":[{"text":"An inventory of what you "},{"text":"intended","i":true},{"text":" to install is worth little if the build resolves something else."}]},{"t":"ul","items":[[{"text":"Lockfiles committed, and "},{"text":"npm ci","code":true},{"text":" rather than "},{"text":"npm install","code":true},{"text":" in CI."}],[{"text":"pip install --require-hashes -r requirements.txt","code":true},{"text":", which refuses anything whose hash differs."}],[{"text":"Container images referenced by digest ("},{"text":"sha256:...","code":true},{"text":"), not by a mutable tag. "},{"text":":latest","code":true},{"text":" means “whatever was pushed most recently by anyone who can push”."}],[{"text":"Actions and CI steps pinned to a commit, not a moving tag. A compromised tag on a popular action reaches thousands of pipelines in minutes, which has happened."}]]},{"t":"h2","id":"signing-and-build-provenance","runs":[{"text":"Signing and build provenance"}]},{"t":"p","runs":[{"text":"The layer above inventory answers: was this artefact built from the source it claims, by the pipeline it claims?"}]},{"t":"p","runs":[{"text":"cosign","code":true},{"text":", part of the Sigstore project, signs artefacts using short-lived certificates tied to an identity, so there is no long-lived private key to steal. SLSA is a framework of levels describing how tamper-resistant a build is, from “there are build records” up to “builds are hermetic and independently verifiable”. Major CI providers can now emit signed provenance attestations for what they built."}]},{"t":"p","runs":[{"text":"The practical starting point for a small team is modest and still worth doing: sign your container images, verify signatures at deploy, and keep the build logs."}]},{"t":"h2","id":"the-honest-limitations","runs":[{"text":"The honest limitations"}],"role":"limit"},{"t":"p","runs":[{"text":"An SBOM is an inventory, not an assessment.","b":true},{"text":" It tells you what is present. It says nothing about whether it is safe, correctly configured, or actually reachable in your code path. Most organisations that produce SBOMs never query them, and an unqueried inventory is a compliance artefact rather than a control."}]},{"t":"p","runs":[{"text":"Inventories are incomplete in practice.","b":true},{"text":" Vendored source copied into the repository, statically linked binaries, a script fetched by "},{"text":"curl","code":true},{"text":" in a Dockerfile: none of these reliably appear."}]},{"t":"p","runs":[{"text":"Signing proves origin, not quality.","b":true},{"text":" A signed artefact built from compromised source is a correctly signed compromised artefact. The signature narrows "},{"text":"who could have produced this","i":true},{"text":", which is useful and much narrower than "},{"text":"this is safe","i":true},{"text":"."}]},{"t":"quote","runs":[{"text":"The purpose of all of this is to make one question answerable quickly on a bad morning: what is in the thing we are running. It is not a security control. It is the thing that makes security controls actionable."}]}],"check":{"question":[{"text":"Why is watermarking generated code not a viable provenance mechanism, when watermarking generated prose is at least plausible?"}],"options":[{"text":[{"text":"Code is shorter than prose on average, so there is too little text to carry a detectable statistical signal."}],"correct":false,"tempting":[{"text":"Length is not the barrier; long generated files have the same problem as short ones."}]},{"text":[{"text":"Watermarks need redundancy among equivalent outputs, and compiling code has none to spare."}],"correct":true,"tempting":null},{"text":[{"text":"Code is usually stored in version control, which normalises whitespace and strips any embedded signal."}],"correct":false,"tempting":[{"text":"Version control preserves bytes exactly; the erasure comes from ordinary editing, not from storage."}]},{"text":[{"text":"Watermarking requires provider cooperation, and open-weight models can be run locally without it."}],"correct":false,"tempting":[{"text":"True of prose watermarking too, yet that is described here as at least plausible."}]}],"order":[0,1,3,2],"why":null},"prev":{"slug":"threat-modelling-a-change","title":"Four questions before you merge","minutes":8},"next":{"slug":"licences-and-familiar-code","title":"When the output looks like something else","minutes":9}}}