Skip to content

Bump h2 to 0.4.16 for RUSTSEC-2026-0258 - #323

Merged
stephane-segning merged 2 commits into
mainfrom
fix-rustsec-2026-0258
Aug 18, 2026
Merged

Bump h2 to 0.4.16 for RUSTSEC-2026-0258#323
stephane-segning merged 2 commits into
mainfrom
fix-rustsec-2026-0258

Conversation

@stephane-segning

@stephane-segning stephane-segning commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Bumps h2 0.4.150.4.16 to clear RUSTSEC-2026-0258. Lockfile only — no manifest change.

Intent

main is currently red on cargo deny check advisories, and neither of today's two PRs caused it. RUSTSEC-2026-0258 ("h2 unbounded empty DATA frames", GHSA-q83h-524g-xf6h) was published after #321 merged green. Found when #322 — a one-file workflow change that touches no dependency — went red on cargo deny.

Confirmed against main directly rather than inferred from which PR happened to fail first:

$ git show main:Cargo.lock | grep -A1 '^name = "h2"' | grep version
version = "0.4.15"

h2 is the HTTP/2 implementation under hyper/axum/reqwest — the stack sms-gateway serves on — so this is a real denial-of-service surface, not a dev-only transitive advisory. The advisory's own stated solution is >=0.4.16.

Scope

Two lockfiles: Cargo.lock (root workspace) and sdks/rust/Cargo.lock.

The first commit did only the root one, on the strength of a claim that read "the other three lockfiles contain no h2 at all — checked, not assumed." That claim was false, and the check that produced it was broken — which is the more useful half to record.

The check ran grep -A1 '^name = "h2"' <file> || echo "(no h2)" over a hardcoded list of four paths, one of which — sdks/rust/vsms-sdk-rust/Cargo.lockdoes not exist. vsms-sdk-rust is a member of a workspace rooted at sdks/rust/, so its lockfile is sdks/rust/Cargo.lock. grep exits non-zero on a missing file exactly as it does on a file with no match, so the || branch printed "(no h2)" for a file that was never read. A false negative was indistinguishable from a real answer.

CI caught it: the cargo deny job's second step, cargo deny on vsms-sdk-rust, failed on RUSTSEC-2026-0258 while the root workspace passed.

Re-enumerated with find . -name Cargo.lock rather than a hardcoded list, so a lockfile cannot be missing from the check by being absent from someone's memory of the layout — and there turn out to be five, not four:

./Cargo.lock                      h2=0.4.16
./ci/e2e-integration/Cargo.lock   h2=none
./deploy/backup-tool/Cargo.lock   h2=none      <- was missing from the list entirely
./examples/rust/Cargo.lock        h2=none
./sdks/rust/Cargo.lock            h2=0.4.15    <- fixed in the second commit

I also re-verified the aws-lc-rs-absence claim across all five real lockfiles, since that check shared the identical flaw: absent from every one. That conclusion was right; the check that produced it was not.

One thing in the diff that is not the h2 bump

Five unrelated packages had their windows-sys reference re-pointed from 0.61.2 down to 0.52.0/0.59.0/0.48.0. Naming it rather than leaving a reviewer to find it:

  • It cannot be avoided by narrowing the invocation. cargo update -p h2 --precise 0.4.16 produces byte-identical output to a plain cargo update -p h2. This is cargo re-resolving, not something the command asked for.
  • It is benign, and that was checked rather than assumed. All four windows-sys versions were already present in main's lockfile — the version set is identical before and after. No version entered or left the tree; five edges moved between versions already there.
$ grep -A1 '^name = "windows-sys"' Cargo.lock | grep version      # this branch
version = "0.48.0"
version = "0.52.0"
version = "0.59.0"
version = "0.61.2"
$ git show main:Cargo.lock | grep -A1 '^name = "windows-sys"' | grep version
version = "0.48.0"
version = "0.52.0"
version = "0.59.0"
version = "0.61.2"

cargo deny check bans still passes, which is the gate that would catch a genuine new duplicate. These crates are cfg(windows)-gated and are never compiled for this project's Linux (distroless) or macOS targets.

Deliberately NOT done

  • No hand-editing of Cargo.lock to suppress the windows-sys churn. A hand-edited lockfile is worse than a noisy one.
  • No broad cargo update. This fixes the advisory and nothing else; sweeping the other 64 out-of-date dependencies is a separate, deliberate decision.
  • RUSTSEC-2023-0071 (rsa/Marvin) is untouched — still the documented, time-boxed, reasoned deny.toml exception, unaffected by this.

Verification

$ cargo deny check          # before
advisories FAILED, bans ok, licenses ok, sources ok
error[vulnerability]: h2 unbounded empty DATA frames
    ├ ID: RUSTSEC-2026-0258
    ├ Solution: Upgrade to >=0.4.16

$ cargo deny check          # after
advisories ok, bans ok, licenses ok, sources ok

cargo check --workspace --all-targets clean. The rest — clippy, the full test suite, the live-Postgres suites — is this PR's own CI run; a patch bump to the HTTP/2 layer is exactly the kind of change where "it compiles" is not the interesting signal.

Screenshots / Evidence

Terminal output inline above.

Risk Assessment

What breaks if I'm wrong: h2 sits under every HTTP path this project has — the gateway's own axum server, reqwest for provider calls, and wiremock in tests. A regression in a patch release would surface as connection-level failures, which the live-Postgres suites and the wiremock-backed provider/chaos suites exercise for real over loopback HTTP.

