From an ArrayRef Warning to a Real Rust Audit on Debian

A video about the ArrayRef supply-chain attack made me question the Rust crates I had recently installed on Debian.

Why investigating a Rust supply-chain story was not a whim

Date: August 28, 2026


The concern that started the investigation

I had recently been installing and compiling Rust crates while learning Burn, Leptos, GPUI, Cargo utilities, and the wider Rust ecosystem. Then I watched Low Level’s video, “Would Rust have fixed this?”, about the August 2026 ArrayRef supply-chain attack.

The central lesson was uncomfortable but important:

Rust can prevent many accidental memory-safety errors, but it cannot make intentionally malicious code safe.

Rust’s ownership and type systems address problems such as invalid references, use-after-free errors, and data races in ordinary Rust code. They do not determine whether a package maintainer’s account has been compromised or whether a dependency deliberately downloads malware.

This incident exploited Cargo’s build process rather than a memory-safety flaw. A poisoned version of a trusted crate added a malicious dependency named proc-macro1, deliberately resembling the legitimate proc-macro2. The malicious code lived in a build.rs file. Cargo compiles and executes build scripts before compiling the rest of a package, so merely building or checking an affected dependency graph could trigger the payload.

That means commands as ordinary as these can cross an execution boundary:

cargo check
cargo build
cargo test
cargo run
cargo install

The danger was not that Rust malfunctioned. Cargo correctly executed code that an attacker had placed inside the trusted build chain.

What happened in the ArrayRef incident

On August 20, 2026, attackers published poisoned releases from what the Rust Security Response Team believes was a compromised maintainer account:

Legitimate crateMalicious versionTime available on crates.io
arrayref0.3.1086 minutes
internment0.8.790 minutes
append-only-vec0.1.9107 minutes

Those releases brought in the typosquatted proc-macro1 dependency. The Rust team also removed proc-macro-en, aovine, arone, aronenao, and tinymember as part of the response.

The incident was a supply-chain backdoor, not a confirmed self-propagating Rust worm. The video compared it with npm worms that steal package-publishing credentials and use them to poison additional packages automatically. The ArrayRef payload demonstrated the dangerous first half of that possibility—code execution through a trusted dependency—but public evidence did not show it automatically stealing Cargo publishing credentials and republishing more crates.

That distinction matters:

  • Supply-chain attack: trusted software or its delivery path is poisoned.
  • Backdoor/dropper: malicious code creates access or downloads another payload.
  • Worm: malware automatically spreads from one compromised target to another.

The unexpected warning about investigating

While trying to understand the incident, ChatGPT displayed:

This content can’t be shown. We’re especially careful with cybersecurity requests.

The likely trigger was the combination of subjects being discussed: malware execution, command-and-control infrastructure, credential theft, persistence, and worm propagation. Those subjects can describe offensive activity, but they are also necessary for understanding how a public attack worked and how to defend against it.

My purpose was defensive and concrete:

  • Determine whether my own Debian machine had downloaded the affected crates.
  • Audit the Rust projects I had recently built.
  • Understand the difference between malware, vulnerabilities, maintenance warnings, and compiler warnings.
  • Apply safe updates where they were justified.

Ignoring the story simply because the investigation received a safety warning would have been the wrong lesson. The correct response was neither panic nor dismissal. It was verification.


Action 1: Check Cargo’s local cache for the malicious crates

The first action was the exact cache search recommended by the Rust Security Response Team:

find ~/.cargo/registry/cache -type f \( \
  -name 'append-only-vec-0.1.9.crate' -o \
  -name 'arrayref-0.3.10.crate' -o \
  -name 'internment-0.8.7.crate' -o \
  -name 'proc-macro1-*.crate' -o \
  -name 'proc-macro-en-*.crate' -o \
  -name 'aovine-*.crate' -o \
  -name 'arone-*.crate' -o \
  -name 'aronenao-*.crate' -o \
  -name 'tinymember-*.crate' \
\) -print

The command returned directly to the prompt without printing a filename.

