Rust has a well-deserved reputation for safety. Memory safety, type safety, thread safety -- the language prevents entire classes of bugs at compile time. But there is a significant security gap that most Rust developers overlook: build scripts.
Every Rust crate can include a build.rs file that runs before the crate is compiled. This build script is a full Rust program with no restrictions. It runs with the same permissions as the developer or CI/CD system invoking cargo build. It can read environment variables, make network requests, modify the filesystem, and execute system commands.
What build.rs Can Do
Legitimate uses of build scripts include: compiling C code for FFI bindings, generating Rust source code from protobuf or schema definitions, detecting system configuration, and setting compiler flags.
Malicious uses include everything a legitimate build script can do, plus: reading environment variables (stealing CI/CD tokens and credentials), downloading and executing additional payloads, modifying source files of the crate being built or other crates, exfiltrating project source code, and installing persistent backdoors.
The key point is that cargo build is not a safe operation when building untrusted code. It is equivalent to running an arbitrary program on your machine.
The Scope of the Problem
Run cargo tree --prefix none -e build on a typical Rust project and count the crates with build scripts. A medium-sized project might have 20-40 crates with build.rs files. Each of these runs arbitrary code during every build.
Many of these build scripts are benign and necessary (compiling C dependencies, generating bindings). But auditing each one requires time and expertise. And every cargo update can change the build script code that runs.
Sandboxing Efforts
The Rust and Cargo teams are aware of this problem. Several proposals for build script sandboxing have been discussed:
Build script capabilities. A proposal to declare what resources a build script needs (network access, filesystem access, environment variables) so that Cargo can restrict access to only what is declared. This has not been implemented yet.
wasm-based build scripts. Running build scripts as WebAssembly modules would provide sandboxing automatically. This is technically feasible but would break build scripts that need to invoke system tools or compile C code.
Declarative build scripts. For common build script patterns (like running cc to compile C files), Cargo could support declarative configuration instead of arbitrary code. The links key in Cargo.toml is a step in this direction.
None of these solutions are fully implemented. For now, build scripts run with full privileges.
Risk Assessment for Dependencies
When evaluating a new crate dependency, check whether it has a build.rs file and what it does:
Read the build.rs. Most build scripts are short and readable. Look for network access, environment variable reading, and filesystem operations outside the crate directory.
Check if build.rs is necessary. Some crates include build scripts for convenience features (like embedding version information) that could be done differently. If the build script is doing something suspicious relative to the crate stated purpose, that is a red flag.
Monitor changes. When updating crate versions, diff the build.rs files. A previously benign build script that adds network access in a new version deserves scrutiny.
Prefer crates without build scripts. When choosing between functionally equivalent crates, prefer the one without a build script. Fewer build scripts means less arbitrary code execution during your build.
Practical Defenses
Use cargo-audit. The cargo-audit tool checks your dependencies against the RustSec advisory database. While it does not analyze build scripts specifically, it catches known malicious crates.
Use cargo-crev. The cargo-crev system enables code reviews of crate dependencies to be shared among the community.
Sandbox your builds. Run cargo build inside a container or VM with restricted network access and minimal filesystem permissions.
Use cargo-vet. Mozilla cargo-vet tool provides a framework for auditing crate dependencies, including build scripts. It tracks which crate versions have been audited and by whom.
How Safeguard.sh Helps
Safeguard.sh monitors your Rust crate dependencies, including their build scripts, for known vulnerabilities and malicious code patterns. Our platform tracks the RustSec advisory database, detects suspicious crate updates, and generates SBOMs that include build-time dependencies. When a crate you depend on is flagged for security concerns, Safeguard.sh alerts you before the compromised code runs on your systems.