Skip to content

Update hackney to 4.x to address advisories - #4907

Merged
stuartc merged 11 commits into
mainfrom
4905-hackney-updates
Jul 29, 2026
Merged

Update hackney to 4.x to address advisories#4907
stuartc merged 11 commits into
mainfrom
4905-hackney-updates

Conversation

@rorymckinley

@rorymckinley rorymckinley commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description

Updates hackney from 1.25 to 4.6 to pick up the fixes for the four advisories we were suppressing in .mix_audit.ignore. Everything else here follows from that upgrade.

Closes #4905

Dependency changes

  • hackney 1.25 → 4.6.0, httpoison 2.2.3 → 3.0.0 (3.0 is the release that requires hackney 4), sentry 10.9 → 13.2 (its default HTTP client moved from Hackney to Finch along the way).
  • swoosh 1.18.2 → 1.26.3, fixing CVE-2026-54893. This forced tidewave (dev-only) 0.5.6 → 0.8.0: swoosh 1.18.3+ requires req ~> 0.5.10, and every req that new requires mime ~> 2.0, which google_gax pins away from. tidewave 0.8 dropped req entirely, and since tidewave was the only thing pulling it in, req leaves the tree and takes CVE-2026-49755 with it.
  • tzdata now tracks upstream master for lau/tzdata#170 (hackney ~> 1.17 or ~> 4.0), pinned to commit 766f38de. That is not on Hex yet — 1.1.4 predates it — and it is what lets tzdata keep its autoupdater running on hackney 4.
  • Dropped from the lock: metrics, unicode_util_compat (hackney 4 depends on neither) and req. Added: h2, quic, webtransport (hackney 4's own deps).

Result: .mix_audit.ignore is now empty, and the hex_audit/0 list in mix.exs is down to the two cowlib advisories that have no patched release.

Validation steps

  1. mix deps.audit --ignore-file .mix_audit.ignore → "No vulnerabilities found", with nothing in the ignore file.
  2. mix hex.audit → only the two cowlib entries listed as ignored, and no "entry ... can be removed" warnings.
  3. mix lightning.install_schemas → completes with 107 installed, 0 skipped and no transient-error warnings.
  4. mix verify → format, dialyzer, credo and sobelow all pass.
  5. Sign in via an OIDC/SSO provider. This exercises the httpoison 3 + hackney 4 path for discovery and JWKS fetches, which is the main runtime code still on hackney.
  6. If you run with MAIL_PROVIDER=mailgun, send a test email. Swoosh still uses Swoosh.ApiClient.Hackney, so this is swoosh 1.26 on hackney 4.

Additional notes for the reviewer

  1. Two override: true are load-bearing. hackney is overridden because phoenix_swoosh 1.2.1 (latest) still declares hackney ~> 1.10 — it never actually calls hackney, so the constraint is dead weight, but it blocks resolution. httpoison is overridden for gcs_signed_url and packmatic, which still declare 2.x.
  2. hackney is capped at 4.6.0 deliberately. 4.6.1 and later require h2 ~> 0.11.0 while hackney's own webtransport dep requires h2 ~> 0.10.4, so they cannot resolve at all. 4.6.0 is the newest installable release.
  3. hackney 4 turns on HTTP/2 by default ([http2, http1]), where 1.x was HTTP/1.1 only. Concurrent requests to one host then multiplex onto a single connection, so retiring that connection fails every in-flight request at once. Measured against mix lightning.install_schemas (130 packages at concurrency 5): 27–35 :closed errors under h2, 0 under [:http1], reproducibly. config/config.exs pins [:http1] to keep the transport hackney 1.25 used — a security upgrade shouldn't smuggle in a protocol change. Worth adopting h2 later as its own piece of work.
  4. :invalid_state is now retriable in install_schemas. hackney keeps a closing pooled connection alive briefly so requests that raced the checkout get an answer rather than crashing the caller. The comment in hackney_conn.erl says it replies {:closed, _}, but the catch-all it falls through to replies :invalid_state, which wasn't in @retriable_reasons — so a package hitting that race was skipped after one attempt with no retry.
  5. The tzdata git dependency has one real cost: mix_audit only reads {:hex, ...} lock entries, so tzdata drops out of mix deps.audit coverage while we track master. It should move back to Hex as soon as a release carries the fix. config/test.exs disables tzdata's autoupdater so the suite doesn't poll IANA on every CI run; dev and prod keep it on.
  6. The three deps/httpoison dialyzer filters are temporary. hackney 4 made a client handle a pid where it used to be a reference, but HTTPoison.AsyncResponse.t() still declares id: reference(). Already fixed upstream in edgurgel/httpoison#511, merged 2026-07-05, just unreleased — 3.0.0 shipped 2026-06-14 and is still latest.

AI Usage

Please disclose whether you've used AI anywhere in this PR (it's cool, we just
want to know!):

  • I have used Claude Code
  • I have used another model
  • I have not used AI

You can read more details in our
Responsible AI Policy

Pre-submission checklist

  • I have performed an AI review of my code (we recommend using /review
    with Claude Code)
  • I have implemented and tested all related authorization policies.
    (e.g., :owner, :admin, :editor, :viewer)
  • I have updated the changelog.
  • I have ticked a box in "AI usage" in this PR

@github-project-automation github-project-automation Bot moved this to New Issues in Core Jun 29, 2026
@rorymckinley
rorymckinley force-pushed the 4905-hackney-updates branch from 4600303 to 8ebaa18 Compare July 8, 2026 10:37
@rorymckinley
rorymckinley force-pushed the 4905-hackney-updates branch from 8ebaa18 to 941399b Compare July 9, 2026 05:17
@rorymckinley rorymckinley self-assigned this Jul 9, 2026
@midigofrank midigofrank mentioned this pull request Jul 27, 2026
7 tasks
@midigofrank midigofrank self-assigned this Jul 27, 2026
hackney 4.x carries the fixes for the four advisories .mix_audit.ignore was
suppressing, so both ignore lists shrink to what is genuinely unfixable.