Meaning of the empty result

No known malicious crate archive from this campaign was present in Cargo’s default registry cache. Ordinary cargo clean removes a project’s build artifacts but does not normally erase this global registry cache, so the empty result was strong evidence that this machine had not downloaded the affected releases.

It was not a magical guarantee that every dependency on the computer was safe. It answered one specific question: Was there evidence of the known ArrayRef campaign in Cargo’s cache? The answer was no.

Action 2: Install the RustSec dependency scanner

The next question was broader: did any of my current Rust projects contain dependencies with known security advisories?

cargo audit was not installed:

error: no such command: `audit`

I installed the RustSec-maintained tool with its published lockfile:

cargo install cargo-audit --locked

I did not use sudo. Cargo build scripts run with the permissions of the account invoking Cargo, so Rust development and tool installation should not be performed as root.

The installation was verified:

cargo audit --version

Result:

cargo-audit-audit 0.22.2

cargo audit examines a project’s Cargo.lock against the RustSec Advisory Database. It identifies known vulnerabilities and can also report informational warnings such as unmaintained, unsound, or yanked packages.

Action 3: Audit the Burn project

The first scan examined the Burn demonstration project:

cargo audit --file ~/Projects/burn-demo/Cargo.lock

The lockfile contained 726 crate dependencies. This large number illustrates why checking only the few dependencies typed into Cargo.toml is insufficient: modern Rust projects often contain hundreds of transitive dependencies.

The initial findings were:

CrateInstalled versionClassificationMeaningResolution
h20.4.15VulnerabilityUnbounded empty HTTP/2 DATA frames could cause excessive memory use or a panicUpgrade to >=0.4.16
stable-vec0.4.2UnsoundPanic-safety flaw could cause double-free/use-after-free in a narrow conditionUpgrade to >=0.4.3
chacha200.10.1YankedThis release had been withdrawn from normal dependency selectionUpgrade to 0.10.2
bincode2.0.1UnmaintainedNo longer actively maintainedMonitor upstream dependencies
paste1.0.15UnmaintainedNo longer actively maintainedMonitor upstream dependencies

The audit ended with:

error: 1 vulnerability found!
warning: 4 allowed warnings found

This was not evidence that malware had executed. It meant one locked dependency had a published vulnerability, while four other packages carried advisory classifications that cargo audit treated as warnings.

Action 4: Audit the Leptos project

The Leptos project was scanned next:

cargo audit --file ~/Projects/leptos-demo/Cargo.lock

It contained 238 crate dependencies and reported:

  • paste 1.0.15 — unmaintained.
  • proc-macro-error2 2.0.1 — unmaintained.

The scan found zero vulnerabilities. These warnings should be watched, but they are primarily matters for the upstream projects that currently depend on those packages.

Action 5: Audit the GPUI project

The GPUI project was also scanned:

cargo audit --file ~/Projects/gpui-demo/Cargo.lock

It contained 699 crate dependencies and reported:

  • paste 1.0.15 — unmaintained.
  • rustybuzz 0.20.1 — unmaintained.
  • ttf-parser 0.25.1 — unmaintained.

Again, the scan found zero vulnerabilities. Because these are transitive dependencies in the GPUI/Zed stack, forcing replacements locally could create incompatibilities. The proper action for a learning project is to monitor upstream updates rather than invent unsupported substitutions.


Action 6: Apply targeted fixes to Burn

I avoided a blanket cargo update, which could have changed dozens or hundreds of unrelated packages. Instead, each confirmed finding was addressed with a precise patch update.

Patch the h2 vulnerability

cd ~/Projects/burn-demo
cargo update -p h2 --precise 0.4.16

Cargo reported:

Updating h2 v0.4.15 -> v0.4.16

Patch the stable-vec soundness problem

cargo update -p stable-vec --precise 0.4.3

Cargo reported:

Updating stable-vec v0.4.2 -> v0.4.3

Replace the yanked chacha20 release

cargo update -p chacha20 --precise 0.10.2

