high

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 the rustpython-vm CI secret leak below, libz-sys (1.0.21 through 1.1.30) executes println!("running {:?}", cmd); in its build.rs script when probing for a system zlib. Starting with cc 1.6.0 (released 3 October 2026), cc::Tool::to_command() clears the child environment and explicitly sets every environment variable on the std::process::Command struct, causing Debug formatting on Unix to print every CI environment variable and secret to build-script stdout (which Cargo saves in cleartext under target/debug/build/libz-sys-*/output and uploads to shared CI caches). Upgrade libz-sys to >= 1.1.31 (rust-lang/libz-sys PR #280) and purge shared target/ 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), and juniper_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 in juniper 0.17.2 via a 64-level depth cap and 15,000-token/field limit in PR #1405), and juniper_hyper / juniper_warp buffered unbounded POST bodies before parsing (0415/0419, now capped at 2 MiB by default).
  • orion < 0.17.16 and 0.18.0 (RUSTSEC-2026-0426 / GHSA-r7wm-4vf2-h44w and RUSTSEC-2026-0428 / GHSA-xcc6-f2jq-43x6): In orion’s post-quantum ML-KEM and ML-DSA field arithmetic (mldsa44 / mldsa87 on x86_64, i586, and s390x), LLVM compiled conditional_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 in orion 0.17.16 and 0.18.1.
  • nix-nar < 0.6.0 (RUSTSEC-2026-0425, CVSS 7.5 High) and sn-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, while sn_releases::SafeReleaseRepoActions::extract_release_archive (<= 0.3.1, unmaintained) joined raw zip entry names onto dest_dir_path without 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:

  1. cargo-nextest (RUSTSEC-2026-0412 / GHSA-vc25-qg58-w57c, CVSS 7.8 High, fixed in 0.9.150): In cargo-nextest 0.9.125 through 0.9.149, running cargo nextest run --rerun (or -R) against a portable recording archive deserialized build_scope_args (Vec<String>) verbatim from rerun-info.json and passed those strings directly to cargo without 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.
  2. rustpython-vm (RUSTSEC-2026-0394, fixed in 0.6.0): From 0.3.0 through 0.5.0, crates/vm/build.rs iterated over std::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 against rustpython-vm in 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 .rodata section in cleartext, readable via strings or Python’s _sysconfigdata module.
  3. rustls-ffi (RUSTSEC-2026-0413 / GHSA-rc3f-4j82-cmrg, fixed in 0.15.7): In rustls-ffi 0.12.0 through 0.15.6 (the C FFI bindings for rustls used by curl, Apache mod_tls, and non-Rust language bindings), rustls_client_connection_new() and rustls_client_connection_new_alpn() converted the caller’s const char *server_name pointer via CStr::from_ptr, which inferred an unbounded 'static lifetime and stored a borrowed ServerName<'static> pointing into the caller’s buffer instead of copying it. When callers freed or reused the server_name buffer after the constructor returned, subsequent TLS handshake processing read freed memory.
  4. zstd, zstd-safe, and zstd-sys (RUSTSEC-2026-0391 through RUSTSEC-2026-0406 and RUSTSEC-2026-0410, fixed in zstd 0.14.3, zstd-safe 8.2.0, and zstd-sys 2.1.2): Across the core Rust Zstandard bindings (~445M downloads for zstd and ~455M for zstd-safe), 16 advisories fix cross-stream data leakage when reusing a CCtx or DCtx with Encoder::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.4 to v0.7) frame fails decompression (RUSTSEC-2026-0403 and RUSTSEC-2026-0392), Seekable::decompress calling Vec::set_len with a wrapped-around length far past the vector’s capacity when offset exceeds the archive size (RUSTSEC-2026-0398), multiple dictionary and multithreaded (zstdmt) use-after-free and heap-overflow bugs, and a wasm32 C allocator shim that wrote to linear memory address 0 and returned address 4 when alloc failed (RUSTSEC-2026-0410).

What to do now:

  1. Upgrade cargo-nextest to 0.9.150, rustls-ffi to 0.15.7, zstd to 0.14.3, zstd-safe to 8.2.0, and zstd-sys to 2.1.2.
  2. If you ever built and distributed a binary using rustpython-vm 0.3.0 through 0.5.0, upgrade to 0.6.0, rebuild, and rotate every credential that was present in the build environment when the older binary was compiled.

Affected packages and projects

