Skip to content

fix(deps): resolve RUSTSEC advisories in lru, anyhow and error-stack - #119

Merged
dduugg merged 2 commits into
mainfrom
security/drop-unused-lru-and-bump-anyhow
Aug 19, 2026
Merged

fix(deps): resolve RUSTSEC advisories in lru, anyhow and error-stack#119
dduugg merged 2 commits into
mainfrom
security/drop-unused-lru-and-bump-anyhow

Conversation

@dduugg

@dduugg dduugg commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

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 between
the two. The two commits are independently reviewable:

1. fix(deps): drop unused lru feature and bump anyhow (deps only, no code change)

RUSTSEC-2026-0253lru 0.7.8, use-after-free in LruCache::pop() (#117).
lru reaches us through memoize's default full feature (full = ["lru", "memoize-inner/full"]).
The advisory is only fixed in lru >= 0.18.2, but memoize — including its latest release,
0.6.0 — still requires lru ^0.7, so no lockfile can resolve a patched version while that
feature is on. We only ever use bare #[memoize], never the Capacity/TimeToLive options
that need lru, so building memoize with default-features = false removes the crate from
the graph entirely instead of suppressing the advisory. Also drops ahash 0.7, getrandom 0.2,
version_check and wasi 0.11.

RUSTSEC-2026-0190anyhow 1.0.83, unsoundness in Error::downcast_mut() (#114).
anyhow is an optional, non-activated dependency of error-stack that still gets resolved
into Cargo.lock (and so still gets flagged). cargo update -p anyhow takes it to 1.0.104,
past the >= 1.0.103 patch line.

2. fix(deps): upgrade error-stack to 0.8.0 (#115)

RUSTSEC-2026-0198Report::frames_mut handed out &mut Frames with lifetimes independent
of 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 means
crossing the 0.6/0.7 API breaks:

removed / renamed in replacement here
Context trait 0.7 impl core::error::Error (3 types)
Result<T, C> alias 0.8 Result<T, Report<C>>
report! 0.7 Report::new
attach_printable / attach_printable_lazy 0.6 attach / attach_with

Two notes on why these are behaviour-preserving rather than merely compiling:

  • core::error::Error is exactly the bound Report::new/change_context require, and its
    default source() returns None, so no new frames appear in any report.
  • 0.6 also renamed the old attach to attach_opaque. That's the dangerous half of the
    rename, because an attachment that becomes opaque silently disappears from rendered output.
    This crate had no plain attach calls, so every rename here is the printable variant.

Result<T, Report<C>> is the same type the deleted alias expanded to, so the public signatures
in src/runner/api.rs are 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 — the
    release job's exact command — succeeds.
  • Rendered stderr is byte-identical before and after the error-stack upgrade across five
    distinct failure paths, including a four-level nested report with attachments
    (diff of the two captures is empty).
  • error-stack 0.8.0's MSRV is 1.83, under the 1.89.0 pin in rust-toolchain.toml.

Not included

No version bump — version stays at 0.3.4, so merging this does not cut a release
(check_for_version_changes evaluates changed=false; verified the added
memoize = { version = ... } line does not false-positive that job's grep "\+version").
Worth noting that downstream Rust consumers of the published crate only pick up the
error-stack fix once a release goes out, and main already carries unreleased #111#113,
so a release PR is a separate decision.

Closes #114
Closes #115
Closes #117

dduugg added 2 commits August 19, 2026 11:22
…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
@dduugg
dduugg requested a review from a team as a code owner August 19, 2026 18:38
@github-project-automation github-project-automation Bot moved this to Triage in Modularity Aug 19, 2026
@dduugg
dduugg enabled auto-merge (squash) August 19, 2026 18:48
@dduugg
dduugg merged commit 7a063f3 into main Aug 19, 2026
12 checks passed
@dduugg
dduugg deleted the security/drop-unused-lru-and-bump-anyhow branch August 19, 2026 21:56
@github-project-automation github-project-automation Bot moved this from Triage to Done in Modularity Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

2 participants