Scroll to top

245 Million Downloads, 86 Minutes: The arrayref Supply-Chain Attack

I’m Joe. On 20 August 2026 someone published a poisoned release of a Rust crate that almost nobody thinks about and almost everybody compiles. Nothing about the attack was novel — that is exactly why it is worth writing down. It used a stolen publishing identity, a name that looked like a name you already trust, and a build script. Below is what happened, what the numbers actually mean, and the handful of controls that decide whether an incident like this is a footnote in your CI logs or a credential-theft event.

  1. What Actually Happened

    On 20 August 2026 a compromised release of arrayref 0.3.10 appeared on crates.io, alongside internment 0.8.7 and append-only-vec 0.1.9. Each of them added a dependency on a typosquatted crate called proc-macro1 — a name chosen to sit one character away from a crate almost every Rust project already pulls in. The build script of that crate downloaded and executed a remote binary while the project compiled. The maintainer account behind two of the affected crates appears to have been compromised rather than complicit; the Rust Security Response Team pulled the releases, locked the account and deleted six attacker-created crates (proc-macro1, proc-macro-en, aovine, arone, aronenao and tinymember).

  2. Build Time Is Worse Than Run Time

    This is the part teams under-rate. You do not have to run the program for build-time malware to run — cargo build is enough. That means the code executes on developer laptops and, far more importantly, inside CI runners: the machines that hold registry tokens, cloud credentials, signing keys and SSH access to everything you deploy. A run-time backdoor needs your users; a build-time backdoor only needs your pipeline. And a pipeline is the one place in most organisations where highly privileged credentials sit next to arbitrary third-party code by design.

  3. The Numbers, and What They Do Not Mean

    arrayref has roughly 245 million all-time downloads, about 53.7 million in the last ninety days, and 403 crates list it as a direct dependency — the transitive count is far higher. Those numbers describe the blast radius of the name, not the number of victims. What decides whether you were exposed is much narrower: did any build in your estate resolve a fresh dependency graph during the window, and did it pick up the bad version? A locked, committed dependency file means the answer is almost certainly no.

  4. Eighty-Six Minutes Is Not “Nothing”

    The bad arrayref release was published at 07:15 UTC and gone from the index by 08:41 — an exposure window of 86 minutes, with the other affected releases in the same 86-to-107 minute range. That is a genuinely fast response and it still is not short. Ninety minutes is a normal morning of CI: dependency-update bots, nightly rebuilds, a merge queue, a release pipeline that re-resolves from scratch in a clean container. Automation does not wait for the advisory. When people say the window was small, they are measuring in human time; attackers are measuring in build time.

  5. How I Triage This on a Client Estate

    The first question is never “are we affected”, it is “can we answer that question at all”. I diff the committed lockfile against what the last few builds actually resolved, then search CI logs for the affected versions by exact string. Next I look for outbound network connections made during a build step, because a compile has no legitimate reason to fetch a binary. Then I check what those runners could reach: which tokens were in the environment, what scope they had, and whether they were rotated after the window. If the answer to any of that is “we do not keep those logs”, that gap is the finding — and it will still be the finding during the next incident.

  6. Controls That Would Have Contained It

    Commit your lockfile and build with --locked in CI, so a fresh malicious version cannot be pulled in silently. Run builds in a sandbox with no outbound network access unless a step genuinely needs it — that single control neuters most build-script droppers. Treat build scripts as untrusted code, because that is precisely what they are. Give each pipeline the narrowest credentials it can function with, and prefer short-lived tokens over long-lived registry keys. Vendor or mirror dependencies for anything you ship to customers. None of this is exotic; all of it is unglamorous, and it is the difference between an alert and an incident.

  7. The Maintainer Is Part of Your Attack Surface

    The technical flaw here was not in Rust, in cargo or in the crate itself. It was one person’s publishing credentials. Every registry you depend on is, in the end, a list of individual accounts that can push code into your build. Registries can shrink that risk with mandatory two-factor authentication on publish, scoped and short-lived publish tokens, and a delay or attestation step on new releases of high-reach packages. Consumers can shrink it by not treating “latest” as a value. Researchers have noted infrastructure overlap between this campaign and previously reported state-linked activity; that is useful context, but attribution has never patched anything.

  8. Your Checklist for This Week

    Search every lockfile and CI log for arrayref 0.3.10, internment 0.8.7 and append-only-vec 0.1.9, and for any reference to proc-macro1. If you find one, rotate every credential that runner could see and treat the build host as compromised until you can prove otherwise. Then make the structural change rather than only the tactical one: lock and commit dependencies, block egress during builds, and shorten the life of your CI credentials. The next poisoned package will have a different name, and none of those three controls will care.

Build-time supply chain attack

Comments (5)

  1. ci_greg 2 days ago Reply
    The egress-blocking tip is the one that finally got approved at my company. Turns out almost nothing in our build actually needed the internet after the fetch step.
    1. joecybertech 2 days ago Reply
      That is usually how it goes. The exception list is always shorter than people expect, and writing it down is half the value.
  2. rustacean_mio 2 days ago Reply
    86 minutes is a genuinely impressive response time from the security team. People are being unfair to them in the threads.
  3. appsec_dana 1 day ago Reply
    We had a nightly job that runs cargo update. Found nothing in the logs, but only because we happened to keep them for 30 days. That was luck, not design.
    1. joecybertech 1 day ago Reply
      Write that sentence into your next risk review verbatim. “We could answer this by luck” is the finding.
  4. pkg_watcher 1 day ago Reply
    Mandatory 2FA on publish for high-download crates seems like the obvious move. Curious why registries are still slow on this.

Leave a Comment

Please check your email
Please check your message
Thank you. Your message has been sent.
Error, email not sent