CVE
Not assigned
CWE
CWE-88, CWE-200, CWE-532, CWE-416, CWE-908, CWE-787, CWE-125, CWE-22, CWE-203
Affected Surface
- Rust `libz-sys` versions `>= 1.0.21, < 1.1.31` when compiled with `cc >= 1.6.0` on Unix (`RUSTSEC-2026-0418` / `GHSA-8p3x-q3q3-q3v5`), `juniper` `< 0.17.2` (`RUSTSEC-2026-0416`, `RUSTSEC-2026-0417`), `juniper_hyper` `< 0.10.1` (`RUSTSEC-2026-0415`), `juniper_warp` `< 0.9.1` (`RUSTSEC-2026-0419`), `nix-nar` `< 0.6.0` (`RUSTSEC-2026-0425`), `orion` `< 0.17.16` and `0.18.0` (`RUSTSEC-2026-0426` / `GHSA-r7wm-4vf2-h44w`, `RUSTSEC-2026-0428` / `GHSA-xcc6-f2jq-43x6`), and `sn-releases` `<= 0.3.1` (`RUSTSEC-2026-0427`)
- Rust `cargo-nextest` versions `>= 0.9.125, < 0.9.150` (`RUSTSEC-2026-0412` / `GHSA-vc25-qg58-w57c`)
- Rust `rustpython-vm` versions `>= 0.3.0, < 0.6.0` (`RUSTSEC-2026-0394`), including any downstream binary compiled in CI with environment secrets present
- Rust / C `rustls-ffi` versions `>= 0.12.0, < 0.15.7` (`RUSTSEC-2026-0413` / `GHSA-rc3f-4j82-cmrg`)
- Rust `zstd` (`< 0.14.3`), `zstd-safe` (`< 8.2.0`), and `zstd-sys` (`>= 2.0.9, < 2.1.2` on `wasm32`) crates (`RUSTSEC-2026-0391` through `RUSTSEC-2026-0406` and `RUSTSEC-2026-0410`)
Update, 30 October 2026: On 29 and 30 October (13:56 to 11:12 UTC), RustSec assigned nine additional crate advisories (RUSTSEC-2026-0415 through RUSTSEC-2026-0419 and RUSTSEC-2026-0425 through RUSTSEC-2026-0428):
libz-sys< 1.1.31(RUSTSEC-2026-0418/GHSA-8p3x-q3q3-q3v5): Directly parallel to therustpython-vmCI secret leak below,libz-sys(1.0.21through1.1.30) executesprintln!("running {:?}", cmd);in itsbuild.rsscript when probing for a systemzlib. Starting withcc 1.6.0(released 3 October 2026),cc::Tool::to_command()clears the child environment and explicitly sets every environment variable on thestd::process::Commandstruct, causingDebugformatting on Unix to print every CI environment variable and secret to build-script stdout (which Cargo saves in cleartext undertarget/debug/build/libz-sys-*/outputand uploads to shared CI caches). Upgradelibz-systo>= 1.1.31(rust-lang/libz-sysPR#280) and purge sharedtarget/caches.juniper< 0.17.2(RUSTSEC-2026-0416/GHSA-34cw-x8g7-rcv5,RUSTSEC-2026-0417/GHSA-36rq-8957-mjrx),juniper_hyper< 0.10.1(RUSTSEC-2026-0415/GHSA-pfrg-qg4f-grmv), andjuniper_warp< 0.9.1(RUSTSEC-2026-0419/GHSA-fj3h-2jc3-973v): Unauthenticated GraphQL queries with deep selection sets or fragment chains overflowed the parser/validator call stack and aborted the server process (0416), 1.2 KB fragment-spread bombs expanded to $10^9$ fields (0417, fixed injuniper 0.17.2via a 64-level depth cap and 15,000-token/field limit in PR#1405), andjuniper_hyper/juniper_warpbuffered unbounded POST bodies before parsing (0415/0419, now capped at 2 MiB by default).orion< 0.17.16and0.18.0(RUSTSEC-2026-0426/GHSA-r7wm-4vf2-h44wandRUSTSEC-2026-0428/GHSA-xcc6-f2jq-43x6): Inorion’s post-quantumML-KEMandML-DSAfield arithmetic (mldsa44/mldsa87onx86_64,i586, ands390x), LLVM compiledconditional_sub_u32()into a secret-dependent conditional branch (0426, PR#689); separately,hazardous::mac::blake2b::Blake2b::reset()reused the initial key length when reset with a different-length key (0428, PR#690). Fixed inorion 0.17.16and0.18.1.nix-nar< 0.6.0(RUSTSEC-2026-0425, CVSS 7.5 High) andsn-releases<= 0.3.1(RUSTSEC-2026-0427, CVSS 8.8 High):nix_nar::Decoder::unpack(< 0.6.0) allowed NAR archive symlink entries and/entry names to write files outside the destination directory, whilesn_releases::SafeReleaseRepoActions::extract_release_archive(<= 0.3.1, unmaintained) joined raw zip entry names ontodest_dir_pathwithout checking for...
TL;DR
On 26 and 27 October 2026, the RustSec Advisory Database assigned identifiers to 19 security advisories across four widely used Rust crates:
cargo-nextest(RUSTSEC-2026-0412/GHSA-vc25-qg58-w57c, CVSS 7.8 High, fixed in0.9.150): Incargo-nextest0.9.125through0.9.149, runningcargo nextest run --rerun(or-R) against a portable recording archive deserializedbuild_scope_args(Vec<String>) verbatim fromrerun-info.jsonand passed those strings directly tocargowithout validation. A crafted recording archive (or a build-scope value starting with-) could inject arbitrary Cargo flags such as--config build.rustc-wrapper="..."to execute arbitrary commands with the developer’s or CI runner’s privileges.rustpython-vm(RUSTSEC-2026-0394, fixed in0.6.0): From0.3.0through0.5.0,crates/vm/build.rsiterated overstd::env::vars_os()during compilation, wrote every environment variable of the build machine into$OUT_DIR/env_vars.rs, and compiled that table unconditionally into_sysconfigdata::build_time_vars. Any binary or library linked againstrustpython-vmin a CI pipeline baked every secret present in the build environment (CARGO_REGISTRY_TOKEN,GITHUB_TOKEN,AWS_SECRET_ACCESS_KEY, signing keys, and API tokens) into its.rodatasection in cleartext, readable viastringsor Python’s_sysconfigdatamodule.rustls-ffi(RUSTSEC-2026-0413/GHSA-rc3f-4j82-cmrg, fixed in0.15.7): Inrustls-ffi0.12.0through0.15.6(the C FFI bindings forrustlsused bycurl, Apachemod_tls, and non-Rust language bindings),rustls_client_connection_new()andrustls_client_connection_new_alpn()converted the caller’sconst char *server_namepointer viaCStr::from_ptr, which inferred an unbounded'staticlifetime and stored a borrowedServerName<'static>pointing into the caller’s buffer instead of copying it. When callers freed or reused theserver_namebuffer after the constructor returned, subsequent TLS handshake processing read freed memory.zstd,zstd-safe, andzstd-sys(RUSTSEC-2026-0391throughRUSTSEC-2026-0406andRUSTSEC-2026-0410, fixed inzstd 0.14.3,zstd-safe 8.2.0, andzstd-sys 2.1.2): Across the core Rust Zstandard bindings (~445M downloads forzstdand ~455M forzstd-safe), 16 advisories fix cross-stream data leakage when reusing aCCtxorDCtxwithEncoder::with_context/Decoder::with_context(RUSTSEC-2026-0391), 32 KiB of uninitialized heap memory written to output streams when a 6-byte corrupt legacy zstd (v0.4tov0.7) frame fails decompression (RUSTSEC-2026-0403andRUSTSEC-2026-0392),Seekable::decompresscallingVec::set_lenwith a wrapped-around length far past the vector’s capacity whenoffsetexceeds the archive size (RUSTSEC-2026-0398), multiple dictionary and multithreaded (zstdmt) use-after-free and heap-overflow bugs, and awasm32C allocator shim that wrote to linear memory address0and returned address4whenallocfailed (RUSTSEC-2026-0410).
What to do now:
- Upgrade
cargo-nextestto0.9.150,rustls-ffito0.15.7,zstdto0.14.3,zstd-safeto8.2.0, andzstd-systo2.1.2. - If you ever built and distributed a binary using
rustpython-vm0.3.0through0.5.0, upgrade to0.6.0, rebuild, and rotate every credential that was present in the build environment when the older binary was compiled.
Affected packages and projects
| Ecosystem | Crate | Advisory ID | Affected versions | Patched version | Vulnerability summary |
|---|---|---|---|---|---|
| crates.io | cargo-nextest | RUSTSEC-2026-0412 (GHSA-vc25-qg58-w57c) | >= 0.9.125, < 0.9.150 | 0.9.150 | Arbitrary Cargo argument injection and code execution via build_scope_args in portable recordings (--rerun / -R) |
| crates.io | rustpython-vm | RUSTSEC-2026-0394 | >= 0.3.0, < 0.6.0 | 0.6.0 | build.rs writes all build-time environment variables (std::env::vars_os()) into $OUT_DIR/env_vars.rs and compiles them into _sysconfigdata |
| crates.io | rustls-ffi | RUSTSEC-2026-0413 (GHSA-rc3f-4j82-cmrg) | >= 0.12.0, < 0.15.7 | 0.15.7 | Use-after-free of caller’s server_name buffer in rustls_client_connection_new and rustls_client_connection_new_alpn |
| crates.io | zstd | RUSTSEC-2026-0391 | >= 0.13.2, < 0.14.3 | 0.14.3 | raw::Encoder::with_context and Decoder::with_context do not reset the session, leaking buffered bytes from an abandoned stream into the next stream |
| crates.io | zstd-safe, zstd | RUSTSEC-2026-0403, RUSTSEC-2026-0392 | zstd-safe >= 4.0.0, < 8.2.0; zstd >= 0.4.22, < 0.8.0 | zstd-safe 8.2.0, zstd 0.8.0+ (with zstd-safe 8.2.0) | Failed streaming decompression of a 6-byte corrupt legacy frame (v0.4 to v0.7) marks the output Vec<u8> as fully written, exposing up to 32 KiB of uninitialized heap memory |
| crates.io | zstd-safe | RUSTSEC-2026-0398 | >= 7.2.2, < 8.2.0 (seekable feature) | 8.2.0 | Seekable::decompress with an offset past the end of data passes a wrapped length from ZSTD_seekable_decompress to Vec::set_len, extending length past capacity |
| crates.io | zstd-sys | RUSTSEC-2026-0410 | >= 2.0.9, < 2.1.2 (wasm32) | 2.1.2 | wasm_shim_alloc does not check for allocation failure or overflow, writing size header to address 0 and returning address 4 on OOM |
| crates.io | zstd-safe, zstd | RUSTSEC-2026-0393, 0395..0397, 0399..0402, 0404..0406 | zstd-safe < 8.2.0; zstd >= 0.1.1, < 0.4.10 | zstd-safe 8.2.0, zstd 0.14.3 | Dictionary/context UAFs (init_using_cdict, decompress_using_dict, zstdmt resets), CCtx::try_clone 512 KiB heap overflow, DCtx::try_clone double free, OutBuffer::around_pos uninitialized read, and train_from_buffer sample-size overflow |
Technical analysis
1. RUSTSEC-2026-0412: cargo-nextest --rerun portable recording argument injection
cargo-nextest 0.9.125 introduced experimental run recordings and portable recordings (.zip archives containing test metadata and outputs that can be shared or downloaded from CI to rerun failed tests locally with cargo nextest run --rerun <archive>).
Before 0.9.150 (commit 456818b35d and commit 59ef7afc78), Nextest stored the original run’s build scope inside the recording’s rerun-info.json as a raw build_scope_args: Vec<String> array and appended those strings directly to the cargo test --no-run command line on rerun. In addition, CargoBuildScopeOptions::to_cli_args() passed flag values as separate arguments (args.extend(["--package", pkg.as_str()])), so even a structured field value starting with - was interpreted by Cargo as a CLI flag.
An attacker who supplied a portable recording archive (for example in a pull request artifact or bug report) could place --config and build.rustc-wrapper="/path/to/payload" inside build_scope_args. As soon as a developer ran cargo nextest run -R recording.zip, Cargo executed the wrapper during test compilation. 0.9.150 bumps the recording store format to 3.0, replaces build_scope_args: Vec<String> with a strongly typed RecordedBuildScope struct in build-scope.json, validates every identifier, and forces CargoCli to format options as single --flag=value tokens (--package=<pkg>, --exclude=<exc>).
2. RUSTSEC-2026-0394: rustpython-vm build.rs compiling every CI environment variable into the binary
In rustpython-vm 0.3.0 through 0.5.0, crates/vm/build.rs generated a Rust source file named env_vars.rs inside Cargo’s $OUT_DIR during compilation and included it directly inside crates/vm/src/stdlib/_sysconfigdata.rs:
--- a/crates/vm/build.rs
+++ b/crates/vm/build.rs
@@ -53,16 +50,6 @@ fn main() {
"cargo:rustc-env=RUSTPYTHON_TARGET_TRIPLE={}",
env::var("TARGET").unwrap()
);
-
- let mut env_path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
- env_path.push("env_vars.rs");
- let mut f = std::fs::File::create(env_path).unwrap();
- write!(
- f,
- "sysvars! {{ {} }}",
- std::env::vars_os().format_with(", ", |(k, v), f| f(&format_args!("{k:?} => {v:?}")))
- )
- .unwrap();
}
--- a/crates/vm/src/stdlib/_sysconfigdata.rs
+++ b/crates/vm/src/stdlib/_sysconfigdata.rs
@@ -69,7 +69,6 @@ mod _sysconfigdata {
"BASECFLAGS" => "",
"BLDSHARED" => "cc -shared",
}
- include!(concat!(env!("OUT_DIR"), "/env_vars.rs"));
vars
}
}
Because std::env::vars_os() returns the entire process environment of the cargo build invocation, any project that depended on rustpython-vm and built release artifacts in GitHub Actions, GitLab CI, or Jenkins embedded every secret present in the runner’s environment (GITHUB_TOKEN, CARGO_REGISTRY_TOKEN, AWS_SECRET_ACCESS_KEY, OPENAI_API_KEY, private signing keys) into the compiled binary. Anyone who downloaded the release binary or container image could extract the full environment with strings or by importing _sysconfigdata inside the embedded Python interpreter. PR #8813 (released in rustpython-vm 0.6.0) deletes the dump and the include!() call.
3. RUSTSEC-2026-0413: unbounded CStr::from_ptr lifetime causing server_name UAF in rustls-ffi
In rustls-ffi 0.12.0 through 0.15.6, rustls_client_connection_new() and rustls_client_connection_new_alpn() in librustls/src/client.rs accepted a C string server_name: *const c_char and converted it into a rustls_pki_types::ServerName:
--- a/librustls/src/client.rs
+++ b/librustls/src/client.rs
@@ -894,12 +891,10 @@ impl rustls_client_config {
alpn_protocols: Vec<Vec<u8>>,
conn_out: *mut *mut rustls_connection,
) -> rustls_result {
- let Some(server_name) = NonNull::new(server_name.cast_mut()) else {
- return rustls_result::NullParameter;
- };
- let server_name = unsafe { CStr::from_ptr(server_name.as_ptr()) };
+ let server_name = try_cstr!(server_name);
let Ok(server_name) = server_name.to_str() else {
return rustls_result::InvalidDnsNameError;
};
- let Ok(server_name) = server_name.try_into() else {
+ let Ok(server_name) = ServerName::try_from(server_name) else {
return rustls_result::InvalidDnsNameError;
};
+ let server_name = server_name.to_owned();
In Rust, unsafe { CStr::from_ptr(ptr) } has signature unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr where the output lifetime 'a is unbound to any input value. Because ClientConnection::new requires a ServerName<'static>, the Rust compiler inferred 'a = 'static on CStr::from_ptr, and server_name.try_into() constructed a borrowed ServerName<'static> pointing directly at the caller’s C char * buffer without allocating a copy. If the C caller or language binding freed server_name after rustls_client_connection_new returned, rustls read freed memory during the TLS handshake. Commit 8749768d6e calls server_name.to_owned() to copy the string into a Rust-owned allocation, and commit b520c8780b binds pointer helper lifetimes so 'static cannot be silently inferred from a raw pointer.
4. RUSTSEC-2026-0391..0406 and RUSTSEC-2026-0410 in zstd, zstd-safe, and zstd-sys
The 16 Zstandard advisories (gyscos/zstd-rs PR #384) close four particularly dangerous bugs in production Rust services:
- Cross-stream data leak on reused contexts (
RUSTSEC-2026-0391, commit74ace2dcdc):zstd::stream::raw::Encoder::with_context(&mut cctx)andDecoder::with_context(&mut dctx)used the borrowedCCtxorDCtxwithout resetting its active session. If a previous request abandoned an encoder before callingfinish()(for example after an I/O error or client disconnect), up to 128 KiB of the previous user’s uncompressed input (or the entire stream whenzstdmtwas enabled) remained buffered inCCtxand was prepended to the next user’s compressed frame.zstd 0.14.3callscontext.reset(ResetDirective::SessionOnly)insidewith_context. - 32 KiB uninitialized heap leak on 6-byte corrupt legacy frames (
RUSTSEC-2026-0403andRUSTSEC-2026-0392, commit006e1d1ad1): Legacy zstd frame support (v0.4tov0.7) is enabled by default. When the bundled C library fails partway through a legacy frame (such as the 6-byte input[0x27, 0xB5, 0x2F, 0xFD, 0x08, 0x00]), the C legacy decoder setsoutput.pos = output.sizebefore returning an error code, claiming the entire output buffer was filled even though zero bytes were written. Becausezstd-safeupdatedVec::set_lenfromoutput.posbefore checking whetherZSTD_decompressStreamreturned an error, up to 32 KiB of uninitialized heap memory was exposed to the caller or flushed to the underlyingWritesink. Vec::set_lenbeyond capacity inSeekable::decompress(RUSTSEC-2026-0398, commit80506be743): In the C seekable extension,ZSTD_seekable_decompressclamps the requested read length using unsigned subtraction (uncompressed_end - offset) without checkingoffset <= uncompressed_end. Passing anoffsetpast the end of the archive caused the C function to return a wrappedsize_twithout writing anything, andWriteBuf::write_frompassed that huge return value straight toVec::set_len, leaving aVec<u8>whose.len()was far larger than its allocated.capacity().- WebAssembly allocator shim writing to address
0on OOM (RUSTSEC-2026-0410, commit165d162555): Onwasm32,zstd-sysprovides custommalloc/calloc/freefunctions backed bystd::alloc. Previously,wasm_shim_allocdid not check whetheralloc(layout)returned a null pointer (0in WASM linear memory): it wrotefull_alloc_sizetoptr(address0) and returnedptr.add(4)(address4), causing a 9-byte zstd frame header requesting a 128 MiB window to overwrite the bottom of the WebAssembly module’s linear memory whenevermemory.growfailed.
Detection and remediation
# 1. Audit Cargo.lock files for vulnerable rustpython-vm, rustls-ffi, zstd, zstd-safe, and zstd-sys versions
rg -n -A 2 'name = "(cargo-nextest|rustpython-vm|rustls-ffi|zstd|zstd-safe|zstd-sys)"' Cargo.lock 2>/dev/null
# 2. Check installed cargo-nextest version on developer workstations and CI runners
cargo nextest --version 2>/dev/null || true
# 3. Inspect any compiled binaries that linked rustpython-vm 0.3.0..0.5.0 for leaked CI environment variables
# (Look for _sysconfigdata keys or known CI variable names in the binary's rodata strings)
strings path/to/binary 2>/dev/null | grep -E 'GITHUB_TOKEN|CARGO_REGISTRY_TOKEN|AWS_SECRET_ACCESS_KEY|ACTIONS_ID_TOKEN' || true
- Update dependencies and tooling: Run
cargo update -p zstd -p zstd-safe -p zstd-sys -p rustls-ffi -p rustpython-vmand upgradecargo-nextestto>= 0.9.150. - Rotate secrets baked into
rustpython-vmbinaries: If any CI workflow built and published a binary, wheel, container image, or WASM bundle withrustpython-vm0.3.0,0.3.1,0.4.0, or0.5.0, assume every environment variable visible to thatcargo buildstep is public and rotate those tokens immediately.
Timeline
- 2026-09-23:
RustPythonmerges PR#8813and publishesrustpython-vm 0.6.0removingenv_vars.rsfrombuild.rs. - 2026-10-16:
gyscos/zstd-rsmerges PR#384and publisheszstd 0.14.3,zstd-safe 8.2.0, andzstd-sys 2.1.2+zstd.1.5.7. - 2026-10-22 to 2026-10-26:
nextest-rs/nextestmerges commits456818b35dand59ef7afc78and releasescargo-nextest 0.9.150;rustls/rustls-ffimerges commits8749768d6eandb520c8780band releasesrustls-ffi 0.15.7. - 2026-10-26 to 2026-10-27: The RustSec Advisory Database assigns
RUSTSEC-2026-0391throughRUSTSEC-2026-0406,RUSTSEC-2026-0410,RUSTSEC-2026-0412(cargo-nextest), andRUSTSEC-2026-0413(rustls-ffi).
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- RustSec Advisory RUSTSEC-2026-0412 / GHSA-vc25-qg58-w57c: cargo-nextest portable recording RCE
- nextest-rs/nextest commits 456818b35d and 59ef7afc78: typed build scope and --flag=value Cargo arguments
- RustSec Advisory RUSTSEC-2026-0394 and RustPython PR #8813: build environment compiled into rustpython-vm
- RustSec Advisory RUSTSEC-2026-0413 / GHSA-rc3f-4j82-cmrg and rustls-ffi commit 8749768d6e
- gyscos/zstd-rs PR #384 and RustSec Advisories RUSTSEC-2026-0391 through RUSTSEC-2026-0406 and RUSTSEC-2026-0410
- rust-lang/libz-sys PR #280 (RUSTSEC-2026-0418 / GHSA-8p3x-q3q3-q3v5) and graphql-rust/juniper PR #1405 (RUSTSEC-2026-0415 to 0419)
- orion-rs/orion PRs #689 and #690 (RUSTSEC-2026-0426 / GHSA-r7wm-4vf2-h44w and RUSTSEC-2026-0428 / GHSA-xcc6-f2jq-43x6)