fix(deps): update dependency guzzlehttp/guzzle to ~7.15.2 [security] - #2222
Open
renovate-bot wants to merge 1 commit into
Open
Conversation
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 10, 2026 08:30
a128096 to
470a63b
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 10, 2026 19:49
470a63b to
f09516a
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 12, 2026 14:55
f09516a to
4f5b843
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 12, 2026 21:14
4f5b843 to
84d7fe9
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 13, 2026 04:20
84d7fe9 to
ef86ed2
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 13, 2026 12:56
ef86ed2 to
330180d
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 24, 2026 22:43
efe4d54 to
2639dd1
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 25, 2026 03:31
2639dd1 to
500e3b2
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 30, 2026 21:59
500e3b2 to
9e79fb0
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
July 31, 2026 04:12
9e79fb0 to
faa41f6
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
August 4, 2026 19:33
faa41f6 to
7682742
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
August 5, 2026 00:17
7682742 to
d59f96a
Compare
renovate-bot
force-pushed
the
renovate/iap-packagist-guzzlehttp-guzzle-vulnerability
branch
from
August 5, 2026 17:04
d59f96a to
f7a1744
Compare
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.10.0→~7.15.2guzzlehttp/guzzle: Silent HTTPS-Proxy Downgrade to Cleartext
CVE-2026-55568 / GHSA-wpwq-4j6v-78m3
More information
Details
Impact
The built-in cURL handlers (
GuzzleHttp\Handler\CurlHandlerandGuzzleHttp\Handler\CurlMultiHandler, used by default whenever the PHP cURL extension is available) accept anhttps://proxy — a proxy reached over a TLS-encrypted connection — through theproxyrequest option, client-levelproxydefaults, or proxy environment variables such ashttp_proxy,https_proxy,HTTPS_PROXY,all_proxy, andALL_PROXY.When the installed libcurl does not support HTTPS proxies, behavior depends on the libcurl version/build:
https://proxy as a plaintexthttp://proxy. The TLS connection to the proxy is never established, and the proxy leg is cleartext with no error or warning.The security-relevant case is the silent downgrade on libcurl older than 7.50.2. An application is affected when it sends requests through one of the built-in cURL handlers, configures an
https://proxy expecting the proxy connection itself to be encrypted, and runs with libcurl older than 7.50.2.In that configuration, traffic expected to be protected by TLS on the hop to the proxy is transmitted in cleartext. Proxy authentication credentials (the
Proxy-Authorizationheader, proxy userinfo in the proxy URL, orCURLOPT_PROXYUSERPWD) are sent without encryption, and theCONNECTtarget host and port for tunneled HTTPS requests are exposed. For plain HTTP requests, request headers and bodies are also exposed on the proxy leg. End-to-end HTTPS requests tunneled through the proxy remain protected by their inner TLS session; the exposure is limited to the proxy negotiation and proxy credentials.Applications that do not configure an
https://proxy are not affected. Installations running libcurl 7.52.0 or newer built with HTTPS-proxy support are not affected because HTTPS proxies work as intended. Installations running libcurl 7.50.2 through 7.51.x, or libcurl 7.52.0 or newer built without HTTPS-proxy support, are not exposed to the silent cleartext downgrade, but Guzzle now rejects those unsupported configurations up front as well. The built-in stream handler is not affected; the issue is specific to the cURL handlers' proxy handling. Low-level cURL options under thecurlrequest option, such asCURLOPT_PROXYorCURLOPT_PROXYTYPE, are advanced custom configuration and remain the caller's responsibility.Patches
The issue is patched in
7.12.1and later. Starting in that release, the built-in cURL handlers detect whether the installed libcurl supports HTTPS proxies — requiring both libcurl 7.52.0 or newer and theCURL_VERSION_HTTPS_PROXYfeature bit — and reject a request configured through Guzzle's first-class proxy handling with anhttps://proxy up front by throwing aGuzzleHttp\Exception\RequestException. No request bytes reach the network when the proxy cannot be used securely. Versions before7.12.1are affected by the silent downgrade when run against libcurl older than 7.50.2.Workarounds
If you cannot upgrade immediately, do not configure an
https://proxy on an installation whose libcurl lacks HTTPS-proxy support, and verify the capability in application code before using one. Remember to check proxy environment variables as well as any explicitproxyoption:Upgrading the system libcurl to 7.52.0 or newer built with HTTPS-proxy support also resolves the underlying unsupported-proxy behavior.
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
guzzlehttp/guzzle: Dot-Only Cookie Domains Match All Hosts
CVE-2026-55767 / GHSA-cwxw-98qj-8qjx
More information
Details
Impact
CookieJarincorrectly accepts cookies with a dot-onlyDomainattribute, such asDomain=.,Domain=..,Domain=..., and whitespace-padded variants such asDomain= .. In affected versions,SetCookie::matchesDomain()removes leading dots from the cookie domain, normalizing dot-only values to the empty string;SetCookie::validate()only rejected a strictly empty domain, so these cookies could be stored and the empty normalized domain was treated as matching any request host.An attacker-controlled origin that an application requests with a shared cookie jar can therefore set a cookie that Guzzle later sends to unrelated hosts using the same jar. This may allow cookie injection or session fixation against downstream services, depending on how those services interpret the injected cookie. Applications are affected when they use Guzzle's cookie support, for example
new Client(['cookies' => true])or an explicit sharedCookieJar, and reuse the same jar across attacker-controlled and trusted origins.Applications that do not use Guzzle's cookie support, or that use separate cookie jars per origin or trust boundary, are not affected. This issue is distinct from public suffix list validation: dot-only domains contain no domain label and should not match unrelated hosts.
Patches
The issue is patched in
7.12.1and later. Starting in that release, Guzzle rejects dot-only cookieDomainattributes and prevents an empty normalized cookie domain from matching any request host.Workarounds
If you cannot upgrade immediately, do not reuse the same
CookieJarinstance across untrusted and trusted origins. Use separate cookie jars per origin or trust boundary, or disable cookie handling for requests to untrusted hosts.Avoid using
new Client(['cookies' => true])for clients that may contact unrelated hosts with different trust levels, because that option creates one shared jar for the client.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: Proxy-Authorization headers can be sent to origin servers
CVE-2026-67339 / GHSA-94pj-82f3-465w
More information
Details
Impact
In affected versions, the built-in cURL handlers (
CurlHandlerandCurlMultiHandler) put every first-class request header in cURL's origin header list (CURLOPT_HTTPHEADER). These handlers are the default when the PHP cURL extension is available. They moveProxy-Authorizationto the proxy-only list (CURLOPT_PROXYHEADER) only when Guzzle predicts an HTTP or HTTPS proxy. A "first-class" header is part of the normal request message and can be set on a PSR-7 request, through clientheadersdefaults, theheadersrequest option, or middleware. It does not include a literal line supplied through rawCURLOPT_HTTPHEADER,CURLOPT_PROXYHEADER, orstream_context.http.headercontrols.Because that migration follows Guzzle's prediction rather than the route libcurl actually takes, the credential stays in the origin list and is sent to the origin server when a request is:
proxyset to''to disable proxying.no,no_proxy, orNO_PROXYmatch.AuthorizationandCookie, does not stripProxy-Authorizationcross-origin.On installations whose libcurl is older than 7.37.0, or whose PHP cURL extension lacks
CURLOPT_PROXYHEADER,CURLOPT_HEADEROPT, andCURLHEADER_SEPARATE, no proxy-only channel is available, so cURL left the header in the origin list for every route. The stream handler also serialized first-class values before selecting a proxy. PHP removes only the firstProxy-Authorizationline from CONNECT, so another first-class value or a URL-userinfo Basic line could reach the tunneled origin. A later rawstream_context.http.proxyoverride could instead reroute either credential directly to the origin.The disclosed value is a private credential meant only for the proxy. RFC 9110 defines
Proxy-Authorizationas credentials for the next inbound proxy, and an origin is never an intended recipient. The flaw can silently give a working proxy credential to an unrelated third party. In the worst case, an attacker controls the origin and records the credential through access logs, tracing systems, or application logs. If it remains valid, the attacker can abuse a paid or access-controlled proxy, impersonate the proxy principal, or reach destinations the proxy is trusted to reach. A strong remote exploit is possible when an application sends a request to an attacker-controlled HTTP URL through a proxy with a defaultProxy-Authorizationheader, then follows the attacker's redirect to an HTTPS or no-proxy destination that Guzzle reaches directly.Using a first-class
Proxy-Authorizationheader is a legitimate, documented configuration, so affected applications are not misusing the library. Guzzle does not create this field, so applications that never configure one are unaffected by the first-class-header flaw. Proxy URL userinfo is not affected on its own, but the stream handler could expose its Basic line when combined with a first-class field or a later rawstream_context.http.proxyoverride.CURLOPT_PROXYUSERPWDis unaffected. Literal lines supplied through rawCURLOPT_HTTPHEADER,CURLOPT_PROXYHEADER, orstream_context.http.headerremain caller-controlled and outside the first-class-header guarantee.Patches
The issue is fixed in
7.14.2. The cURL handlers keep first-classProxy-Authorizationvalues out of the origin header list. When proxy header separation is available, they pass the values throughCURLOPT_PROXYHEADERwithCURLHEADER_SEPARATE. An empty value uses cURL's semicolon form to suppress credentials from proxy URL userinfo. On older builds, Guzzle drops the field for direct, bypassed, and SOCKS routes, but fails before network I/O if the request might use an HTTP or HTTPS proxy.The stream handler removes the field from origin headers before choosing a route. If it selects a proxy, it accepts one value, including empty, writes a validated proxy header, and gives that value precedence over proxy URL userinfo. Multiple values, line breaks, and raw proxy overrides that could reroute generated credentials fail before connection. Direct and bypassed requests drop the field. Versions before
7.14.2are affected by these origin-bound credential paths.Workarounds
If you cannot upgrade immediately, remove first-class
Proxy-Authorizationfields from requests, client defaults, and middleware. Supply proxy credentials instead through proxy URL userinfo, for examplehttp://user:pass@proxy.example:8080, or useCURLOPT_PROXYUSERPWDwith the cURL handlers. Do not combine proxy URL userinfo with a first-class field or a rawstream_context.http.proxyoverride. If a first-class field is unavoidable, use libcurl 7.37.0 or newer withCURLOPT_PROXYHEADER,CURLOPT_HEADEROPT, andCURLHEADER_SEPARATE, and ensure the field is never present on a client that can issue direct, bypassed, or SOCKS requests or follow redirects into those routes. A newer libcurl is necessary but does not fix Guzzle's route-dependent migration by itself, and disabling redirects reduces but does not eliminate exposure.References
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: Cookie Disclosure and Injection via IP-Address Domains
CVE-2026-59883 / GHSA-g446-98w2-8p5w
More information
Details
Impact
CookieJardoes not restrict a cookie scoped to an IP address to the exact host that set it. When a stored cookie'sDomainattribute is an IPv4 literal (Domain=192.168.0.1), a bracketed IPv6 literal (Domain=[::1]), or a bare numeric value that denotes an IPv4 address (Domain=1, which is0.0.0.1),SetCookie::matchesDomain()applies ordinary subdomain (suffix) matching to it instead of requiring an exact host. The IP-address check insidematchesDomain()is applied only to the request host and never to the cookie's own domain, and response cookies that carry an explicitDomainattribute are stored as suffix-matchable. As a result Guzzle sends such a cookie to any host whose name ends in that value as a trailing label, for example sending aDomain=192.168.0.1cookie toevil.192.168.0.1, or aDomain=1cookie toevil.1. The same flaw lets a look-alike host store a cookie that Guzzle then sends to the bare IP address. Depending on how the receiving service interprets the cookie, the impact is cross-host cookie disclosure, cookie injection, or session fixation.You are affected if your application uses Guzzle's cookie support, for example
new Client(['cookies' => true])or an explicitCookieJar, reuses one cookie jar across more than one host or trust boundary, and that jar can hold a cookie scoped to an IP-address or bare-numeric host. To actually leak a cookie the application must also resolve and connect to a look-alike host whose name ends in the IP-address label, such asevil.192.168.0.1. Such names are not publicly registrable, so in practice an attacker needs some influence over name resolution, which is realistic on private, split-horizon, container, or development networks. You are not affected if you do not use Guzzle's cookie support, if you use a separate cookie jar per host or trust boundary, or if your jars never hold cookies scoped to IP-address or bare-numeric hosts. This issue is independent of public suffix list validation. Per RFC 6265, an IP-address cookie domain must match only the exact host that set it and must never match a subdomain.Patches
The issue is patched in
7.12.3and later. Starting in that release, Guzzle matches an IPv4 literal, a bracketed IPv6 literal, or a bare-numeric cookieDomainonly against the exact request host, and no longer applies subdomain suffix matching to them.Workarounds
If you cannot upgrade immediately, do not reuse one
CookieJarinstance across untrusted and trusted origins. Use a separate cookie jar per host or trust boundary, or disable cookie handling for requests to untrusted hosts, and avoid scoping cookies to IP-address or bare-numeric hosts. In particular, avoidnew Client(['cookies' => true])for any client that may contact unrelated hosts at different trust levels, because that option creates a single shared jar for the whole client.References
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: Host-only cookie scope is not preserved
CVE-2026-67355 / GHSA-wm3w-8rrp-j577
More information
Details
Impact
In affected versions,
CookieJardoes not preserve whether a response cookie was set without aDomainattribute or with an empty one. A cookie withoutDomainis host-only and must be returned only to the exact host that set it. Under current cookie processing rules, an emptyDomainvalue is also host-only. Guzzle instead stores the request host in the cookie'sDomainfield and later applies ordinary domain matching, as though the server had supplied a valid domain. For example, a host-onlysid=secretcookie set byexample.comcan subsequently be sent tochild.example.com.FileCookieJarandSessionCookieJaralso persist the request host without recording the host-only state, so reloading a jar preserves the widened scope.An attacker who controls or can observe a child host can therefore receive cookies that were intended only for its parent host. Depending on the cookie, this can disclose session identifiers, authorization tokens, or other sensitive state. Exploitation requires the application to enable Guzzle's cookie support, reuse the same built-in cookie jar, receive a host-only cookie from a parent host, and later make a matching request to a less-trusted child host. The cookie's other restrictions still apply. Its path must match, a
Securecookie is sent only over a secure connection, and an expired cookie is not sent.Applications that do not use Guzzle's cookie support are not affected. Applications are also not affected by this disclosure if they use a separate jar for every host or trust boundary, never request a less-trusted subdomain with the same jar, or only store cookies carrying a valid, non-empty
Domainattribute. The incorrect behavior occurs between an otherwise valid parent host and its subdomains.Patches
The issue is patched in
7.15.1and later. Starting in that release, Guzzle records whether a response cookie is host-only and matches it only against the exact host. The host-only flag is part of cookie identity for replacement and response-driven deletion. Cookies carrying a valid, non-emptyDomainattribute retain their existing domain-matching behavior. A host-only cookie can coexist with an explicit-domain cookie having the same name, domain string, and path.The built-in persistent jars now write a boolean
HostOnlymarker for every stored cookie record. They reject records where that marker is missing or is not a boolean, and validate all records before changing the live jar. Persisted cookie records written by an older version are therefore rejected rather than silently interpreted with an unsafe scope. Loading non-empty file or session data written without this marker throws aRuntimeExceptionuntil the data is deleted, regenerated, or correctly annotated. Versions before7.15.1are affected.Workarounds
If you cannot upgrade immediately, do not reuse one
CookieJarinstance across parent and child hosts with different trust levels. Use a separate cookie jar for each host or trust boundary, disable cookie handling for requests to less-trusted hosts, or avoid making those requests through a client configured with a shared jar. In particular, avoidnew Client(['cookies' => true])for a client that may contact both a trusted parent host and less-trusted subdomains, because that option creates one jar for the whole client.When upgrading, delete or rotate existing
FileCookieJarandSessionCookieJardata that contains cookie records written without aHostOnlymarker. Records may instead be annotated manually only when the original presence or absence of the cookie'sDomainattribute is known. The stored domain string is not sufficient to infer it safely.References
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: Unbounded response cookies risk denial of service
CVE-2026-67353 / GHSA-f283-ghqc-fg79
More information
Details
Impact
In affected versions, Guzzle's built-in
CookieJaraccepts any number ofSet-Cookieheader fields from one response, with no limit on the size of each field. When a later request matches the stored cookies, Guzzle places every match into one generatedCookieheader without limiting the number of cookies or the total header length.A malicious or compromised server can therefore return many large cookies, causing Guzzle to store attacker-controlled data in memory and copy it into later request headers. This can increase memory use and processing time. It can also make later requests fail when the generated header exceeds a limit in a handler, HTTP implementation, proxy, or destination server. A server on one sibling host, such as
attacker.example.com, can also set parent-domain cookies that are later selected for another sibling, such asservice.example.com. The denial can therefore affect a different service that uses the same jar.An application is affected when it enables the built-in cookie support, receives an attacker-controlled response, and retains or reuses the jar. The issue affects both built-in handlers because Guzzle manages these cookies itself instead of using libcurl's native cookie engine. cURL addressed a similar denial-of-service issue in CVE-2022-32205 by limiting the cookies it accepts and sends, but those native limits do not protect Guzzle's separate jar. Applications that do not use cookies, use separate jars for untrusted origins, or use a third-party
CookieJarInterfacewith suitable limits are not affected by this behavior. The demonstrated direct impact is limited to availability. The patch does not impose a lifetime limit on a jar built up over an unlimited number of responses or populated directly by application code.Patches
The issue is patched in
7.15.1and later. Starting in that release, the built-inCookieJarignores aSet-Cookiefield value longer than 8,190 bytes and applies at most 50 successful cookie insertions or replacements from one response. When generating a request, it emits at most 150 matchingname=valuepairs and limits the completeCookie:header line to 8,190 bytes, including the field name and following space.These limits follow the same practical shape as cURL's response to CVE-2022-32205. Both bound cookies accepted from one response, cookies added to one request, and generated header size. Guzzle's 8,190-byte incoming field limit is more generous than cURL's current 5,000-byte cookie-line limit. Neither approach adds a global jar quota or an eviction policy. The 8,190-byte incoming boundary is inclusive. Invalid, unrelated, identical, oversized, and deletion fields do not consume the 50-cookie limit. For outgoing requests, Guzzle preserves its existing matching and iteration order. It stops after 150 pairs or before the first matching cookie that would exceed the line limit. The output limits also apply to directly imported cookie state, but that state is not limited when it is added to the jar. Explicit caller-supplied
Cookieheaders and third-party jar implementations remain the caller's responsibility. Versions before7.15.1are affected.Workarounds
If you cannot upgrade immediately, do not enable a shared built-in cookie jar for requests to untrusted origins. Use separate jars per host or trust boundary, disable cookie handling for untrusted requests, and discard or clear a jar after receiving an untrusted response before it is reused. Applications that must accept cookies from untrusted peers can provide a custom
CookieJarInterfaceimplementation that enforces suitable limits.Guzzle does not use libcurl's cookie engine for cookies stored in a
CookieJar. The cURL handler sends theCookieheader that Guzzle's cookie middleware has already built, so libcurl's cookie limits do not apply. Upgrading libcurl therefore does not fix this issue.References
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: URI fragments disclosed in redirect Referer headers
CVE-2026-67354 / GHSA-h95v-h523-3mw8
More information
Details
Impact
When the optional
refererredirect setting is enabled, affected versions ofRedirectMiddlewarecan copy the fragment from the referring request URI into a generatedRefererheader. A URI fragment is the part after#. It is handled locally by the client and is not part of the HTTP request target, so the server handling the original request does not receive it. A generatedReferertells the redirect destination which URI led to the request. Guzzle correctly removes user information from that value, but retains the fragment when it follows a redirect to the same scheme, such as HTTPS to HTTPS. For example, an initial URI ending in#secretis sent without the fragment, but the redirect destination can receive aRefererending in#secret. This behavior affects both the cURL and stream handlers.An attacker who controls the redirect destination can read the fragment from the incoming header, including through request logs or application code. If the fragment contains a one-time login secret, access token, state value, or other private client data, it is disclosed to a server that was never meant to receive it. Exploitation requires the application to enable
allow_redirects.referer, make a request to a URI with a sensitive fragment, and follow a same-scheme redirect to a less-trusted destination.The
referersetting is disabled by default. Applications that leave it disabled, do not put sensitive data in URI fragments, do not follow redirects, or only redirect within the same trust boundary are not affected. Guzzle already omits the generated header when the scheme changes. This issue is limited to the fragment's inclusion. Reducing the path and query on cross-origin redirects is a separate privacy policy question.Patches
The issue is patched in
7.15.1and later. Starting in that release, Guzzle removes both user information and the fragment before generating a redirectReferervalue. Other redirect behavior does not change. Guzzle retains the referring path and query, and continues to omit the header when the scheme changes. Versions before7.15.1are affected when the optionalreferersetting is enabled.Workarounds
If you cannot upgrade immediately, leave automatic Referer generation disabled. It is off by default. If redirect options are configured explicitly, ensure that
refererisfalse:Alternatively, disable automatic redirects and follow trusted destinations manually, or remove the fragment from the request URI before sending a request that may redirect. Do not rely on the fact that fragments are absent from the initial HTTP request target, because the affected middleware can reintroduce them in the generated header.
References
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Guzzle: 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 streamv7.15.1Compare Source
Security
Cookieheaders (GHSA-f283-ghqc-fg79)Refererheaders generated for redirects (GHSA-h95v-h523-3mw8)v7.15.0Compare Source
Added
Multiplexing::NONEsupport as a client, cURL multi handler, and conditional request optionChanged
guzzlehttp/psr7version constraint to^2.13Content-LengthCURLOPT_SHAREcURL option when named connection caps are configuredCURLOPT_PRE_PROXYand opaque share handlesDeprecated
Utils::jsonDecode()andUtils::jsonEncode()in favor of native JSON functionsCURLMOPT_PIPELININGin the cURL multi handleroptionsarrayCURLOPT_PROXYHEADERwithout cURL proxy header separation supportFixed
Content-LengthandTransfer-Encodingwhen redirects discard the request bodydelayrequest option to followed redirectsv7.14.2Compare Source
Security
v7.14.1Compare Source
Changed
guzzlehttp/psr7version constraint to^2.12.5Fixed
@separatorsstream => true) on cap-configured stream handlersoptionsvaluesCURLOPT_HTTPAUTHmask permits NTLMCURLMOPT_PIPELININGwhen combined with explicit multiplexingon_statscallback throwson_trailerscallback is configuredon_trailerscallback before starting a cURL transferon_trailersrequest option on the stream handler, which cannot observe trailersv7.14.0Compare Source
Added
on_trailersrequest option to expose parsed HTTP response trailersmultiplexrequest option withMultiplexing::*modes to control or require HTTP/2 multiplexingmultiplexrequests whenCURLMOPT_PIPELININGdisables multiplexingmax_host_connectionsandmax_total_connectionsclient and cURL multi handler optionsChanged
CURLOPT_SHAREwhen combined with authenticated SOCKS proxy configurationCURLOPT_CERTINFOoptionDeprecated
CURLOPT_PIPEWAITcURL option in favour of themultiplexrequest optionselect_timeoutcURL multi handler option valuesv7.13.3Compare Source
Changed
guzzlehttp/promisesversion constraint to^2.5.1guzzlehttp/psr7version constraint to^2.12.4Fixed
no_proxyvaluesv7.13.2Compare Source
Fixed
RuntimeException[
v7.13.1](https://redirect.github.com/guzzle/guzzle/blob/HEAD/CHANGELOG.mdConfiguration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, 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.