skip to content

Supply Chain Attack Vectors

You will learn the concrete ways attackers inject themselves between a developer and production — poisoned packages, hijacked maintainers, compromised build servers — anchored in the named incidents everyone cites. Interviewers open with these cases to test whether you understand the threat model before the tooling.

on this pageshow

explore

questions

page 1 of 2

How can a build produce a backdoored artifact when every source file is clean?

level: juniorimportance: must knowfreq 64%

answer

  1. what you reviewed is not what you shipped
  2. review covers inputs, not the output
  3. anything that executes during the build
  4. plugins, rewriters, packaging, base image
  5. clean diff, dirty binary

basics

~20 s

Because the artifact is made by the build, not by the repository. Anything that runs during the build - a build script, a plugin, a build-time dependency, the compiler image, a packaging step - can add code that appears in no source file.

solid answer

~50 s

Review, branch protection and a green pull-request check all operate on the build's **inputs**. The thing you ship is the build's **output**, and between the two sits a whole execution environment: build scripts, build plugins, annotation processors and bytecode rewriters, packaging and shading steps, dependencies that exist only at build time, and the base image the build runs in. Any of those can add, replace or rewrite code on its way into the artifact. That is build-time injection, and it is deliberately chosen by attackers precisely because the repository stays clean: the diff is honest, the tests pass, and the release looks normal. It also means the investigation has to start from the artifact rather than from `git log` - you compare what shipped against what the declared source would produce, and you enumerate everything the build was allowed to execute.

go deeper

for a junior

Be ready to say plainly that the repository is an input and the artifact is an output, and to name three things that run during a build and could change the output.

for a middle

You are expected to walk the build step by step and point at every stage with write access to the artifact - plugins, post-compile rewriting, packaging, the runner image - and explain why each check stays green.

for a senior

Show that you would start an investigation from the published bytes rather than from the repository, and that you treat the builder and every secret it could read as compromised until proven otherwise.

for a principal

Own the framing that source-side controls cannot answer an artifact-side question, and be able to explain to a customer or auditor what your organisation can actually evidence about a release beyond "we reviewed the code".