EcosystemCrateAdvisory IDAffected versionsPatched versionVulnerability summary
crates.iocargo-nextestRUSTSEC-2026-0412 (GHSA-vc25-qg58-w57c)>= 0.9.125, < 0.9.1500.9.150Arbitrary Cargo argument injection and code execution via build_scope_args in portable recordings (--rerun / -R)
crates.iorustpython-vmRUSTSEC-2026-0394>= 0.3.0, < 0.6.00.6.0build.rs writes all build-time environment variables (std::env::vars_os()) into $OUT_DIR/env_vars.rs and compiles them into _sysconfigdata
crates.iorustls-ffiRUSTSEC-2026-0413 (GHSA-rc3f-4j82-cmrg)>= 0.12.0, < 0.15.70.15.7Use-after-free of caller’s server_name buffer in rustls_client_connection_new and rustls_client_connection_new_alpn
crates.iozstdRUSTSEC-2026-0391>= 0.13.2, < 0.14.30.14.3raw::Encoder::with_context and Decoder::with_context do not reset the session, leaking buffered bytes from an abandoned stream into the next stream
crates.iozstd-safe, zstdRUSTSEC-2026-0403, RUSTSEC-2026-0392zstd-safe >= 4.0.0, < 8.2.0; zstd >= 0.4.22, < 0.8.0zstd-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.iozstd-safeRUSTSEC-2026-0398>= 7.2.2, < 8.2.0 (seekable feature)8.2.0Seekable::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.iozstd-sysRUSTSEC-2026-0410>= 2.0.9, < 2.1.2 (wasm32)2.1.2wasm_shim_alloc does not check for allocation failure or overflow, writing size header to address 0 and returning address 4 on OOM
crates.iozstd-safe, zstdRUSTSEC-2026-0393, 0395..0397, 0399..0402, 0404..0406zstd-safe < 8.2.0; zstd >= 0.1.1, < 0.4.10zstd-safe 8.2.0, zstd 0.14.3Dictionary/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, commit 74ace2dcdc): zstd::stream::raw::Encoder::with_context(&mut cctx) and Decoder::with_context(&mut dctx) used the borrowed CCtx or DCtx without resetting its active session. If a previous request abandoned an encoder before calling finish() (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 when zstdmt was enabled) remained buffered in CCtx and was prepended to the next user’s compressed frame. zstd 0.14.3 calls context.reset(ResetDirective::SessionOnly) inside with_context.
  • 32 KiB uninitialized heap leak on 6-byte corrupt legacy frames (RUSTSEC-2026-0403 and RUSTSEC-2026-0392, commit 006e1d1ad1): Legacy zstd frame support (v0.4 to v0.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 sets output.pos = output.size before returning an error code, claiming the entire output buffer was filled even though zero bytes were written. Because zstd-safe updated Vec::set_len from output.pos before checking whether ZSTD_decompressStream returned an error, up to 32 KiB of uninitialized heap memory was exposed to the caller or flushed to the underlying Write sink.
  • Vec::set_len beyond capacity in Seekable::decompress (RUSTSEC-2026-0398, commit 80506be743): In the C seekable extension, ZSTD_seekable_decompress clamps the requested read length using unsigned subtraction (uncompressed_end - offset) without checking offset <= uncompressed_end. Passing an offset past the end of the archive caused the C function to return a wrapped size_t without writing anything, and WriteBuf::write_from passed that huge return value straight to Vec::set_len, leaving a Vec<u8> whose .len() was far larger than its allocated .capacity().
  • WebAssembly allocator shim writing to address 0 on OOM (RUSTSEC-2026-0410, commit 165d162555): On wasm32, zstd-sys provides custom malloc/calloc/free functions backed by std::alloc. Previously, wasm_shim_alloc did not check whether alloc(layout) returned a null pointer (0 in WASM linear memory): it wrote full_alloc_size to ptr (address 0) and returned ptr.add(4) (address 4), causing a 9-byte zstd frame header requesting a 128 MiB window to overwrite the bottom of the WebAssembly module’s linear memory whenever memory.grow failed.

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
  1. Update dependencies and tooling: Run cargo update -p zstd -p zstd-safe -p zstd-sys -p rustls-ffi -p rustpython-vm and upgrade cargo-nextest to >= 0.9.150.
  2. Rotate secrets baked into rustpython-vm binaries: If any CI workflow built and published a binary, wheel, container image, or WASM bundle with rustpython-vm 0.3.0, 0.3.1, 0.4.0, or 0.5.0, assume every environment variable visible to that cargo build step is public and rotate those tokens immediately.

Timeline

  • 2026-09-23: RustPython merges PR #8813 and publishes rustpython-vm 0.6.0 removing env_vars.rs from build.rs.
  • 2026-10-16: gyscos/zstd-rs merges PR #384 and publishes zstd 0.14.3, zstd-safe 8.2.0, and zstd-sys 2.1.2+zstd.1.5.7.
  • 2026-10-22 to 2026-10-26: nextest-rs/nextest merges commits 456818b35d and 59ef7afc78 and releases cargo-nextest 0.9.150; rustls/rustls-ffi merges commits 8749768d6e and b520c8780b and releases rustls-ffi 0.15.7.
  • 2026-10-26 to 2026-10-27: The RustSec Advisory Database assigns RUSTSEC-2026-0391 through RUSTSEC-2026-0406, RUSTSEC-2026-0410, RUSTSEC-2026-0412 (cargo-nextest), and RUSTSEC-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