chore(deps): Update dependency guzzlehttp/guzzle to v7.15.2 [SECURITY] - #327
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): Update dependency guzzlehttp/guzzle to v7.15.2 [SECURITY]#327renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #327 +/- ##
===========================================
Coverage 100.00% 100.00%
Complexity 90 90
===========================================
Files 7 7
Lines 226 226
===========================================
Hits 226 226 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
This PR contains the following updates:
7.15.1→7.15.2Guzzle: Noncanonical cookie domain keeps subdomain scope
CVE-2026-69245 / GHSA-f7vp-7xgx-4w4r
More information
Details
Impact
SetCookie::matchesDomain()gives every subdomain of a cookieDomainthat cookie unless it recognizes theDomainas an IP literal or a numeric host, and it decides that from the domain's own text, so two spellings a transport reads as an address keep subdomain scope. On7.15, hexadecimal and mixed-base forms such as0x7f000001and0177.0.0.0x1go unrecognized while libcurl 8.21.0 reads both as127.0.0.1, so closing them completes the ruleGHSA-g446-98w2-8p5w(CVE-2026-59883) set out to establish, which cited the WHATWG IPv4 parser and so already admitted a0xpart. A percent-escapedDomainkeeps that scope on both branches for a different reason: percent-decoding sits above numeric parsing, so192.168.0.%31and127.0.0.1%2eare registered names in the URI grammar rather than address literals, and no numeric rule in any base classifies them, while libcurl decodes the host before it resolves and reads them as192.168.0.1and127.0.0.1. The escape must fall in the rightmost label on8.0.0, which already catches a decimal, octal or0xpart there, but not on7.15, where%31%32%37.0.0.0x1also reads as127.0.0.1;8.0.0is affected in the percent spelling alone.Both directions of the defect are reproduced over a real socket. A cookie stored for
Domain=0x7f000001is placed in theCookieheader of a request toevil.0x7f000001, disclosing a session identifier or token to a host that is not that address, and a response fromevil.0x7f000001settingDomain=0x7f000001is accepted into the jar and replayed to the address, so a server answering for the look-alike name can fix a session or set application state.Exploitation requires the application to enable cookie support, address an origin by one of these spellings, and contact a host whose name ends in it. No public DNS delegation provides such a name, so the exposure is to private, split-horizon and container zones. Applications that do not use Guzzle's cookies, that keep a separate jar for each host or trust boundary, or that address origins only by ordinary names are not affected.
Patches
This is a summary; the patches are the authority. The issue is fixed in
7.15.2and8.0.1, which hold a cookieDomaincontaining a%byte to an exact match against the request host.7.15.2additionally holds to an exact match aDomainwhose every dot-separated part is decimal, octal or hexadecimal, a rule wide enough that0x100000000, a name to libcurl, loses subdomain matching too;8.0.0already recognizes a0x-prefixed rightmost label. One class stays open: an IDN-capable transport reads a fullwidth-digitDomainsuch as127.0.0.1as127.0.0.1while Guzzle matches it by suffix, and these patches do not close that. Versions before7.15.2and version8.0.0are affected,8.0.0in the percent spelling alone.Workarounds
If you cannot upgrade, do not share one
CookieJarbetween an origin addressed by a numeric or percent-escaped spelling and any other host ending in that spelling. Use a separate jar for each host or trust boundary, or address such an origin by its canonical dotted-decimal form, writing127.0.0.1, which released versions already hold to an exact match, rather than0x7f000001or127.0.0.%31. Rejecting a request URI host that contains a percent escape, before you hand the URI to Guzzle and on every redirect hop, closes that spelling, and rejecting a non-ASCII byte closes the fullwidth one, but nothing outside the jar closes the hexadecimal one.Do not use
filter_var()withFILTER_VALIDATE_IPto decide whether a domain is an address: it rejects0x7f000001and127.0.0.%31, which a transport reads as127.0.0.1.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: Noncanonical host can bypass host-based checks
CVE-2026-69246 / GHSA-v5mv-p594-2x33
More information
Details
Impact
In affected versions, Guzzle gives a transport the request URI as text and supplies the
Hostheader separately. The cURL handlers setCURLOPT_URLto the URI exactly as written and push thatHostintoCURLOPT_HTTPHEADER;StreamHandlerdoes the same throughfopen(). libcurl then parses the authority itself, percent-decoding it and, on an IDN-capable build, applying IDNA mapping, and uses the result to resolve, connect, name the TLS peer and address a proxyCONNECT, while the suppliedHostsuppresses the aligned one it would have generated. Inhttp://127.0.0.%31/the URI host is onefilter_var()rejects as an IP literal, yet libcurl decodes it to127.0.0.1and reaches loopback with no DNS lookup while the server receivesHost: 127.0.0.%31.An attacker who influences a fetched URI can therefore reach a host the application's checks excluded and read whatever it exposes of the response. The same divergence moves Guzzle's own decisions onto a spelling the transport does not use:
no_proxyselects proxy routing from the literal host, andRedirectMiddlewaredecides from it whether to stripAuthorizationandCookie. The cookie middleware extractsSet-Cookieagainst the URI Guzzle produced, not the authority contacted, so for a raw divergent URI the cookie is stored under the URI host as written. Where Guzzle rewrote that URI but left a divergentHost, or where the caller supplied one, the cookie is stored under the canonical name and replayed by ordinary later requests to it. With a third-partyUriInterface, a host ofblocked.example.com@127.0.0.1reaches127.0.0.1through all three handlers and generatesAuthorization: Basicfrom userinfo the application never wrote.Exploitation requires the application to build a request URI from untrusted input and to make a host decision before handing it to Guzzle. Applications that only fetch URIs they construct themselves are not affected, and an exact allowlist of canonical names ordinarily fails closed; the exposure is to denylists, private-range and IP-literal checks, and any check that treats an unresolvable name as safe. The raw Unicode class needs an IDNA transformation somewhere: either a libcurl built with IDN support or Guzzle's own
idn_conversion, off by default on both branches, which rewrites the URI inClient::buildUri()before a handler sees it and leaves a prebuilt request's explicitHostas written, while a request the client builds derives that header from the rewritten URI and produces no divergence. Noncanonical numeric spellings such as127.1,2130706433,0x7f000001and0177.0.0.1remain accepted after the patch and reach whatever the transport reads them as, loopback or a routable public host, and the cURL and stream handlers can differ, so a check comparing a host against an address as text stays bypassable. Guzzle does not offer SSRF protection, and neither cache poisoning nor cross-tenant compromise was established.Patches
This is a summary; the patches are the authority. The issue is fixed in
7.15.2and8.0.1, which validate the request host in all three built-in handlers before any network I/O. A URI host is rejected for a byte outside0x21to0x7E, a percent escape, a URI authority delimiter, unbalanced brackets, or numeric-looking parts followed by a trailing dot. That last rule is deliberately conservative and also refuses out-of-range forms libcurl keeps as names, such as256.0.0.1.. An explicitHostheader must be printable ASCII, and on7.15.2free of percent escapes. The client also regenerates a derivedHostwhen it rewrites the request URI. Versions before7.15.2and version8.0.0are affected.Workarounds
If you cannot upgrade, constrain the host yourself before handing a URI to Guzzle, and constrain any explicit
Hostheader separately, on every redirect hop. The URI rule assumes$uriis a validatedGuzzleHttp\Psr7\Uri, so re-parse a third-partyUriInterfacewithnew Uri((string) $uri)first.It differs from the patch in both directions: it refuses
example.com., which the patch accepts, and it does not canonicalize127.1or0x7f000001. Reparsing the URI separates a valid port from the host and rejects malformed bracket forms, so the snippet checks the host component alone.idn_conversion => trueis not an access control, since IDNA maps127。0。0。1onto127.0.0.1and direct handler use bypasses it, andUri::getHost()is not an SSRF boundary: it is the host as written, not the host a transport connects to. Where the destination matters, resolve the host and check the addresses, and use a separate cookie jar for untrusted origins.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
guzzle/guzzle (guzzlehttp/guzzle)
v7.15.2Compare Source
Security
Hostheaders (GHSA-v5mv-p594-2x33)Hostheader after client URI rewrites (GHSA-v5mv-p594-2x33)Fixed
RequestExceptionwhen the stream handler rejects a request before opening a streamConfiguration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.