## The repository is not the artifact Almost every control an engineering organisation is proud of operates on **inputs**: pull-request review, required approvals, protected branches, signed commits, a green check on the PR. All of them answer the question "is the source we intend to build acceptable?" None of them answers "does the thing we published correspond to that source?" Build-time injection is the attack that lives in that gap. The attacker does not modify a source file, because modifying a source file is the one thing your process is built to catch. Instead they arrange for something that runs *during* the build to change what comes out. ## What actually runs during a build A build is not a pure function of the repository. It is a program - usually a long one - executing on a machine with write access to the output. The places code can enter without touching application source include: - **The build configuration itself** - build scripts are code, and a build script can rewrite files, download something and execute it, or swap an output. - **Build plugins and their transitive dependencies.** A plugin is code you run at full privilege on your own source tree. Its own dependency graph is usually nobody's job to review. - **Post-compilation steps** - annotation processors, bytecode rewriters, obfuscators, shading and relocation, minifiers, resource packers. These exist to modify compiled output; that is their normal function, which is what makes a malicious one so easy to hide. - **Build-time-only dependencies** - code generators, linters that run in-process, test infrastructure that shares the build JVM or interpreter. - **The environment** - the base image or runner the build executes on, the language toolchain installed on it, an interpreter or agent injected via an environment variable, a pre-existing file in the workspace. - **The packaging and publishing step**, which handles the finished bytes last and is often the least-reviewed part of the pipeline. A concrete shape: a build plugin in a payments SDK's build rewrites compiled classes after compilation, so the published library contains a call to an attacker-controlled endpoint. That call exists in no source file and in no reviewed diff. Every consumer that embeds the SDK now handles card data next to attacker code, and the source repository they could inspect is genuinely clean. ## Why the usual signals stay green - **Tests pass**, because tests exercise code built by the same compromised build, and because the payload is typically dormant - gated on the environment, a date, or a target it is looking for. - **The diff is small and correct**, because the attacker did not author it. - **Static analysis on the source finds nothing**, because the source is not where the code is. - **A signature verifies**, because the signing step is at the *end* of the build. Signing after injection produces a validly signed backdoored artifact. A signature says who vouches for these bytes and that nobody altered them afterwards; it never says the bytes correspond to any particular source. - **A component inventory can also look clean.** An inventory answers what is *inside* the artifact; injected code that adds no new component simply does not show up as an entry. ## The direction that matters Keep the three claims straight, because mixing them up is the canonical wrong answer in this area. An inventory of contents tells you *what is inside*. A signature tells you *who vouches for it*. Neither tells you *how it came to be* - and build-time injection is entirely a question of how it came to be. That is why the countermeasures for this class are about the build rather than about the code, and why "we reviewed the repository" is never a complete answer to "is this release safe?". ## What this changes in practice When you suspect it, you work from the artifact backwards, not from the repository forwards: get the published bytes and their digest, build the declared source yourself somewhere you control, and compare - even a coarse comparison of what classes or symbols exist localises the injection. Then enumerate everything the release build was permitted to execute and check what changed between the last release you trust and this one. And treat the build machine as compromised: every credential it could read is burned, so rebuilding and republishing from the same machinery is not a fix.

  • Does a green CI run on the pull request tell you the released artifact is clean?
    No. It tells you a build of that source passed its checks. The released artifact is produced by a later run, usually with different privileges, different secrets and a publishing step. If something in that run rewrites the output, every check can pass on the PR and still ship an artifact that does not correspond to the reviewed source.
  • Where would you look first for the injected step?
    At everything with write access to the output: the build configuration, the plugin list and each plugin's own dependencies, any post-compilation rewriting or packaging step, and the image or runner the build executes on. Then diff those inputs against the last release you have reason to trust - the injected step is usually something that appeared or changed recently.
  • Why does a passing unit-test suite not rule this out?
    Because the tests run against code built by the same compromised build, so they measure the injected artifact's behaviour, not the source's. Payloads are also normally dormant - conditioned on a production environment, a specific consumer or a delay - so the behaviour the tests can observe is exactly the benign behaviour the source describes.

Auditing the recipe tells you nothing about the kitchen. Build-time injection is a cook adding an ingredient after the last person read the recipe, then serving the dish under the recipe's name.

saying these in an interview costs you the question

  • Claims review of the diff proves the release is safe
  • Says a green CI run means the artifact matches the source
  • Thinks a valid signature proves it was built from that source
  • Considers only application dependencies, never build-time ones
  • Assumes the git tag is what actually produced the binary

context

open as a page

A team installs its internal CLI by piping a hosted install script into a shell - what supply chain risk does that create?

level: juniorimportance: must knowfreq 66%

basics

~20 s

It runs whatever that URL returns at that moment, with the caller's full privileges and no version, review or integrity check. Anyone who can write to the hosting location therefore runs code on every laptop and every CI job.

open as a page

In SUNBURST, xz-utils, event-stream, Codecov and ua-parser-js, what was each entry point?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Five different doors. SUNBURST came through a compromised build system, xz-utils through a contributor who earned maintainer trust, event-stream through inherited maintainership, Codecov through a tampered delivery script, and ua-parser-js through a stolen publisher account.

open as a page

Why does installing an npm dependency execute that package's code before your build starts?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Many package managers let a package declare install hooks, scripts the manager runs automatically as part of installing it. Installing is therefore code execution, not a file copy, and it happens before your tests or scanners ever run.

open as a page

Why is installing a PyPI package your AI assistant suggested but nobody recognises risky?

level: juniorimportance: must knowfreq 62%

basics

~20 s

AI assistants invent plausible package names that were never published. Attackers collect those hallucinated names, register them on the public index, and wait for someone to install one. The name looks right precisely because a model generated it.

open as a page

What is a package maintainer account takeover, and why doesn't a valid release signature stop it?

level: juniorimportance: must knowfreq 66%

