skip to content

Why can a dependency's git history look clean while its published release tarball is malicious?

level: seniorimportance: should knowfreq 43%

answer

  1. publishing is a separate act
  2. reviewed source is not the shipped bytes
  3. release archives legitimately differ from the tag
  4. nobody diffs the upload against the commit
  5. provenance binds artifact digest to source

basics

~20 s

Because publishing is a separate act from committing. Whoever holds release rights builds and uploads the artifact themselves, and nobody diffs it against the tag, so the shipped release can contain material that never appeared in the reviewed source.

solid answer

~50 s

The reviewed source and the consumed artifact are two different objects, connected only by a maintainer's promise. Whoever holds release rights builds the distributable on their own machine — often adding generated files, packaging scripts and vendored content that legitimately do not live in the repository — signs it and uploads it. Nothing in that flow compares the uploaded bytes to the tagged commit, so a compromised or malicious releaser can ship a tarball whose contents diverge from a git tree that reviews perfectly. An audit of the repository is therefore not an audit of what you installed. Closing the gap needs the *how it came to be* half of the chain: build from the tagged source in a logged, hosted builder that emits provenance binding the artifact digest to that commit, verify it at consumption, and make the build reproducible so a third party can rebuild and contradict a lie.

code

json · 18 lines
json
{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    {
      "name": "libexample-1.4.2.tar.gz",
      "digest": { "sha256": "9f2c...e10b" }
    }
  ],
  "predicateType": "https://slsa.dev/provenance/v1",
  "predicate": {
    "buildDefinition": {
      "externalParameters": {
        "repository": "https://example.org/libexample",
        "ref": "refs/tags/v1.4.2"
      }
    }
  }
}

go deeper

for a junior

Remember that what you download is not automatically the same as what is in the project's repository — the release is built and uploaded as a separate step.

for a middle

Explain why archives legitimately contain generated and packaging files absent from the tree, and why that normality hides an inserted change.

for a senior

Show the control ladder and keep the three claims apart: signature is who, SBOM is what, provenance is how — and only provenance, verified at consumption, binds artifact to source.

for a principal

Argue where the organisation spends: mandating verified provenance on ingested third-party artifacts versus building critical dependencies from source yourself, and who absorbs the cost of each.

## Two artifacts, one assumed equality Every ecosystem has a **reviewed object** — the source in version control, with its history, its pull requests and its tags — and a **consumed object**: the tarball, archive or package that people actually download and build against. Almost all reasoning about upstream trust is done on the first and applied to the second. The link between them is usually nothing more than a maintainer's assertion that one was produced from the other. That gap is where a maintainer takeover pays off best, because it is unreviewed by construction. Commits get read; releases get uploaded. ## Why the two legitimately differ It would be easier if the release were simply an archive of the tag, but in many ecosystems it is not, for honest reasons: - **Generated files.** Build-system scaffolding, parsers, configure scripts and similar outputs are produced at packaging time so consumers do not need the generators installed. They exist only in the release. - **Packaging metadata.** Manifests, descriptors and index files may be assembled during the release step. - **Vendored or fetched content.** Some releases bundle dependencies or assets that the repository references rather than stores. - **Pruning.** Tests, fixtures and development tooling are often stripped from the distributable. So a release that is not byte-identical to the tag is normal, and "there are extra files in here" is not by itself suspicious. That normality is the cover: an extra line inside a large generated file, in an archive nobody expects to match the repository, is not a thing reviewers look at — and often nobody has the diff in front of them at all. ## Who realistically finds it Whoever rebuilds. Downstream packagers who compile from the version-control tag rather than the upstream archive, and anyone who rebuilds a release independently and compares the result, are the parties positioned to notice a divergence — because they are the only ones who ever produce a second copy to compare against. That is a slow, partial detection path, and it is not available to a consumer who simply installs the published artifact. ## Keeping the three claims straight This is the question where candidates most often mix up the chain's three statements, so state them explicitly: | Claim | What it asserts | Does it close this gap? | |---|---|---| | **Signature** | *Who* vouches for this artifact | No — a malicious releaser signs with the real identity | | **SBOM** | *What* components are inside the artifact | No — a tampered release can ship a truthful inventory of itself | | **Provenance** | *How* the artifact came to be: from what source, by what builder | Yes — this is the claim that binds artifact to commit | An SBOM is frequently offered as the answer here and it is the wrong one. An inventory of what is inside a package does not assert that the package was built from reviewed source; if the malicious content was compiled into a component the SBOM lists, the inventory is accurate and useless for this purpose. ## The control ladder 1. **Build the release where it can be observed.** Move the packaging step out of a maintainer's laptop and into a hosted, logged build triggered from the tagged source, so the release stops being an unobserved private act. 2. **Emit provenance.** The build produces a signed statement naming the artifact by digest as its subject and the source repository and commit as its inputs. Now the assertion "this came from that tag" is machine-checkable instead of assumed. 3. **Verify it as a consumer, at the point of consumption.** Check that the provenance's subject digest is the digest of the artifact you actually fetched, that the source it names is the repository you meant, and that the builder identity is one you accept. Provenance that is generated but never verified changes nothing. 4. **Make the build reproducible.** If independent rebuilds from the same source produce the same artifact, any third party can contradict a lying release, which converts trust in the releaser into a verifiable property. 5. **Prefer the source of truth where you can.** Building from the version-control tag rather than the published archive removes the gap rather than instrumenting it — the cost is that you now own the generation steps the release used to do for you. ## What to say in an interview The sentence that shows you understand the class is: *the repository is not the artifact, and reviewing one tells you nothing authoritative about the other unless something binds them.* Then name the binder — provenance, verified at consumption, ideally backed by reproducibility — rather than reaching for signatures or inventories, which answer different questions.

  • What would you actually check before trusting an upstream release archive?
    Prefer the version-control tag if you can build from it. If you must consume the archive, require provenance whose subject digest equals the digest of the file you fetched and whose named source is the tag you expected, verify it at the point of consumption rather than at publish, and account for every file present in the archive but absent from the tree.
  • Why doesn't an SBOM for the release close this gap?
    An SBOM answers what components are inside the artifact. It makes no claim about where the artifact came from or whether its contents correspond to reviewed source. A tampered release can carry a completely accurate SBOM of its own tampered contents, so the document verifies while the artifact lies.
  • How does reproducibility change the trust model here?
    It moves the claim from something you must believe to something anyone can test. If the same source reliably yields the same artifact, an independent rebuilder can produce a contradicting digest, so a lying release becomes detectable by parties with no access to the maintainer's account or machine.
  • Does this attack require the maintainer to be malicious?
    No. It requires whoever performs the release to be compromised, coerced or replaced — a stolen publish credential, a compromised release machine, or a co-maintainer who was granted release rights. The gap exists because the release step is unobserved, regardless of who is standing in it.

Reviewing the repository and installing the tarball is like inspecting a restaurant's kitchen and then eating a meal delivered from an unmarked van. The inspection was real; it just does not cover what arrived.

saying these in an interview costs you the question

  • Says the SBOM would have revealed the tampering
  • Assumes the release archive always matches the git tag
  • Offers a signature as proof of build origin
  • Thinks reviewing upstream commits audits what you installed
  • Generates provenance but never verifies it downstream

context