From 564e0a8d0fe440dff21a6c405c88e05698b3059f Mon Sep 17 00:00:00 2001 From: Tejas Chopra Date: Thu, 6 Aug 2026 17:46:51 -0700 Subject: [PATCH] fix(deps): bump h2 to 4.4.1 for CVE-2026-71554 (#2839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description `pip-audit` is currently red on every open PR. Not because of anything in those branches — `uv.lock` pins `h2` at 4.3.0, and CVE-2026-71554 was published against `h2 <=4.4.0`. > h2 <=4.4.0 accepts request header blocks containing more than one Host header, and forwards every Host header to the consuming application. Where the consumer downgrades HTTP/2 to HTTP/1.1, the resulting request carries two Host header lines, which is a request smuggling primitive (CWE-444). Fixed in 4.4.1. Closes # ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - `uv lock --upgrade-package h2`, which moves exactly two packages: ``` h2 4.3.0 -> 4.4.1 hpack 4.1.0 -> 4.2.0 ``` `h2` arrives transitively via `httpx[http2]`, and the constraint in `pyproject.toml` is already wide enough (`>=3,<5`), so only the lock needed to move — no source or `pyproject.toml` change. `requirements-prod.txt` is not checked in; the audit workflow exports it from `uv.lock` at run time, so the lock bump is the entire fix. ## Testing - [x] Unit tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [x] Manual testing performed ### Test Output Reproduced the CI gate locally with the exact command from `.github/workflows/security.yml`: ```text $ uv export --frozen --no-dev --no-emit-project --no-hashes \ --extra all --format requirements-txt > requirements-prod.txt $ grep -E '^(h2|hpack)==' requirements-prod.txt h2==4.4.1 hpack==4.2.0 $ pip-audit -r requirements-prod.txt No known vulnerabilities found ``` Before this change, the same command reported: ```text Name | Version | ID | Fix Versions h2 | 4.3.0 | CVE-2026-71554 | 4.4.1 Found 1 known vulnerability in 1 package ``` ## Real Behavior Proof - **Environment:** macOS, uv 0.9.x, Python 3.12.6. - **Exact command / steps:** `uv lock --upgrade-package h2 --dry-run` to confirm the blast radius, then the real lock, then the workflow's own export + `pip-audit` invocation. - **Observed result:** resolution touches only `h2` and `hpack`; 269 packages resolved with no other version movement. `pip-audit` goes from 1 known vulnerability to none. - **Not tested:** HTTP/2 traffic against a live upstream. `h2` 4.4.1 is a patch release on a library used transitively by `httpx`; Headroom does not import `h2` directly (`grep -rn "import h2" headroom/` is empty), so the exposure is whatever `httpx[http2]` does with it. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] I did **not** edit `CHANGELOG.md` ## Additional Notes N/A items above: no code changed, so ruff/mypy/new tests do not apply — the verification that matters is the audit output, which is quoted in full. **Why this is standalone.** It surfaced while fixing CI on #2838, but it is not caused by that branch and it blocks #2832 identically. Landing it separately unblocks the gate for every open PR at once and keeps a supply-chain bump out of an unrelated change. **One unrelated warning the resolver prints**, noted so it is not mistaken for a side effect of this PR: ``` warning: `pypdfium2==5.12.0` is yanked (reason: "Setup blunder breaking some bindgen codepaths ... Wheels are valid and effectively identical to 5.12.1") ``` That predates this change and is not touched by it. Worth its own bump, but not here. --- uv.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/uv.lock b/uv.lock index c70b4098d..b80d4d91f 100644 --- a/uv.lock +++ b/uv.lock @@ -1667,15 +1667,15 @@ wheels = [ [[package]] name = "h2" -version = "4.3.0" +version = "4.4.1" source = { registry = "https://pypi.org/simple/" } dependencies = [ { name = "hpack" }, { name = "hyperframe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/85/7c366e69d84c17bb778fe41419e1fbcce3033d5b7ce29bbffff0a98b859f/h2-4.4.1.tar.gz", hash = "sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516", size = 2157281, upload-time = "2026-08-03T11:45:09.509Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/e85faf23bd72a92d1921e37d674ca56eb298a3c8be31fdecef0ff2b3aaac/h2-4.4.1-py3-none-any.whl", hash = "sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6", size = 62636, upload-time = "2026-08-03T11:44:59.164Z" }, ] [[package]] @@ -2078,11 +2078,11 @@ sdist = { url = "https://files.pythonhosted.org/packages/cf/7a/1a9b1405f2eb59515 [[package]] name = "hpack" -version = "4.1.0" +version = "4.2.0" source = { registry = "https://pypi.org/simple/" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/48/71de9ed269fdae9c8057e5a4c0aa7402e8bb16f2c6e90b3aa53327b113f8/hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca", size = 51276, upload-time = "2025-01-22T21:44:58.347Z" } +sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c6/80c95b1b2b94682a72cbdbfb85b81ae2daffa4291fbfa1b1464502ede10d/hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496", size = 34357, upload-time = "2025-01-22T21:44:56.92Z" }, + { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" }, ] [[package]]