basics

~20 s

A maintainer account takeover is an attacker gaining the credentials or rights that publish a package, then shipping a malicious version under the real identity. A signature proves who published, not that the publisher was honest or still in control.

open as a page

What is protestware, and why do package signing and an SBOM fail to stop it?

level: juniorimportance: must knowfreq 62%

basics

~10 s

Protestware is a release the package's own maintainer deliberately sabotages to make a point. A signature proves who published the bytes and an SBOM lists what is inside; neither judges intent.

open as a page

Why did signature checks and vulnerability scanners both pass on the SUNBURST and ua-parser-js releases?

level: middleimportance: must knowfreq 58%

basics

~20 s

Both releases were authentic. SUNBURST was signed with the vendor's real key because the code was injected before signing, and ua-parser-js was published from the maintainer's real account. Scanners match published advisories, and no advisory existed yet.

open as a page

Why does a dependency confusion attacker publish a package with an absurdly high version number?

level: middleimportance: must knowfreq 72%

basics

~20 s

Because when a build consults an internal source and a public one for the same name, precedence is decided by version-selection rules, not by which source is trusted. The highest satisfying version wins, so a huge public version beats the internal one.

open as a page

A postinstall hook runs on a CI runner holding a production deploy role - what is the blast radius?

level: seniorimportance: must knowfreq 62%

basics

~20 s

Everything the job can reach: injected secrets in the environment, the short-lived credentials behind the deploy role, the source checkout, caches it writes and outbound network. The hook runs before any test, so production access is available to it from the start.

open as a page

A contractor self-merged a two-line change to the nightly job that writes your production ledger. Which controls failed?

level: seniorimportance: must knowfreq 64%

basics

~20 s

Separation of duties failed on a privileged path: one person authored and approved a change to code that holds production ledger credentials. Independent review of that path, a narrower job identity, and an out-of-band reconciliation are the missing controls.

open as a page

In dependency confusion, how does an attacker learn your internal package names?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Internal package names are not secrets. They leak through lock files in public repositories, shipped front-end bundles, container images, stack traces and CI logs. The attacker needs no access to your private index, only the name.

open as a page

In code review, which changed files get skimmed rather than read, and why does that matter?

level: juniorimportance: should knowfreq 57%

basics

~10 s

Lockfiles, generated code, vendored directories and binary fixtures. Reviewers approve them because the diff is machine-produced or unreadable, so a change hidden there inherits the trust of reviewed code without anyone having read it.

open as a page

Why can a backdoored compiler reinfect itself when rebuilt from clean, audited source?

level: middleimportance: should knowfreq 34%

basics

~20 s

Because the compiler that does the rebuild is a binary, not its source. A subverted compiler can recognise that it is compiling the compiler and re-insert both the backdoor and the rule that re-inserts it, so the clean source produces a backdoored binary again.

open as a page

Why doesn't HTTPS to a package mirror prove the packages that mirror serves are untampered?

level: middleimportance: should knowfreq 54%

basics

~20 s

TLS protects the channel to an endpoint; the mirror is the endpoint. It authenticates who you are talking to and stops tampering in transit, but it cannot say whether the files that endpoint holds are the ones the publisher produced.

open as a page

Why does installing a Python sdist execute package code when a prebuilt wheel does not?

level: middleimportance: should knowfreq 48%

basics

~20 s

An sdist is source, so the installer has to build it first, and that build runs the package's own build code. A wheel is already built, so installing it only unpacks files and metadata. The package's code then waits for an import.

open as a page

Why can a code reviewer miss a homoglyph dependency name in a manifest diff?

level: middleimportance: should knowfreq 45%

basics

~20 s

Reviewers read names for meaning, not glyph by glyph, and confusable characters render nearly identically: a Cyrillic vowel, a capital I standing in for lowercase l, the pair rn for m. Catching these needs mechanical normalisation, not attention.

open as a page

How can a payload committed inside a binary test fixture end up executing during a build?

level: middleimportance: should knowfreq 46%

basics

