Skip to content
News briefSC-2026-0317

Ninety minutes inside the build step

Three Rust crates, one of them downloaded 53 million times in ninety days, briefly shipped a dependency that ran an infostealer at compile time.

SeverityHigh

3 minEnterprise CyberSec

On 20 August three crates on the Rust registry — arrayref 0.3.10, append-only-vec 0.1.9 and internment 0.8.7 — were published with an added dependency called proc-macro1. No such crate belongs in a normal build. It is a typosquat of proc-macro2, one of the most widely used packages in the ecosystem, and it existed to be mistaken for it.

arrayref alone has 245 million lifetime downloads and 53 million in the past ninety days. The other two account for roughly 19 million installs between them.

The payload ran before anyone ran anything

The malicious code sat in build.rs, the script Cargo executes during compilation. Nothing had to be launched for it to fire; compiling was enough. It rebuilt its own infrastructure from base64 fragments, picked a payload matching the host — Linux x86-64, Windows x86-64, macOS x86-64 or macOS ARM64 — and then read the SQLite login databases used by Chrome, Brave and Edge, along with host details.

The timeline is the whole story

  • 01:17 UTC — a fake developer account is created.
  • 01:55 — a clean proc-macro1 1.0.106 is published, establishing the name.
  • 07:11 — proc-macro1 1.0.107 arrives carrying the payload.
  • 07:15 — arrayref 0.3.10 goes up and earlier versions are pulled, so builds resolve to the new one.
  • 07:54 — the incident is reported.
  • 08:03 to 08:41 — the malicious packages are removed.

The exposure window was about an hour and a half. That sounds survivable until you consider what runs continuously: CI pipelines, container image builds, dependency refresh jobs. A ninety-minute window against a crate pulled 53 million times a quarter is not a narrow one — it is simply a short one, which is a different property.

What this argues for

  • Lockfiles committed and honoured in CI, so a fresh resolve cannot silently pick up a new transitive dependency.
  • A hold-back window on new versions rather than resolving to latest at build time.
  • Alerting on new transitive dependencies, not just on new direct ones — proc-macro1 was never added by any maintainer's own hand.
  • Treating build scripts as executed code in review, because that is what they are.

Pulling the earlier versions was the effective move. It removed the choice: anything resolving arrayref during that window had one version available to resolve to.

Retold from BleepingComputer. This is a summary in our own words; follow the link for the original reporting.

Read next

Across the network

Desks that share a zone with this one on the BITBRIEF coverage map.

Terms defined