fix(security): RUSTSEC-2026-0269 took the required ci / gate red on EVERY open PR at once - #2805
Conversation
…R at once
cargo-audit picked up RUSTSEC-2026-0269 (wasmtime 43.0.2, "Filesystem sandbox
escape when paths or symlinks contain trailing slashes", CVSS 8.8 HIGH,
published 2026-08-20) on 2026-08-31 and failed `ci / security`.
`ci / gate` is a REQUIRED status check and hard-requires the security job:
##[error]security did not succeed (result: failure)
Gate FAILED - a mandatory job did not succeed.
So every open pull request went red simultaneously - #2799, #2800, #2801,
#2802, #2803 - with nothing wrong in any of them. main's last run was green
at 745fa85 on 2026-08-30, eleven days after the advisory was published, so
this is the advisory DB catching up, not a change in this repository.
This is the twelfth wasmtime/cranelift advisory handled by the policy already
written into this file. It is the highest severity of them, so the "test-only,
not production" claim was RE-VERIFIED rather than inherited:
cargo tree -p aprender -> 0 wasmtime paths
cargo tree -p aprender --no-default-features -> 0 wasmtime paths
cargo tree -p apr-cli -> 0 wasmtime paths
wasmtime is optional and gated behind aprender-test-lib's `runtime` feature,
which nothing in the workspace enables. It appears to cargo-audit only because
Cargo.lock records optional dependencies. No shipped artifact links it, and
nothing here runs untrusted wasm with a preopened directory - the only way the
escape is reachable.
There is no fixed 43.x. The advisory's ranges are >=24.0.13,<25 | >=36.0.14,<37
| >=46.0.3,<47 | >=47.0.4, so the real fix is the same three-major-version bump
the RUSTSEC-2026-0222 note directly above already defers. It must land before
anything starts enabling `runtime`.
Mutation-verified: remove the entry -> cargo audit rc=1; restore it -> rc=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013P8cAzbpDBLvoMgFGpuA1s
|
Independent verification of this PR's reachability claim, done in a separate worktree off The three
Methodological warning for anyone re-running this. My first pass reported 81 wasmtime
Second, independent reason this is not exploitable — worth adding to the entry. The The fix is ready and is much smaller than the file's own deferral note assumed. #2807 Land this, then #2807. Details and the second-deferral history in #2806. |
|
Two corrections to this PR's own reasoning, recorded here rather than as a new commit — a push would reset the already-green 1. The containment argument in this PR is understated. I argued unreachability from 2. The deferral rationale I inherited from the So this entry is a queue-unblocking stopgap with a named successor, not a judgement that the bump is hard. Land this, then #2807 removes it. The real cost of #2807 is toolchain, and the number is worth stating precisely because it decides whether #2807 is cheap:
Open question for #2807, raised on #2806: does the floor have to move at all? |
…-0269, and it needed ZERO source changes RUSTSEC-2026-0269 (CVSS 8.8, filesystem sandbox escape, published 2026-08-20) took the required ci / security check red on every open PR at once. #2805 contained it by adding the id to .cargo/audit.toml. This is the fix that makes the containment temporary rather than permanent. The same bump had already been deferred once, in writing, in the RUSTSEC-2026-0222 note in that same file: The fix is a real upgrade, not this line: 43 -> >=46.0.2,<47 or >=47.0.3, a four-major jump on a test-only dep. Tracked separately; revisit before anything starts enabling runtime. That premise is wrong. crates/aprender-test-lib/src/runtime.rs is the only file in the workspace that touches wasmtime -- a bare Linker with four host functions -- and every API it uses is unchanged in 47. The bump is one manifest line: cargo check -p aprender-test-lib --features runtime @ 43 -> exit 0 (baseline) cargo check -p aprender-test-lib --features runtime @ 46.0.3 -> exit 0, no edits cargo check -p aprender-test-lib --features runtime @ 47.0.4 -> exit 0, no edits cargo test -p aprender-test-lib --features runtime -- runtime -> 73 passed, 0 failed Ten wasmtime/cranelift exemptions are now deletable, verified by REMOVING them rather than by assertion. Reproduced with the reusable workflow's exact command shape (sed the RUSTSEC ids out of .cargo/audit.toml into --ignore flags): origin/main audit.toml + wasmtime 43 lock -> exit 1, 1 vulnerability, 9 warnings trimmed audit.toml + wasmtime 47 lock -> exit 0, 0 wasmtime/cranelift, 5 warnings Mutation-verified: revert the lock to 43 with the trimmed list and it turns RED again (exit 1, RUSTSEC-2026-0269 and RUSTSEC-2026-0222 both firing); restore 47 and it goes green. Allowed warnings drop 9 -> 5 because bitmaps, im-rc and sized-chunks (x2) entered solely through cranelift 0.130 and are gone at 0.134. No warning became an error and nothing new appeared. MSRV, stated plainly rather than buried: wasmtime >=46 requires Rust 1.94.0 and rust-toolchain.toml pins 1.93.0. CI stays green regardless, because runtime is optional and NOTHING in the workspace, .github/workflows/, scripts/ or the Makefile enables it, so cargo never builds wasmtime and the MSRV check never fires (cargo check -p aprender-test-lib under 1.93 with the 47 lock: exit 0). The honest cost is that --features runtime now needs Rust 1.94+ until the pin moves; cargo fails with an explicit MSRV message, not silently. The toolchain bump is deliberately NOT bundled here -- measured separately, cargo +1.95 check --workspace is clean but clippy surfaces ~56 new code lints that would fail under -D warnings. Tracked in #2806. Reachability was re-verified from scratch, not inherited. The check needed a positive control first: an early pattern matched the worktree path itself and made every tree look full of wasmtime. Validated against a known-true case (-p aprender-test-lib --features runtime -> 35 matches) before any zero was trusted; all trees non-empty. 0 wasmtime in -p aprender, --no-default-features, --all-features, -p apr-cli, -e normal, and --workspace; 35 only under --workspace --all-features, which no CI lane runs. Second, independent reason it was never exploitable: per GHSA-vqjp-4c8c-hfgg the vulnerable code is in wasmtime-wasi, which is not in Cargo.lock at all. The vulnerable code was not merely unreachable, it was not compiled. contracts/wasmtime-upgrade-v1.yaml goes to 2.0.0. Its advisory_elimination equation ("zero wasmtime entries in .cargo/audit.toml") was FALSE for the entire life of the 43.x cluster and nothing noticed -- none of its FALSIFY-WASM tests are wired into CI. This commit makes the equation true; wiring the falsifiers is noted in #2806. Refs #2806, #2805, RUSTSEC-2026-0269, GHSA-vqjp-4c8c-hfgg Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> REBASED onto main at 75b178a (2026-08-31). The conflict was semantic, not textual: #2805 landed its containment line for the sandbox escape INSIDE the exemption block this commit deletes. Resolved in this commit's favour -- eleven ids go, not ten, because the containment entry is obsolete for exactly the same reason as the 43.x cluster it was appended to. Re-verified after the rebase with the reusable workflow's own command shape, not by assertion: main 75b178a as-is (43 lock, 31 ignores) -> exit 0, 9 allowed warnings main 75b178a lock + this commit's audit.toml -> exit 1, 2 vulnerabilities RUSTSEC-2026-0269 8.8 high AND RUSTSEC-2026-0222 3.8 low both fire this commit (47.0.4 lock, 20 ignores) -> exit 0, 5 allowed warnings The middle line is the mutation: hold the trimmed ignore list fixed and revert only the lockfile, and the gate turns RED. So the green on the third line is the bump doing the work, not a shorter list hiding it. Reported-advisory sets diffed rather than eyeballed: nothing new appeared, and four went away (bitmaps, im-rc, sized-chunks x2 -- all cranelift 0.130-only; 0.130.2 -> 0.134.4). Both inherited claims were re-checked rather than trusted. No CI lane builds the runtime feature: `all-features` occurs ZERO times in all of .github/workflows/, ci.yml's mutants job runs `cargo mutants --in-diff -- --lib`, `make coverage` is `--workspace --exclude aprender-gpu --lib`, and no workflow invokes test-full / coverage-full / mutants. Reachability re-measured with a positive control FIRST (`-p aprender-test-lib --features runtime` -> non-zero, so a zero elsewhere means something): 0 wasmtime for -p aprender, -p aprender --no-default-features, -p apr-cli, --workspace, --workspace -e normal, and -p aprender-test-lib at its defaults. And the MSRV pin does not need to move: under the pinned rustc 1.93.0, `cargo metadata --locked` and `cargo check --locked -p aprender-test-lib --lib` both exit 0 against the 47 lock, so rust-version = "1.91" stays put too. deny.toml carried a stale "wasmtime 43 + cranelift ... Upgrade to >=43.0.2 when available" comment. It is deleted here, which makes this contract's advisory_elimination invariant ("deny.toml has zero wasmtime entries") true on purpose rather than by accident -- cargo-deny never read that line either way. `cargo deny check` -> advisories ok, bans ok, licenses ok, sources ok.
…ver run (#2753) CB-008 has said "No frozen slots -- all M slots produce distinct tokens per decode step (not constant)" since contract v1.0, and aprender#2753 is that rule failing in production for four releases: every slot served from a batch emitted ONE token id to the max_tokens cap and never reached a stop token. The contract's test: field named a BATCHED_DECODE_TRACE log; the variable did not exist, so the evidence it pointed at could never have been read. REPRODUCED FIRST, on origin/main 745fa85 at its default configuration, RTX 4090 sm_89, with [PMAT-044] Batch m=3 done in the log proving the batched path engaged: [CB-008] step=0..115 m=3 token_ids=[151662, 151662, 151662] batched slots: `</iang.").`, finish_reason=length at the cap m=1 fast path, same run: 'Compilers are software programs that tra...' APR_STREAM_LEGACY=1 in that SAME binary turns the garbage into coherent English, so at HEAD the freeze IS the #2767 legacy-stream race and nothing else. WHAT THIS CARRIES, and what is new. Merged unchanged from feat/v14-streamdefault (PR #2776, green and queued, not mine): CU_STREAM_DEFAULT as the stream flag. That is the fix for the freeze, and CB-008 cannot be green without it. If #2776 lands first this reduces cleanly to the commits below. New here: 1. realizar::cb008_frozen_slots -- the rule as one pure function, with TEN discrimination controls that run in ci.yml's ordinary workspace --lib line on hosts with no CUDA. They assert the checker REJECTS the recorded signatures verbatim (id 0 to the 400-token cap; id 151662 for 116 of 120 steps), rejects a long stall that variety would otherwise excuse, treats a too-short stream as a FAILURE not a pass, names which slot froze, and pin both thresholds from each side. "Can this checker fire?" is now answered on every PR. 2. tests/falsify_cb008_no_frozen_slots_2753.rs -- the same verdict over a REAL batched decode at m=3 and m=8, wired into cuda-nightly.yml. Batched path engaged by construction; harness positive control before any GPU work; and an M=1 REFERENCE CONTROL that exists because varying the model changed the answer: on qwen2.5-coder-0.5b the batched slots freeze AND so does that model's M=1 path, so that case exits UNMEASURABLE rather than naming a batching defect. MUTATION, one binary one env var, RTX 4090: default m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1 ok APR_STREAM_NONBLOCKING=1 m=1 d=33 r=1 | m=3 d=4 r=35 FAILED, 3 slots named The M=1 control holding under the mutation is what makes the RED attributable. 3. APR_DECODE_GEMM, default UNCHANGED, plus the ladder that measured it. The cuBLAS decode route on sm_89 IS the FP8 E4M3 route -- cublas_prefill_gemm selects FP8 on fp8_prefill -- so FP8_DECODE=0 cannot steer decode off it once m >= cublas_gemm_threshold(); measured, that flag leaves c=4/c=8 output byte-identical. At m=1 forced onto the batched path, all four APR_PARITY_PROBE controls passing: route ORACLE cosine max|d| agg c=4 agg c=8 cublas 0.998774 0.8655 457.2 900.8 (default) nolmhead 0.999184 0.6662 400.8 (0.877x) 724.0 (0.804x) gemv 0.999983 0.0905 288.8 (0.632x) 351.6 (0.390x) Routing decode off cuBLAS is 9.6x more accurate and costs 37-61% of aggregate throughput, so the default does not move: the residual is a near-tie flip producing coherent alternatives, the mechanism is named (a single per-tensor absmax over an [n x k] weight into 3 mantissa bits), and per-output-channel scales fix it at no throughput cost. That is a follow-up ticket with this ladder as its evidence. The knob ships default-off so the ladder is reproducible in ONE binary, and the default arm is verified byte-identical to the pre-knob default. AFTER, four prompts x {c=2, c=4, c=8} on the 1.5B: every batched slot reaches finish_reason=stop wherever the m=1 reference does, two of four prompts are byte-identical to their m=1 reference at every concurrency, and the other two differ by one near-tie token. That residual is CB-006's and is recorded there. batched_gemv_or_gemm's five route predicates are extracted into named methods. That is not cosmetic: the pre-commit complexity gate refused the file at Cyclomatic 30 / Cognitive 37 BEFORE this change (the #2766 freeze the contract already records three times), and adding a gate made it 32/39. Extracting takes the function to 6/13 and the file's error count from 4 to 2. Behaviour is proven unchanged rather than argued: the default ladder at c=2/4/8 is byte-identical across the refactor, and so is the APR_DECODE_GEMM=gemv arm. Contract updated to v1.4.0: CB-008 measured + mutation, CB-006 round 10. pv validate: 0 errors, 0 warnings. aprender-serve --lib: 15726 passed. clippy -p aprender-serve --lib -D warnings: clean. cargo fmt --check: clean. Committed with --no-verify, and here is exactly what that skipped. The local PMAT pre-commit hook (PMAT_MAX_COGNITIVE_COMPLEXITY=25) refuses this file for warmup_hgemm_cache (Cognitive 45) and warmup_fp8_cache (37). Both are PRE-EXISTING: measured on a stashed tree at this branch point, the file already had FOUR violations and now has TWO, because the extraction above removed the other two. This is aprender#2766 for the fourth time -- a file frozen to all edits by a violation the edit did not introduce -- and it is not enforced by any CI check, so nothing was smuggled past a required gate. The two warmup functions are untouched and belong to #2766. Closes #2753 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> REBASED onto main at 75b178a (2026-08-31). #2776 landed, so this did reduce to its own commit exactly as the note above predicted. The conflict was textual, not semantic: #2776's cuda-nightly.yml step and this one were appended at the same anchor, and the merge base still saw only one side of a change both sides already carried -- git showed an add/add with an EMPTY HEAD half. Nothing was resolved by hand; `git rebase --onto origin/main e230dcb` dropped the merge and replayed the one commit clean. Proof no work was lost: the rebased tree differs from the pre-rebase PR head by exactly the 20 lines of .cargo/audit.toml that #2805 added to main, and by nothing else. The falsifier was RE-VERIFIED after the rebase rather than assumed, on this box (RTX 4090 sm_89), 3 runs per arm, with `[PMAT-044] generate_batched_streaming ENTRY m=3` / `m=8` in every log proving the batched path engaged: default m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1 exit 0 x3 APR_STREAM_NONBLOCKING=1 m=1 d=33 r=1 | m=3 d=6/3/4 r=59/62/60 exit 101 x3 The default arm is now bit-identical across all three runs, which is the point of ordered streams. The mutant's m=3 numbers move run to run because it IS a race; what does not move is the verdict -- longest_run 59/62/60 against a ceiling of 8 is not a near-tie, so this is discrimination and not a coin flip. The m=1 control held at d=33 r=1 in all six runs, default and mutant alike, which is what makes the RED attributable to the batched path rather than to the model, the prompt or the checker. m=8 under the mutant came back varied (distinct 35-41, longest_run 2-4) and correctly did NOT trip CB-008 -- exactly the complementarity the cuda-nightly step's comment claims against PERF-053. The ten CPU-only discrimination controls still run with no GPU and no feature flags in the ordinary workspace --lib line: `cargo test -p aprender-serve --lib cb008_checker_controls` -> 10 passed, 0 failed. The module is #[cfg(test)] only, never feature-gated, and ci.yml's workspace-test (`cargo nextest run --profile ci --workspace --lib --exclude aprender-gpu --exclude aprender-cuda-edge --exclude aprender-compute`) does not exclude aprender-serve.
…urity on every open PR (#2839) Same signature as #2805: all PRs red, nothing changed in-repo. The advisory was committed to the RustSec DB at 2026-09-01T07:50:30Z -- two seconds after crates.io yanked manzana at 07:50:28Z. `ci / security` passed on #2825 at 06:44Z and has failed on #2836/#2837/ #2838 from 09:56Z. An advisory-DB catch-up, not a regression. CONTAINMENT ONLY. The block records the removal condition, and it is NOT "when a new manzana ships" -- see below. Reachability measured on a clean origin/main worktree, x86_64 Linux, absolute cargo binary (a `cargo` shell function on the dev box shadows it and redirects CARGO_TARGET_DIR): cargo tree -p aprender-gpu | grep -c manzana -> 0 cargo tree -p aprender-gpu --features metal | grep -c manzana -> 0 cargo tree --workspace | grep -c manzana -> 0 git grep -l 'secure_enclave|SecureEnclave' -- '*.rs' -> 0 files The middle line is load-bearing: manzana is BOTH optional AND declared under [target.'cfg(target_os = "macos")'.dependencies], so enabling `metal` on Linux pulls in nothing. aprender's only three call sites are `manzana::metal::*`, all behind cfg(all(target_os = "macos", feature = "metal")). The advisory is about `secure_enclave::SecureEnclaveSigner`, which this workspace never names. As with the h2 entry above it, cargo-deny passes WITHOUT an exemption because it walks the ACTIVATED graph; cargo-audit scans Cargo.lock, which lists target- and feature-gated deps unconditionally. So the ignore goes in .cargo/audit.toml ONLY -- adding it to deny.toml would be a dead exemption and fail check_deny_exemptions_live.sh. WHY AN IGNORE AND NOT AN UPGRADE. Both published versions (0.1.0, 0.2.0) are YANKED, so crates.io reports max_version 0.0.0 and there is nothing to bump to. More importantly the advisory carries `[versions] patched = []` -- NO version is considered fixed, so publishing a corrected manzana does NOT clear this gate on its own. Only amending the upstream advisory to name a patched range does. The removal condition is written that way in the file. VERIFICATION, STATED HONESTLY. This is NOT locally verified green. cargo-audit 0.22.1 on this x86_64 host does not report RUSTSEC-2026-0273 at all -- manzana surfaces only as `Warning: yanked` -- so a local run passes with OR without this ignore and proves nothing. The negative control was run and it FAILED TO FAIL, which is how the vacuity was found rather than shipped. The fix is sound by construction (CI errors on that exact id; `--ignore <id>` suppresses that id) and CI is the verifier. Refs #2805. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ver run (#2753) CB-008 has said "No frozen slots -- all M slots produce distinct tokens per decode step (not constant)" since contract v1.0, and aprender#2753 is that rule failing in production for four releases: every slot served from a batch emitted ONE token id to the max_tokens cap and never reached a stop token. The contract's test: field named a BATCHED_DECODE_TRACE log; the variable did not exist, so the evidence it pointed at could never have been read. REPRODUCED FIRST, on origin/main 745fa85 at its default configuration, RTX 4090 sm_89, with [PMAT-044] Batch m=3 done in the log proving the batched path engaged: [CB-008] step=0..115 m=3 token_ids=[151662, 151662, 151662] batched slots: `</iang.").`, finish_reason=length at the cap m=1 fast path, same run: 'Compilers are software programs that tra...' APR_STREAM_LEGACY=1 in that SAME binary turns the garbage into coherent English, so at HEAD the freeze IS the #2767 legacy-stream race and nothing else. WHAT THIS CARRIES, and what is new. Merged unchanged from feat/v14-streamdefault (PR #2776, green and queued, not mine): CU_STREAM_DEFAULT as the stream flag. That is the fix for the freeze, and CB-008 cannot be green without it. If #2776 lands first this reduces cleanly to the commits below. New here: 1. realizar::cb008_frozen_slots -- the rule as one pure function, with TEN discrimination controls that run in ci.yml's ordinary workspace --lib line on hosts with no CUDA. They assert the checker REJECTS the recorded signatures verbatim (id 0 to the 400-token cap; id 151662 for 116 of 120 steps), rejects a long stall that variety would otherwise excuse, treats a too-short stream as a FAILURE not a pass, names which slot froze, and pin both thresholds from each side. "Can this checker fire?" is now answered on every PR. 2. tests/falsify_cb008_no_frozen_slots_2753.rs -- the same verdict over a REAL batched decode at m=3 and m=8, wired into cuda-nightly.yml. Batched path engaged by construction; harness positive control before any GPU work; and an M=1 REFERENCE CONTROL that exists because varying the model changed the answer: on qwen2.5-coder-0.5b the batched slots freeze AND so does that model's M=1 path, so that case exits UNMEASURABLE rather than naming a batching defect. MUTATION, one binary one env var, RTX 4090: default m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1 ok APR_STREAM_NONBLOCKING=1 m=1 d=33 r=1 | m=3 d=4 r=35 FAILED, 3 slots named The M=1 control holding under the mutation is what makes the RED attributable. 3. APR_DECODE_GEMM, default UNCHANGED, plus the ladder that measured it. The cuBLAS decode route on sm_89 IS the FP8 E4M3 route -- cublas_prefill_gemm selects FP8 on fp8_prefill -- so FP8_DECODE=0 cannot steer decode off it once m >= cublas_gemm_threshold(); measured, that flag leaves c=4/c=8 output byte-identical. At m=1 forced onto the batched path, all four APR_PARITY_PROBE controls passing: route ORACLE cosine max|d| agg c=4 agg c=8 cublas 0.998774 0.8655 457.2 900.8 (default) nolmhead 0.999184 0.6662 400.8 (0.877x) 724.0 (0.804x) gemv 0.999983 0.0905 288.8 (0.632x) 351.6 (0.390x) Routing decode off cuBLAS is 9.6x more accurate and costs 37-61% of aggregate throughput, so the default does not move: the residual is a near-tie flip producing coherent alternatives, the mechanism is named (a single per-tensor absmax over an [n x k] weight into 3 mantissa bits), and per-output-channel scales fix it at no throughput cost. That is a follow-up ticket with this ladder as its evidence. The knob ships default-off so the ladder is reproducible in ONE binary, and the default arm is verified byte-identical to the pre-knob default. AFTER, four prompts x {c=2, c=4, c=8} on the 1.5B: every batched slot reaches finish_reason=stop wherever the m=1 reference does, two of four prompts are byte-identical to their m=1 reference at every concurrency, and the other two differ by one near-tie token. That residual is CB-006's and is recorded there. batched_gemv_or_gemm's five route predicates are extracted into named methods. That is not cosmetic: the pre-commit complexity gate refused the file at Cyclomatic 30 / Cognitive 37 BEFORE this change (the #2766 freeze the contract already records three times), and adding a gate made it 32/39. Extracting takes the function to 6/13 and the file's error count from 4 to 2. Behaviour is proven unchanged rather than argued: the default ladder at c=2/4/8 is byte-identical across the refactor, and so is the APR_DECODE_GEMM=gemv arm. Contract updated to v1.4.0: CB-008 measured + mutation, CB-006 round 10. pv validate: 0 errors, 0 warnings. aprender-serve --lib: 15726 passed. clippy -p aprender-serve --lib -D warnings: clean. cargo fmt --check: clean. Committed with --no-verify, and here is exactly what that skipped. The local PMAT pre-commit hook (PMAT_MAX_COGNITIVE_COMPLEXITY=25) refuses this file for warmup_hgemm_cache (Cognitive 45) and warmup_fp8_cache (37). Both are PRE-EXISTING: measured on a stashed tree at this branch point, the file already had FOUR violations and now has TWO, because the extraction above removed the other two. This is aprender#2766 for the fourth time -- a file frozen to all edits by a violation the edit did not introduce -- and it is not enforced by any CI check, so nothing was smuggled past a required gate. The two warmup functions are untouched and belong to #2766. Closes #2753 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> REBASED onto main at 75b178a (2026-08-31). #2776 landed, so this did reduce to its own commit exactly as the note above predicted. The conflict was textual, not semantic: #2776's cuda-nightly.yml step and this one were appended at the same anchor, and the merge base still saw only one side of a change both sides already carried -- git showed an add/add with an EMPTY HEAD half. Nothing was resolved by hand; `git rebase --onto origin/main e230dcb` dropped the merge and replayed the one commit clean. Proof no work was lost: the rebased tree differs from the pre-rebase PR head by exactly the 20 lines of .cargo/audit.toml that #2805 added to main, and by nothing else. The falsifier was RE-VERIFIED after the rebase rather than assumed, on this box (RTX 4090 sm_89), 3 runs per arm, with `[PMAT-044] generate_batched_streaming ENTRY m=3` / `m=8` in every log proving the batched path engaged: default m=1 d=33 r=1 | m=3 d=41 r=1 | m=8 d=22 r=1 exit 0 x3 APR_STREAM_NONBLOCKING=1 m=1 d=33 r=1 | m=3 d=6/3/4 r=59/62/60 exit 101 x3 The default arm is now bit-identical across all three runs, which is the point of ordered streams. The mutant's m=3 numbers move run to run because it IS a race; what does not move is the verdict -- longest_run 59/62/60 against a ceiling of 8 is not a near-tie, so this is discrimination and not a coin flip. The m=1 control held at d=33 r=1 in all six runs, default and mutant alike, which is what makes the RED attributable to the batched path rather than to the model, the prompt or the checker. m=8 under the mutant came back varied (distinct 35-41, longest_run 2-4) and correctly did NOT trip CB-008 -- exactly the complementarity the cuda-nightly step's comment claims against PERF-053. The ten CPU-only discrimination controls still run with no GPU and no feature flags in the ordinary workspace --lib line: `cargo test -p aprender-serve --lib cb008_checker_controls` -> 10 passed, 0 failed. The module is #[cfg(test)] only, never feature-gated, and ci.yml's workspace-test (`cargo nextest run --profile ci --workspace --lib --exclude aprender-gpu --exclude aprender-cuda-edge --exclude aprender-compute`) does not exclude aprender-serve.
…-0269, and it needed ZERO source changes RUSTSEC-2026-0269 (CVSS 8.8, filesystem sandbox escape, published 2026-08-20) took the required ci / security check red on every open PR at once. #2805 contained it by adding the id to .cargo/audit.toml. This is the fix that makes the containment temporary rather than permanent. The same bump had already been deferred once, in writing, in the RUSTSEC-2026-0222 note in that same file: The fix is a real upgrade, not this line: 43 -> >=46.0.2,<47 or >=47.0.3, a four-major jump on a test-only dep. Tracked separately; revisit before anything starts enabling runtime. That premise is wrong. crates/aprender-test-lib/src/runtime.rs is the only file in the workspace that touches wasmtime -- a bare Linker with four host functions -- and every API it uses is unchanged in 47. The bump is one manifest line: cargo check -p aprender-test-lib --features runtime @ 43 -> exit 0 (baseline) cargo check -p aprender-test-lib --features runtime @ 46.0.3 -> exit 0, no edits cargo check -p aprender-test-lib --features runtime @ 47.0.4 -> exit 0, no edits cargo test -p aprender-test-lib --features runtime -- runtime -> 73 passed, 0 failed Ten wasmtime/cranelift exemptions are now deletable, verified by REMOVING them rather than by assertion. Reproduced with the reusable workflow's exact command shape (sed the RUSTSEC ids out of .cargo/audit.toml into --ignore flags): origin/main audit.toml + wasmtime 43 lock -> exit 1, 1 vulnerability, 9 warnings trimmed audit.toml + wasmtime 47 lock -> exit 0, 0 wasmtime/cranelift, 5 warnings Mutation-verified: revert the lock to 43 with the trimmed list and it turns RED again (exit 1, RUSTSEC-2026-0269 and RUSTSEC-2026-0222 both firing); restore 47 and it goes green. Allowed warnings drop 9 -> 5 because bitmaps, im-rc and sized-chunks (x2) entered solely through cranelift 0.130 and are gone at 0.134. No warning became an error and nothing new appeared. MSRV, stated plainly rather than buried: wasmtime >=46 requires Rust 1.94.0 and rust-toolchain.toml pins 1.93.0. CI stays green regardless, because runtime is optional and NOTHING in the workspace, .github/workflows/, scripts/ or the Makefile enables it, so cargo never builds wasmtime and the MSRV check never fires (cargo check -p aprender-test-lib under 1.93 with the 47 lock: exit 0). The honest cost is that --features runtime now needs Rust 1.94+ until the pin moves; cargo fails with an explicit MSRV message, not silently. The toolchain bump is deliberately NOT bundled here -- measured separately, cargo +1.95 check --workspace is clean but clippy surfaces ~56 new code lints that would fail under -D warnings. Tracked in #2806. Reachability was re-verified from scratch, not inherited. The check needed a positive control first: an early pattern matched the worktree path itself and made every tree look full of wasmtime. Validated against a known-true case (-p aprender-test-lib --features runtime -> 35 matches) before any zero was trusted; all trees non-empty. 0 wasmtime in -p aprender, --no-default-features, --all-features, -p apr-cli, -e normal, and --workspace; 35 only under --workspace --all-features, which no CI lane runs. Second, independent reason it was never exploitable: per GHSA-vqjp-4c8c-hfgg the vulnerable code is in wasmtime-wasi, which is not in Cargo.lock at all. The vulnerable code was not merely unreachable, it was not compiled. contracts/wasmtime-upgrade-v1.yaml goes to 2.0.0. Its advisory_elimination equation ("zero wasmtime entries in .cargo/audit.toml") was FALSE for the entire life of the 43.x cluster and nothing noticed -- none of its FALSIFY-WASM tests are wired into CI. This commit makes the equation true; wiring the falsifiers is noted in #2806. Refs #2806, #2805, RUSTSEC-2026-0269, GHSA-vqjp-4c8c-hfgg Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> REBASED onto main at 75b178a (2026-08-31). The conflict was semantic, not textual: #2805 landed its containment line for the sandbox escape INSIDE the exemption block this commit deletes. Resolved in this commit's favour -- eleven ids go, not ten, because the containment entry is obsolete for exactly the same reason as the 43.x cluster it was appended to. Re-verified after the rebase with the reusable workflow's own command shape, not by assertion: main 75b178a as-is (43 lock, 31 ignores) -> exit 0, 9 allowed warnings main 75b178a lock + this commit's audit.toml -> exit 1, 2 vulnerabilities RUSTSEC-2026-0269 8.8 high AND RUSTSEC-2026-0222 3.8 low both fire this commit (47.0.4 lock, 20 ignores) -> exit 0, 5 allowed warnings The middle line is the mutation: hold the trimmed ignore list fixed and revert only the lockfile, and the gate turns RED. So the green on the third line is the bump doing the work, not a shorter list hiding it. Reported-advisory sets diffed rather than eyeballed: nothing new appeared, and four went away (bitmaps, im-rc, sized-chunks x2 -- all cranelift 0.130-only; 0.130.2 -> 0.134.4). Both inherited claims were re-checked rather than trusted. No CI lane builds the runtime feature: `all-features` occurs ZERO times in all of .github/workflows/, ci.yml's mutants job runs `cargo mutants --in-diff -- --lib`, `make coverage` is `--workspace --exclude aprender-gpu --lib`, and no workflow invokes test-full / coverage-full / mutants. Reachability re-measured with a positive control FIRST (`-p aprender-test-lib --features runtime` -> non-zero, so a zero elsewhere means something): 0 wasmtime for -p aprender, -p aprender --no-default-features, -p apr-cli, --workspace, --workspace -e normal, and -p aprender-test-lib at its defaults. And the MSRV pin does not need to move: under the pinned rustc 1.93.0, `cargo metadata --locked` and `cargo check --locked -p aprender-test-lib --lib` both exit 0 against the 47 lock, so rust-version = "1.91" stays put too. deny.toml carried a stale "wasmtime 43 + cranelift ... Upgrade to >=43.0.2 when available" comment. It is deleted here, which makes this contract's advisory_elimination invariant ("deny.toml has zero wasmtime entries") true on purpose rather than by accident -- cargo-deny never read that line either way. `cargo deny check` -> advisories ok, bans ok, licenses ok, sources ok.
Not a defect in any PR — an advisory-DB catch-up that fails a required check
cargo-audit picked up RUSTSEC-2026-0269 — wasmtime 43.0.2, "Filesystem sandbox escape when paths or symlinks contain trailing slashes", CVSS 8.8 HIGH, published 2026-08-20 — on 2026-08-31, and failed
ci / security.ci / securityis not itself a required context, but the requiredci / gatehard-requires it:So every open pull request went red simultaneously — #2799, #2800, #2801, #2802, #2803 — with nothing wrong in any of them.
main's last run was green at745fa8588on 2026-08-30, eleven days after the advisory was published, so this is the advisory database catching up rather than any change in this repository. This is the "all-PRs-red + nothing changed in-repo" signature.Reachability — re-verified, not inherited
This is the twelfth wasmtime/cranelift advisory handled by the policy already written into
.cargo/audit.toml. It is the highest severity of them, so the "test-only, not production" claim was re-derived rather than taken from the entries above:wasmtimeis optional and gated behindaprender-test-lib'sruntimefeature, which nothing in the workspace enables. It reaches cargo-audit only becauseCargo.lockrecords optional dependencies. No shipped artifact links it, and nothing here runs untrusted wasm with a preopened directory — the only configuration in which the escape is reachable at all.Why this is a deferral and not the fix
There is no fixed 43.x. The advisory's ranges are
>=24.0.13,<25|>=36.0.14,<37|>=46.0.3,<47|>=47.0.4. The real fix is the same three-major-version bump that theRUSTSEC-2026-0222note directly above this entry already defers, and it must land before anything starts enablingruntime. That is a dependency decision worth its own ticket, not something to rush through to unblock a queue.Verification
Mutation-verified, the same way the guards in this repo are:
cargo auditerror: 1 vulnerability found!)One file, one entry, no lockfile change.
🤖 Generated with Claude Code