- hackney 4.4.5 -> 4.6.0, which fixes a hackney_conn process leak on
  connections flagged no_reuse. 4.6.0 is the ceiling: 4.6.1 and later require
  h2 ~> 0.11.0 while hackney's own webtransport dep requires h2 ~> 0.10.4.
- swoosh -> 1.26.3, fixing CVE-2026-54893. This needs tidewave 0.8, because
  swoosh 1.18.3 and later require req ~> 0.5.10, and every req that new
  requires mime ~> 2.0, which google_gax pins away from. tidewave 0.8 drops
  req altogether, so req leaves the tree and takes CVE-2026-49755 with it.
- tzdata tracks upstream master for `hackney ~> 1.17 or ~> 4.0`
  (lau/tzdata#170), which is not on Hex yet.
- Drop metrics and unicode_util_compat from the lock; hackney 4 depends on
  neither.

.mix_audit.ignore is now empty and hex_audit/0 is down to the two cowlib
advisories with no patched release. The file stays because CI passes it via
--ignore-file and mix_audit reads it with File.read!/1.

Filter the httpoison 3 callback contract warnings: hackney 4 made a client
handle a pid where it was a reference, but HTTPoison.AsyncResponse.t() still
declares id: reference().
hackney 4 negotiates HTTP/2 by default where 1.x was HTTP/1.1 only. Concurrent
requests to one host multiplex onto a single connection, so retiring that
connection fails every in-flight request at once. mix lightning.install_schemas
lost about a quarter of its fetches to :closed this way. They were retried, but
a transport change is not something this upgrade needs.

tzdata's autoupdater works again now that it accepts hackney 4, so drop the
config that disabled it. Disable it in test instead: it polls IANA a few
seconds after boot, and CI starts from a fresh container every run.
hackney keeps a closing pooled connection alive briefly so requests that raced
the checkout get an answer instead of crashing the caller. It means to answer
{:closed, _} -- the comment in hackney_conn.erl says as much -- but the
catch-all it falls through to answers :invalid_state, which was not in
@retriable_reasons. A package hitting that race was skipped after one attempt.
edgurgel/httpoison#511 already retypes the async struct ids as `id: pid` and
drops the dead `stream_next/1` clause, so these three filters are temporary.
It merged on 2026-07-05 and 3.0.0 shipped on 2026-06-14, so there is nothing
to do but wait for the next release. Record that so the filters get removed
then rather than outliving the bug.
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.5%. Comparing base (dace94d) to head (2ddced1).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@          Coverage Diff          @@
##            main   #4907   +/-   ##
=====================================
  Coverage   90.4%   90.5%           
=====================================
  Files        425     425           
  Lines      20237   20237           
=====================================
+ Hits       18304   18306    +2     
+ Misses      1933    1931    -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@midigofrank midigofrank changed the title First pass at updating and overriding Update hackney to 4.x to address advisories Jul 29, 2026
@midigofrank
midigofrank marked this pull request as ready for review July 29, 2026 03:30
@midigofrank
midigofrank requested a review from stuartc July 29, 2026 03:30
@github-actions

Copy link
Copy Markdown

This PR is purely a dependency upgrade: hackney 4.6, httpoison 3.0, sentry 13.2, swoosh 1.26, tidewave 0.8, tzdata from master, along with associated dialyzer/mix_audit ignore updates and small changes to install_schemas.ex retry logic and its tests. No queries, controllers, LiveView events, or project-config mutations were touched, so none of S0/S1/S2 have surface area in this diff.

Security Review ✅

  • S0 (project scoping): N/A — the diff touches only dependency pins (mix.exs/mix.lock), Sentry/hackney config, dialyzer/audit ignore lists, and the install_schemas mix task; no queries or web-layer entrypoints are added or changed.
  • S1 (authorization): N/A — no new controller actions, LiveView handle_events, or channel handlers are introduced; the only new logic is transport-level retry handling for a build-time schema fetcher.
  • S2 (audit trail): N/A — no writes to project/instance configuration resources; changes are limited to build/runtime dependency configuration and a mix-task retry list.

cowboy 2.18.0 and cowlib 2.19.0 clear EEF-CVE-2026-65624 and EEF-CVE-2026-59248.
Both were published on 2026-07-28, about an hour after this branch's last lint
run, so hex.audit was green then and would fail now. Back to exit 0 with only
the two cowlib records that carry no fixed event.

finch backs the app's own Lightning.Finch pool and the default Tesla adapter,
but its version was resolving entirely through prom_ex and goth. Dropping or
loosening either would break the supervision tree with no compile-time signal.
Sentry 13 defaulting its client to Finch makes it worth stating outright.

tzdata takes an explicit ref. The lock pinned the commit, but `github:` with no
ref tracks the default branch, so unlocking it would pull whatever master
happens to be at that moment into a release. Also drop the guess that the fix
lands in 1.1.5: master still declares 1.1.4 and no release is scheduled.
@stuartc
stuartc merged commit e776f7c into main Jul 29, 2026
7 checks passed
@stuartc
stuartc deleted the 4905-hackney-updates branch July 29, 2026 09:38
@github-project-automation github-project-automation Bot moved this from New Issues to Done in Core Jul 29, 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

Development

Successfully merging this pull request may close these issues.

Update Hackney to address vulnerabiliites

3 participants