Subject
cc is a build-time helper for Cargo build scripts. A consumer builds a cc::Build, hands it a set of .c/.cpp/.cu/.S source files plus flags and defines, and the crate orchestrates: detect the host/target compiler family (clang, gcc, MSVC, NVCC, emscripten, VxWorks, zig cc, …), assemble the right argv per source file, spawn the compiler, optionally in parallel, and archive the resulting object files into a static library that Cargo can link.
The crate ships a single library target with two opt-in features: parallel (pulls in jobserver and libc, enabling concurrent compiles with jobserver coordination) and jobserver (a placeholder retained for backwards compatibility — it never enabled parallelism). No default features. No build.rs. No proc-macro target. The published .crate excludes the in-VCS tests/, src/bin/, .github/, and the workspace's dev-tools/ and find-msvc-tools/ members.
Methodology
The published crate contents were compared against the upstream Git repository at the commit recorded in .cargo_vcs_info.json using diff (GNU diffutils 3.10). The diff is what cargo's normaliser produces (reordered keys, [lib] block inserted, [dev-dependencies] / [workspace] / [patch.crates-io] stripped from the published manifest); no code-file divergence. The published archive carries Cargo.lock — normal for recent cargo, irrelevant for downstream library consumers.
All of src/ (~14.9k LOC across lib.rs, tool.rs, flags.rs, command_helpers.rs, tempfile.rs, utilities.rs, target/*, parallel/*) was inspected. The large lib.rs (4505 lines) was delegated to a Sonnet sub-agent with a structured questionnaire covering process execution, filesystem, environment, network, crypto, unsafe, FFI, concurrency, command-injection, and path-traversal; the smaller modules were read directly. ripgrep (14.1.1) was used to enumerate unsafe, extern "C", Command::new, std::net, std::fs, Mutex/RwLock/Atomic, and the crypto/hash/sign/cipher family. Only the published feature set was reviewed; tests were not executed.
openvet 0.6.0 was used to drive the audit workflow.
Results
The diff between contents and VCS is consistent with cargo normalisation; no unexpected files in the package. The crate ships one .c source: src/detect_compiler_family.c (14 lines, four #pragma message probes for __clang__/__GNUC__/__EMSCRIPTEN__/__VXWORKS__) which tool.rs:detect_family_inner writes into a tempfile and preprocesses to identify the compiler. No pre-compiled binaries, libraries, or archive files (justifying has-binaries). The crate has no build.rs and is not a proc-macro crate — there is no code that runs at the audited crate's own build time on the consumer's machine outside of cargo's normal compilation of the library itself (justifying has-build-exec and has-install-exec).
The crate's purpose is to spawn external compilers, so uses-exec is asserted. All invocations construct std::process::Command and append arguments as individual OsString argv items; no shell is invoked. When CC_SHELL_ESCAPED_FLAGS is set, CFLAGS/CXXFLAGS/ARFLAGS are tokenised with the shlex crate and the tokens become argv items, still without a shell (justifying exec-safe). The executable path comes from env vars (CC, CXX, AR, NVCC, RUSTC_LINKER, …) or Build::compiler(); in normal usage these are end-user or build-script-author controlled, not attacker-controlled.
Filesystem writes are confined to OUT_DIR (object files, the final archive, the compiler-detection temp source); reads cover the build-script-author-supplied source files and a handful of probe paths during cross-compiler discovery. tempfile::NamedTempfile opens with O_CREAT|O_EXCL and unix mode 0o600 / Windows FILE_ATTRIBUTE_TEMPORARY, retrying up to ten times on collision (justifying filesystem-safe, with a quality caveat tracked in FINDING-2). Environment access goes through Build::getenv (cache-aware, emits cargo:rerun-if-env-changed); a project-level clippy lint disallows the raw std::env::* accessors. Reads include the documented Cargo CARGO_* variables and the standard Unix-toolchain set (CC/CXX/*FLAGS, the Apple SDKROOT / *_DEPLOYMENT_TARGET family, WASI/WASM sysroot vars, PATH, CROSS_COMPILE, the cc-specific CC_ENABLE_DEBUG_OUTPUT/CC_SHELL_ESCAPED_FLAGS/CC_FORCE_DISABLE/CC_KNOWN_WRAPPER_CUSTOM/CRATE_CC_NO_DEFAULTS). The crate does not enumerate or exfiltrate the environment, and does not write env vars (justifying environment-safe).
Cryptographic libraries were searched for and none was found; the only "hash" usage is DefaultHasher over a directory name to disambiguate same-basename source files in the object-file cache, and RandomState::new().build_hasher().finish() for tempfile names — neither is a cryptographic primitive (justifying uses-crypto, impl-crypto). Source-tree review found no network APIs (std::net, reqwest, ureq, curl, hyper) and no DNS lookups (justifying uses-network). The crate neither embeds a JIT/interpreter nor implements one (justifying uses-jit, impl-jit, uses-interpreter, impl-interpreter, impl-protocol). It does not implement non-trivial data structures or algorithms (justifying impl-datastructure, impl-algorithm).
The crate implements two non-trivial pieces of functionality. First, impl-parser: flags.rs parses CARGO_ENCODED_RUSTFLAGS (the U+001F-separated rustc args) into a typed RustcCodegenFlags, and target/parser.rs matches rustc target triples against a generated table to recover arch/vendor/os/env/abi. Both are data-driven and panic-free on observed paths (justifying parser-impl-safe). The integration tests in the upstream tests/ directory cover the rustflags and target-info parsing paths (tests/rustflags.rs, plus property-style coverage embedded in tests/test.rs), justifying parser-impl-tested and parser-impl-correct. Second, impl-concurrency: the parallel/ module implements a hand-rolled two-future executor with backoff, a wrapper over the inherited make-jobserver, and an in-process atomic-counter fallback jobserver. The jobserver wrapper uses a Mutex<bool> to coordinate the global implicit token (the author comment explains why an AtomicBool is insufficient). All BuildCache access goes through RwLock<HashMap<...>> shared via Arc. Public concurrency contract is documented through Rust's type system (Send + Sync bounds; the Arc<BuildCache> exposure is pub(crate)). The custom primitives are exercised by the integration suite under parallel but not under loom or ThreadSanitizer, so concurrency-impl-tested is asserted false; concurrency-impl-safe, concurrency-impl-correct, concurrency-impl-documented hold on review (justifying uses-concurrency, concurrency-safe, concurrency-documented).
uses-unsafe is asserted true and the unsafe surface is small and contained: a hand-rolled OnceLock<T> in utilities.rs (replaceable with std::sync::OnceLock once MSRV moves past 1.70), two Pin::new_unchecked calls plus a Waker::from_raw with a noop vtable in the async executor, and three libc FFI calls (fcntl, ioctl(FIONREAD)) plus a Windows PeekNamedPipe call in parallel/stderr.rs. Each block carries a SAFETY-style comment or a justifying author comment (justifying unsafe-documented, unsafe-minimal). Invariants reviewed and hold (justifying unsafe-safe). No targeted miri/sanitizer runs were observed in the workspace, so unsafe-tested is asserted false.
Two low-severity quality findings were recorded: FINDING-1 enumerates unwrap/expect sites that panic on environment-derived inputs instead of returning the crate's Error type, and FINDING-2 documents the non-CSPRNG tempfile naming and the mitigations that make it acceptable.
The package was reviewed for malicious patterns (obfuscated payloads, network exfiltration, target-conditional behaviour, suspicious binaries, typosquatting); none was found (justifying is-benign). Tests live in the upstream VCS but are excluded from the published .crate (justifying has-unit-tests = true and has-integration-tests = true through the in-VCS layout); no fuzz or property tests were observed in the test suite (justifying has-fuzz-tests = false and has-property-tests = false).
Conclusion
cc is a mature, focused build-time helper. Its attack surface — spawning external compilers and managing build outputs under OUT_DIR — is exactly what its purpose demands, and the implementation handles it with discipline: argv-only process construction, a clippy-enforced env-access discipline, exclusive-create tempfiles, and a small contained unsafe surface that is well documented and replaceable as MSRV moves. The two recorded findings are quality-class, low-severity, and do not affect downstream consumers' security posture. No network access, no cryptography, no install/build-time code execution beyond compiling the crate itself.