Why the risk is low: patch release within 0.4.x, semver-compatible, no manifest change, no API surface touched by this repo directly (nothing here imports h2).

The larger risk is not merging itmain stays red, which blocks every other PR and trains people to ignore a failing cargo deny.

AI Usage Declaration

Claude found the advisory while investigating an unrelated red check, verified it was pre-existing on main rather than PR-introduced, applied the bump, and investigated the unexpected windows-sys churn to the point of confirming it introduces no new version. Every claim is command output.

  • A human directed this change and is accountable for it.
  • Claims in this PR were verified against the repository or a running system, rather than assumed.

Reviewer Focus

  1. The windows-sys re-pointing is the only thing here that isn't mechanical. I'm satisfied it's inert (identical version set, bans ok, never compiled on our targets) but it is the one part of the diff worth a second opinion.
  2. Only two of the five lockfiles are cargo deny-checked at all — the root workspace and vsms-sdk-rust. ci/e2e-integration, examples/rust and deploy/backup-tool have no advisory gate. None carries h2 today, so nothing is currently hiding there, but that is luck rather than coverage. Worth its own issue; deliberately not widened here.

Checklist

  • docs/roadmap.md checked — no edit needed; this changes no milestone, gate, dependency or decision.
  • No framework/toolchain surprise to record in AGENTS.md — a routine advisory bump. (The windows-sys re-resolution behaviour is captured in the commit message, where it belongs.)
  • No new R1 exceptions.

@changeset-bot

changeset-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e85d830

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

stephane-segning and others added 2 commits August 18, 2026 12:24
`main` is red on `cargo deny check advisories`, and neither of today's two
PRs caused it: RUSTSEC-2026-0258 ("h2 unbounded empty DATA frames",
GHSA-q83h-524g-xf6h) was published after #321 merged green, and h2 0.4.15
is on `main` independently of it. Confirmed by checking `git show
main:Cargo.lock` directly rather than inferring from which PR happened to
go red first.

h2 is the HTTP/2 implementation under hyper/axum/reqwest, i.e. in the
stack sms-gateway serves on, so this is a real denial-of-service surface
rather than a transitive dev-only advisory. The advisory's own solution
is ">=0.4.16"; this is a lockfile-only change, no manifest edit.

One thing in the diff is not the h2 bump and is worth naming rather than
leaving for a reviewer to spot: five unrelated packages had their
`windows-sys` reference re-pointed from 0.61.2 down to 0.52.0/0.59.0/
0.48.0. That is cargo re-resolving, not something this change asked for
--- `--precise 0.4.16` produces byte-identical output to a plain `cargo
update -p h2`, so it cannot be avoided by narrowing the invocation.

It is benign, checked rather than assumed: all four windows-sys versions
were ALREADY present in `main`'s lockfile, so the version set is
identical before and after --- no version entered or left the tree, only
five edges moved between versions already there. `cargo deny check bans`
still passes, which is the gate that would catch a genuine new duplicate.
These crates are `cfg(windows)`-gated and are never compiled for this
project's Linux (distroless) or macOS targets.

Verified: `cargo deny check` -> `advisories ok, bans ok, licenses ok,
sources ok` (was `advisories FAILED`); `cargo check --workspace
--all-targets` clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first commit claimed "the other three lockfiles contain no h2 at all
--- checked, not assumed." The claim was false and the check that
produced it was broken, which is the more useful half to record.

The check ran `grep -A1 '^name = "h2"' <file> || echo "(no h2)"` over a
hardcoded list of four paths, one of which --- `sdks/rust/vsms-sdk-rust/
Cargo.lock` --- does not exist. `vsms-sdk-rust` is a member of a
workspace rooted at `sdks/rust/`, so its lockfile is `sdks/rust/
Cargo.lock`. grep exits non-zero on a missing file exactly as it does on
a file with no match, so the `||` branch printed "(no h2)" for a file
that was never read, and a false negative was indistinguishable from a
real answer.

Two consequences: there are FIVE lockfiles in this tree, not four
(`deploy/backup-tool/Cargo.lock` was missing from the list entirely), and
`sdks/rust/Cargo.lock` was still on h2 0.4.15. CI caught it --- the `cargo
deny` job's second step, `cargo deny on vsms-sdk-rust`, failed on
RUSTSEC-2026-0258 while the root workspace passed.

Enumerated with `find . -name Cargo.lock` this time rather than a
hardcoded list, so a lockfile cannot be missing from the check by being
absent from someone's memory of the layout:

  ./Cargo.lock                      h2=0.4.16
  ./ci/e2e-integration/Cargo.lock   h2=none
  ./deploy/backup-tool/Cargo.lock   h2=none
  ./examples/rust/Cargo.lock        h2=none
  ./sdks/rust/Cargo.lock            h2=0.4.15  <- fixed here

Same `--precise 0.4.16`, same benign windows-sys re-pointing (two edges
this time), same reasoning as the first commit.

Also re-verified the `aws-lc-rs` absence claim across all five real
lockfiles, since that check shared the identical flaw: absent from every
one. The conclusion was right; the check that produced it was not.

Verified: `cargo deny check` in sdks/rust/vsms-sdk-rust -> `advisories
ok, bans ok, licenses ok, sources ok` (was `advisories FAILED`);
`cargo check --locked --all-targets` clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stephane-segning
stephane-segning merged commit e9cb7ae into main Aug 18, 2026
8 checks passed
@stephane-segning
stephane-segning deleted the fix-rustsec-2026-0258 branch August 18, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant