fix(deps): resolve RUSTSEC advisories in lru, anyhow and error-stack - #119
Merged
Conversation
…ories Resolves two RUSTSEC advisories that reach us only through transitive dependencies, without touching any application code. RUSTSEC-2026-0253 (lru 0.7.8, use-after-free in `LruCache::pop()`): `lru` arrives via `memoize`'s default `full` feature. The fix landed in lru 0.18.2, but `memoize` (through 0.6.0, its latest) still requires `lru ^0.7`, so the lockfile can never resolve a patched version while that feature is on. We only use bare `#[memoize]` and never the `Capacity`/`TimeToLive` options that need `lru`, so building `memoize` with `default-features = false` removes the crate from the graph entirely rather than suppressing the advisory. RUSTSEC-2026-0190 (anyhow 1.0.83, unsoundness in `Error::downcast_mut`): `anyhow` is an optional, non-activated dependency of `error-stack` that still gets resolved into the lockfile. `cargo update -p anyhow` takes it to 1.0.104, satisfying the `>= 1.0.103` patch requirement. Closes #117 Closes #114
`Report::frames_mut` handed out `&mut Frame`s whose lifetimes were independent of the iterator, so a frame and one of its sources could be borrowed mutably at once — UB reachable from safe code. Fixed in 0.8.0, which replaces the iterator with a visitor closure. We never call `frames_mut`, so this is a dependency upgrade rather than a behaviour fix on our side, but 0.8.0 is the only patched release. Getting there means crossing the 0.6/0.7 API breaks: - `Context` was deprecated in 0.6 and removed in 0.7; the three implementors now `impl core::error::Error` instead. That is precisely the bound `Report::new`/`change_context` require, and the blanket default `source()` returning `None` keeps report contents identical. - The `Result<T, C>` alias was deprecated in 0.6 and is gone in 0.8. Call sites spell out `Result<T, Report<C>>`, which is the same type the alias expanded to — no signature change for library consumers. - `report!` was removed in 0.7, replaced here by `Report::new`. - 0.6 renamed `attach_printable` -> `attach` and `attach_printable_lazy` -> `attach_with` (and the old `attach` -> `attach_opaque`). We had no plain `attach` calls, so every rename is the printable variant and no attachment silently becomes opaque. Verified the rendered error output is byte-identical before and after across five failure paths, including a four-level nested report with attachments. Closes #115
dkisselev
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves the three open RUSTSEC advisories from the triage board. Kept as one PR because
both halves touch
Cargo.lock; splitting them would have guaranteed a merge conflict betweenthe two. The two commits are independently reviewable:
1.
fix(deps): drop unused lru feature and bump anyhow(deps only, no code change)RUSTSEC-2026-0253 —
lru0.7.8, use-after-free inLruCache::pop()(#117).lrureaches us throughmemoize's defaultfullfeature (full = ["lru", "memoize-inner/full"]).The advisory is only fixed in
lru >= 0.18.2, butmemoize— including its latest release,0.6.0 — still requires
lru ^0.7, so no lockfile can resolve a patched version while thatfeature is on. We only ever use bare
#[memoize], never theCapacity/TimeToLiveoptionsthat need
lru, so buildingmemoizewithdefault-features = falseremoves the crate fromthe graph entirely instead of suppressing the advisory. Also drops
ahash0.7,getrandom0.2,version_checkandwasi0.11.RUSTSEC-2026-0190 —
anyhow1.0.83, unsoundness inError::downcast_mut()(#114).anyhowis an optional, non-activated dependency oferror-stackthat still gets resolvedinto
Cargo.lock(and so still gets flagged).cargo update -p anyhowtakes it to 1.0.104,past the
>= 1.0.103patch line.2.
fix(deps): upgrade error-stack to 0.8.0(#115)RUSTSEC-2026-0198 —
Report::frames_muthanded out&mut Frames with lifetimes independentof the iterator, so a frame and one of its sources could be borrowed mutably at the same time.
We never call
frames_mut, but 0.8.0 is the only patched release, and getting there meanscrossing the 0.6/0.7 API breaks:
Contexttraitimpl core::error::Error(3 types)Result<T, C>aliasResult<T, Report<C>>report!Report::newattach_printable/attach_printable_lazyattach/attach_withTwo notes on why these are behaviour-preserving rather than merely compiling:
core::error::Erroris exactly the boundReport::new/change_contextrequire, and itsdefault
source()returnsNone, so no new frames appear in any report.attachtoattach_opaque. That's the dangerous half of therename, because an attachment that becomes opaque silently disappears from rendered output.
This crate had no plain
attachcalls, so every rename here is the printable variant.Result<T, Report<C>>is the same type the deleted alias expanded to, so the public signaturesin
src/runner/api.rsare unchanged for consumers.Verification
cargo audit(real cargo-audit, 1217 advisories loaded): zero advisories, down from three.cargo fmt --check,RUSTFLAGS=-Dwarnings cargo clippy --all-targets --all-features,cargo test(148 passing) all green.cargo build --release --target aarch64-apple-darwin --target x86_64-apple-darwin— therelease job's exact command — succeeds.
distinct failure paths, including a four-level nested report with attachments
(
diffof the two captures is empty).rust-toolchain.toml.Not included
No version bump —
versionstays at 0.3.4, so merging this does not cut a release(
check_for_version_changesevaluateschanged=false; verified the addedmemoize = { version = ... }line does not false-positive that job'sgrep "\+version").Worth noting that downstream Rust consumers of the published crate only pick up the
error-stack fix once a release goes out, and
mainalready carries unreleased #111–#113,so a release PR is a separate decision.
Closes #114
Closes #115
Closes #117