cargo build Executes Code You Never Read

A malicious Rust crate fired its payload during compilation. Thanks to build.rs and proc macros, cargo build runs code you never read.

Share

SafeDep just published an analysis of a malicious Rust crate published under the arrayref name. The payload doesn't wait for your app to run — it executes the moment you compile. cargo build was the infection vector.

That's the uncomfortable part. In Rust, adding a dependency is closer to curl | sh than we like to admit.

Why this matters

Most dependency threat models assume bad code ships to production and misbehaves there. Build-time malware is worse: it runs on your dev machine, as your user, before a single test executes.

That process can read ~/.aws/credentials, your SSH agent, and every CI environment variable — including GITHUB_TOKEN. Runtime sandboxes, image scanners, and e2e tests never see it, because the malicious code never ships anywhere.

How it works

Two perfectly legitimate Cargo features make this trivial. First, build.rs: any crate can ship a build script that Cargo compiles and executes before building your code. Second, proc macros: the macro crate gets compiled to a dynamic library and loaded into rustc itself to expand your source.

Both are everywhere — serde, bindgen, and thiserror all use them — which is exactly why a dependency containing one raises no eyebrows. Per SafeDep, the arrayref payload rode this path: compile-time execution, zero runtime footprint.

Where this helps

  • CI gate: run cargo audit against the RustSec advisory database on every PR, and fail on yanked or unmaintained crates.
  • Lockfile review: diff Cargo.lock in pull requests. When a new transitive dependency shows up, open its build.rs before approving.
  • Sandboxed builds: compile untrusted or brand-new dependencies inside a container or CI runner with no access to your home directory.
  • Vendoring: cargo vendor freezes dependency sources in-repo, so a swapped upstream crate becomes a visible diff instead of a silent fetch.

Watch out

Cargo has no stable sandbox for build scripts or proc macros — the toolchain will not protect you here. Sandboxing work exists but is still experimental.

cargo audit only flags known-bad versions. A freshly published malicious crate has no advisory yet, so scanners lag by hours or days.

Lockfiles protect updates, not the initial cargo add. And you can't avoid proc macros in modern Rust — the realistic goal is shrinking the blast radius, not abstinence.

Try it yourself

# Does anything in your tree depend on a given crate?
# "did not match any packages" is the answer you want
cargo tree -i arrayref

# Check your lockfile against the RustSec advisory database
cargo install cargo-audit --locked
cargo audit

# Enforce it in CI: advisories, bans, licenses
cargo install cargo-deny --locked
cargo deny check advisories bans

TL;DR

  • What happened: a malicious crate under the arrayref name executed its payload at compile time through Cargo's build-time machinery.
  • Why it matters: build.rs and proc macros run with your permissions during cargo build — before tests, scanners, or sandboxes see anything.
  • Try today: run cargo audit, check cargo tree -i arrayref, and move builds of unvetted deps into a container.