Rust is memory-safe, not supply-chain-safe: the arrayref case
by Claudiu Hulea · IT Management Consultant
Rust gives you safe memory. It does not give you a safe supply chain. On 20 August 2026, three popular Rust crates were poisoned to deliver an infostealer, and memory safety had nothing to say in this story. The attack ran code on the developer’s machine at compile time, through exactly the mechanism the ecosystem treats as normal.
The most downloaded of them, arrayref, has 245 million downloads. Downstream are libraries like blake3 and GUI frameworks (egui, eframe, iced), plus components from Ethereum and Solana.
In brief
- Poisoned crates:
arrayref(0.3.10),append-only-vec(0.1.9),internment(0.8.7), all from the same maintainer account, compromised via account takeover. - The method: the upstream source stayed untouched. The attacker injected a dependency on
proc-macro1, a typosquat of the extremely popularproc-macro2. - The payload: an infostealer that runs at compile time, through
build.rs. Multi-platform (Windows, Linux, macOS), it steals credentials from Chrome, Brave and Edge, establishes persistence and talks to a command server. - The scale:
arrayrefhas 245 million total downloads, 53 million in the last 90 days. Downstream:blake3,egui,eframe,iced, Ethereum and Solana components. - Response: the exposure window was about two hours. Attribution: the infrastructure overlaps with North Korean campaigns (Mastra, axios).
- Discovered by: StepSecurity, Wiz, SafeDep, Aikido.
How it worked
The attack broke nothing in the classic sense. It used trust.
First, the account takeover. The attacker created fake GitHub and crates.io accounts impersonating a well-known developer (David Tolnay) and gained control over publishing the three crates. Their code stayed identical to the legitimate one, so as not to raise suspicion on a quick read.
Then, the typosquat. The only visible change was a new dependency: proc-macro1, with a name one digit away from proc-macro2, one of the most used crates in the whole ecosystem. In a tree of hundreds of transitive dependencies, an “almost right” name goes unnoticed.
proc-macro1 contained a build.rs script, which runs automatically at compile time. It reconstructed its infrastructure from base64-encoded fragments and selected the payload matching the operating system (Windows, Linux, macOS on x86_64 and ARM64). Then it started an infostealer: credentials from the SQLite databases of Chrome, Brave and Edge, persistence via Registry, LaunchAgent and systemd, plus communication with a command server.
Why build.rs changes everything
This is the crux, and the part that has nothing to do with Rust in particular. The malicious code does not run when you start the application. It runs when you compile it.
build.rs is a legitimate mechanism: many crates use it to generate code, detect capabilities or link native libraries. But it means a simple cargo build executes third-party code, on the developer’s workstation or on the CI runner, with the rights of the user compiling. It is a layer that endpoint antivirus and EDR rarely monitor, because “a build running a build script” looks perfectly normal.
It is the same pattern as npm, with postinstall, and as the obug/piccolore typosquat in the JavaScript ecosystem: execution at install or at build, under the radar. Rust is not an exception to this class. It is just newer on the target list.
Why memory safety does not save you
Rust eliminates a whole category of vulnerabilities: buffer overflow, use-after-free, data races. They are real and valuable. But they are bugs in the code you write.
A supply-chain attack does not exploit a bug. It exploits trust in what you pull. A malicious build.rs runs with all the rights compilation gives it, no matter how memory-safe the rest of the program is. The language’s guarantee stops at the code you control, and the hostile dependency is already inside, invited.
That is why “we write in Rust, so we are safe” is an incomplete sentence. Rust solves one problem. The supply chain is another, and it is defended with other controls.
What stops this attack
The good news: this exact attack is stopped by the supply-chain hygiene that matters anyway. None of the controls below is exotic.
cargo denyon sources and advisories. A policy that forbids any unexpected registry or source and checks the tree against the advisory database (RustSec) catches a new, unknown dependency likeproc-macro1. Run in CI, as a gate that stops the build.- Vendoring and offline builds. If dependencies are vendored and the build runs with no network access, a newly published, malicious version cannot be pulled automatically. You build from what you reviewed, not from what was published today.
- Pinning with
Cargo.lock. Pin exact versions and treat any change to the dependency tree as a code change, reviewed in the PR.arrayref@0.3.10does not reach the build if the lock file is pinned to a clean version. - Isolated CI. Runners that compile execute third-party code through
build.rs. Limit their access to the network and to secrets, and rotate the credentials reachable from the build environment regularly. - Fewer dependencies, with verified provenance. Every extra crate is another link. Prefer a small tree and watch for “almost right” names and unusual version jumps.
What to take away
The incident was caught quickly, in about two hours, because researchers watch the registries. But the window existed, and arrayref sits in a chain that reaches hundreds of thousands of projects.
The lesson is not “Rust is unsafe”. It is that a language’s safety and its supply chain’s safety are two different things, defended differently. Memory safety you get from the compiler. Supply-chain safety you build: cargo deny, vendoring, offline builds, pinning, isolated CI.
Want to know which dependencies actually enter your builds, and who can change them? Get in touch and we start with a software supply-chain audit.
Sources
Frequently asked questions
What happened with the arrayref crate?
On 20 August 2026, three popular Rust crates (arrayref, append-only-vec, internment) were poisoned via maintainer account takeover. The upstream source stayed untouched, but the attacker injected a dependency on proc-macro1, a typosquat of the popular proc-macro2, which ran an infostealer at compile time through build.rs. arrayref has 245 million downloads.
Why does Rust not protect you in this case?
Because memory safety eliminates bugs in the code you write (buffer overflow, use-after-free), not supply-chain attacks. A malicious build.rs runs with the rights of compilation, no matter how memory-safe the rest of the program is. The language guarantee stops at the code you control, and the hostile dependency is already inside.
Why is build.rs dangerous?
Because it runs automatically at compile time, so a simple cargo build executes third-party code on the developer workstation or the CI runner, with the user rights. It is a layer that antivirus and EDR rarely monitor. The same pattern as postinstall in npm.
How do I defend against it?
With supply-chain hygiene: cargo deny on sources and advisories (catches a new, unknown dependency), vendoring and offline builds (you do not pull newly published versions), pinning with Cargo.lock (the poisoned version does not reach the build), isolated CI (build.rs runs third-party code), and fewer dependencies, watching for almost-right names.