~20 s

A fixture is only data until something interprets it. If a build or test helper reads that file and evaluates, extracts or links what it holds, committing bytes there is committing code — through a diff no reviewer opened.

open as a page

How does a maintainer takeover happen through earned commit rights rather than stolen credentials?

level: middleimportance: should knowfreq 56%

basics

~20 s

The attacker volunteers. Over months or years of genuine useful contributions to an overloaded single-maintainer project they are granted commit and release rights, then abuse authority that was given, not taken. No credential is stolen, so no account control fires.

open as a page

Why does a geolocation-gated destructive payload in a dependency evade CI and code review?

level: middleimportance: should knowfreq 42%

basics

~20 s

The payload only runs when a host-environment condition matches, so the maintainer's own pipeline and most reviewers' sandboxes execute the benign branch. Review also inspects source, while consumers install a published artifact that need not correspond to it.

open as a page

A signed release jar calls an endpoint that exists in no source file - how do you investigate?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Investigate from the artifact backwards, not the repository forwards. Preserve the published bytes, rebuild the tagged source somewhere you control and compare, then enumerate every build step with write access to the output. Treat the builder and its secrets as compromised.

open as a page

A CI helper script that thousands of pipelines download at run time was tampered with - how far does that reach?

level: seniorimportance: should knowfreq 57%

basics

~20 s

Every job that fetched it ran attacker code with that job's credentials and could alter what the job published, so the reach extends to those artifacts' consumers. Worse, a run-time fetch leaves no dependency record to query.

open as a page

A dependency was backdoored for six hours last Tuesday — what evidence proves whether it reached your production?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Point-in-time records, not a fresh scan. You need the exact affected versions and window, the lockfiles at the commits that were built, build and dependency-resolution logs from that window, and the inventory of image digests actually deployed and running.

open as a page

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

level: seniorimportance: should knowfreq 43%

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.

open as a page

An archived, sole-maintained library ships inside your firmware. What does abandonment actually cost you?

level: seniorimportance: should knowfreq 50%

basics

~20 s

The visible loss is that no fix will ever arrive. The deeper loss is the disclosure channel: nobody remains to receive a report or publish an advisory, so a clean scan means nobody is looking.

open as a page

Your ingest gate blocks any dependency name within edit distance two of one you already use, and developers hit false positives weekly. How should that policy work?

level: principalimportance: should knowfreq 40%

basics

~20 s

Name similarity is a routing signal, not a verdict. Route flagged and never-before-seen names into a quarantine lane where a person decides once, record every decision on an allow-list, and watch for teams bypassing the gate.

open as a page

How can a dependency's import path still resolve correctly after an attacker takes over its expired domain?

level: middleimportance: nice to knowfreq 31%

basics

~20 s

Because an import path is a name, not an identity. Resolution asks whoever controls that domain or namespace today for the code, so a lapsed domain bought by a stranger serves their content under the original, unchanged path.

open as a page

A nightly build that always resolved an internal Python package from the company index installed a public copy of the same name, with no manifest change — how do you work out what let it in?

level: seniorimportance: nice to knowfreq 38%

basics

~20 s

Something stopped the internal source answering for that name — a not-found on a new or transitive requirement, an outage, or a cold cache on a fresh runner — while a public source stayed reachable. Start from the build's resolution log.

open as a page

Why are a package page's linked repository, stars and download count weak evidence of authenticity?

level: seniorimportance: nice to knowfreq 34%

basics

~20 s

Most registries never verify that the repository a package links to actually produced the published archive. Stars, README and badges belong to that repository, so an attacker can point at a popular project and borrow its reputation wholesale.

open as a page

A dependency just added a co-maintainer whose account is six weeks old — what does that signal actually tell you?

level: seniorimportance: nice to knowfreq 31%

basics

~20 s

It tells you that who can publish has changed, which is worth acting on. It tells you little about the person: account age, activity history and profile detail are cheap to manufacture, so treat the change as a trigger, not a verdict.

open as a page

showing 1–30 of 34