Cargo reported:

Updating chacha20 v0.10.1 -> v0.10.2

Each command altered the locked version of only the named package. Cargo explicitly noted that the unrelated dependencies remained unchanged.

Action 7: Re-audit before compiling

The updated Burn lockfile was scanned again:

cargo audit

The vulnerability, unsoundness, and yanked-release findings were gone. Only two allowed maintenance warnings remained:

  • bincode 2.0.1 — unmaintained.
  • paste 1.0.15 — unmaintained.

There was no error: vulnerability found line. At this point, Burn’s locked dependency graph contained no vulnerabilities known to RustSec.

Action 8: Compile the exact audited dependency set

The final verification used:

cargo check --locked

The --locked option told Cargo to use the exact versions already recorded in Cargo.lock and fail rather than silently calculate a different dependency set.

Cargo downloaded the three selected patched crates:

Downloaded chacha20 v0.10.2
Downloaded h2 v0.4.16
Downloaded stable-vec v0.4.3

The complete Burn dependency graph compiled successfully:

Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 20s

The build log also contained this legitimate crate:

Compiling proc-macro2 v1.0.107

That name deserves deliberate attention. proc-macro2 is the legitimate crate. The malicious typosquat from the ArrayRef incident was proc-macro1. The similar name and matching-looking version were part of what made the attack deceptive.


The final compiler warning: important, but not a security incident

After the successful check, Cargo reported that these packages contain code that a future Rust compiler will reject:

  • burn-cubecl 0.21.0
  • burn-cubecl-fusion 0.21.0

I inspected the details:

cargo report future-incompatibilities --id 1

The warning concerns a trailing semicolon produced by the local_tuner!() macro in expression position. Rust accepts this behavior today but is phasing it out. In a future release, it will become a compilation error.

This warning does not indicate:

  • malware;
  • a currently exploitable vulnerability;
  • a failure of today’s build;
  • a problem in my own main.rs.

It means Burn’s maintainers will need to adjust macro code for a future Rust compiler. Pre-release versions 0.22.0-pre.1 and 0.22.0-pre.2 were available, but jumping to a pre-release solely to silence the warning would introduce unnecessary instability. The prudent action is to remain on the working stable 0.21.0 release and revisit the issue when Burn publishes its next stable version.

Understanding every warning category

Today’s investigation showed why security terminology must be interpreted carefully:

ClassificationWhat it meansWhat I should do
MaliciousCode intentionally performs harmful behaviorStop, isolate, investigate exposure, and follow incident-response guidance
VulnerableA known security flaw exists in otherwise legitimate codeUpgrade to a patched version and test
UnsoundSafe Rust APIs may permit memory-unsafe behavior under documented conditionsPrefer a patched version even when the advisory is informational
YankedThe publisher removed a release from normal new dependency selectionInvestigate why; do not automatically assume malware
UnmaintainedActive maintenance has stoppedMonitor upstream replacements and avoid adopting it directly in new work
Future-incompatibleCurrent code will become invalid under a future compiler ruleContinue safely today; plan an upstream upgrade before the rule becomes an error

These labels are not interchangeable. A yanked package is not necessarily malicious. An unmaintained package is not automatically vulnerable. A future compiler warning is not evidence of compromise. A clean build is also not proof that every dependency is trustworthy.


What “safe” means after this investigation

No single command can mathematically prove that a development computer is completely safe. What I now have is a collection of independent, mutually reinforcing evidence:

  1. The official ArrayRef cache search found none of the malicious releases.
  2. RustSec scans found no ArrayRef campaign advisories in any of the three project lockfiles.
  3. The only actual Burn vulnerability was identified and patched.
  4. The stable-vec soundness issue was patched.
  5. The yanked chacha20 version was replaced.
  6. A second audit found zero known vulnerabilities.
  7. cargo check --locked successfully compiled the precise audited dependency set.
  8. The remaining messages were classified and understood rather than ignored.

The responsible conclusion is not “nothing bad can ever happen.” It is:

My Rust projects show no evidence of the ArrayRef compromise, and their current locked dependencies contain no vulnerabilities known to RustSec as of this audit.

That is a defensible conclusion grounded in evidence—not a feeling and not a whim.

My Rust dependency-security routine from now on

Before adding a direct dependency

For an unfamiliar crate, I will first examine its metadata, repository, maintenance activity, release history, and purpose:

cargo info crate-name

I do not need to manually audit hundreds of transitive crates before every experiment, but I should understand the direct dependency I am choosing and be cautious with brand-new, obscure, or unexpectedly complex packages.

After adding a dependency, but before the first build

cargo add crate-name
git diff -- Cargo.toml Cargo.lock
cargo tree
cargo audit

cargo add records and resolves the dependency. The later compile or check is the point at which build scripts may execute, so reviewing and auditing the lockfile first is meaningful.

Build the audited versions

cargo check --locked

For an application or executable project, I will keep Cargo.lock under version control. It is the receipt showing the exact dependency set I audited and compiled.

Install Cargo tools reproducibly

cargo install tool-name --locked

I will never run Cargo installation or builds through sudo.

Audit regularly

I will run:

cargo audit

at these times:

  • after adding or substantially updating dependencies;
  • before returning to an older Rust project;
  • before publishing or deploying a project;
  • periodically while a project remains active;
  • after hearing about a credible Rust ecosystem incident.

Investigate dependency paths

When an advisory names a transitive crate, I can discover why it is present:

cargo tree -i crate-name

This tells me which higher-level dependency brought it into the project. I can then decide whether a precise patch update is sufficient or whether an upstream library must be upgraded.

Patch narrowly, then verify

When RustSec identifies a patched version and the dependency requirements permit it:

cargo update -p crate-name --precise patched-version
cargo audit
cargo check --locked

I will avoid broad, reflexive updates when a targeted patch is available.

Treat yanked warnings as questions, not commands

The ArrayRef attackers reportedly yanked clean releases to make an update appear urgent. Therefore, a yanked warning should prompt investigation—not an automatic rush to the newest version.

For nonurgent releases, allowing time for maintainers, automated scanners, and the community to examine a fresh package can reduce exposure to a short-lived poisoned release.

Use isolation for higher-risk experiments

For a new, obscure, or suspicious crate, I can use one of my disposable virtual machines. That environment should not contain personal browser profiles, SSH keys, publishing credentials, password-manager sessions, or other secrets.

Isolation is especially useful when experimenting with a package that contains an unexpected build script or network-related build dependency.

Let upstream maintainers handle transitive replacements

I will monitor the remaining unmaintained dependencies:

  • Burn: bincode, paste.
  • Leptos: paste, proc-macro-error2.
  • GPUI: paste, rustybuzz, ttf-parser.

I will not force random replacements into a dependency graph that I do not maintain. When Burn, Leptos, or GPUI publishes a compatible stable update, I can update the top-level framework, re-audit, and rebuild.

Watch Burn’s future compiler compatibility

I will remain on Burn 0.21.0 while it is the working stable release. When the next stable Burn version becomes available, I should review its release notes, update deliberately, run cargo audit, and repeat cargo check --locked.


Reflection: why this was worth following through

The original fear—an ArrayRef compromise on my Debian computer—was not confirmed. That does not make the investigation unnecessary. The process found and resolved three real dependency concerns that would otherwise have remained in my Burn lockfile.

Had I ignored the video, I would not necessarily have been infected. But I would still have missed:

  • a published HTTP/2 vulnerability;
  • a patched Rust soundness problem;
  • a yanked cryptography dependency;
  • several maintenance warnings worth monitoring;
  • a future Rust compatibility issue in Burn’s GPU stack.

The lesson is not to react fearfully to every security headline. The lesson is to develop a repeatable method:

Hear the warning. Identify the exact claim. Check the official advisory. Inspect the local evidence. Patch only what is confirmed. Re-audit. Rebuild. Record what was learned.

That is how curiosity becomes security practice.

Sources