PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / guzzle / src / Handler / CurlVersion.php

CurlVersion.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at vendor_prefixed/guzzlehttp/guzzle/src/Handler/CurlVersion.php

207 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace YoastSEO_Vendor\GuzzleHttp\Handler;
4
5 /**
6 * @internal
7 */
8 final class CurlVersion
9 {
10 private const MIN_VERSION = '7.21.2';
11 private const TLS_12_VERSION = '7.34.0';
12 private const TLS_13_VERSION = '7.52.0';
13 private const CONNECTION_CAP_VERSION = '7.30.0';
14 // CURLOPT_PIPEWAIT exists since libcurl 7.43.0, and multi handles have
15 // multiplexed by default since 7.62.0 - but a 7.65.0-7.65.1 regression
16 // dropped that default, which 7.65.2 restored, so 7.65.2 is the floor at
17 // which PIPEWAIT is reliably effective.
18 private const MULTIPLEX_VERSION = '7.65.2';
19 // libcurl's connection matcher refuses to hand a transfer wanting
20 // HTTP/1.x a pooled connection that already negotiated HTTP/2 or newer
21 // from 7.77.0: ConnectionExists() in lib/url.c gained the check between
22 // the 7.76.0 and 7.77.0 releases. The HTTP/2 branch of the check
23 // regressed to a debug log in 8.11.0 (curl commit 433d730) and was
24 // restored in 8.13.0 via the negotiation mask (curl commit db72b8d), so
25 // 8.11.0 through 8.12.1 are vulnerable again.
26 private const HTTP_VERSION_REUSE_MATCH_VERSION = '7.77.0';
27 private const HTTP_VERSION_REUSE_MATCH_REGRESSION = '8.11.0';
28 private const HTTP_VERSION_REUSE_MATCH_RESTORED = '8.13.0';
29 // CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE restricts the ALPN offer to h2 only
30 // since libcurl 8.10.0, and connection reuse matching stopped handing
31 // lower-version connections to prior-knowledge transfers in 8.14.0; below
32 // that, a required request could silently be sent over a reused HTTP/1.1
33 // connection.
34 private const REQUIRED_MULTIPLEX_VERSION = '8.14.0';
35 // curl 7.52.0 introduced HTTPS proxy support, advertised by a feature bit
36 // (a build can meet the version yet lack the feature). Earlier libcurl
37 // mishandles an https:// proxy: before 7.50.2 it silently downgrades to a
38 // plaintext HTTP proxy, and 7.50.2 through 7.51 reject it at connect time.
39 private const HTTPS_PROXY_VERSION = '7.52.0';
40 private const HANDLER_SHARING_VERSION = '7.35.0';
41 private const SSL_SESSION_SHARING_VERSION = '8.6.0';
42 // curl 7.57.0 added share-handle connection caches through
43 // CURL_LOCK_DATA_CONNECT; older share objects can only hold DNS, TLS
44 // session, and cookie data, never connections.
45 private const SHARE_CONNECTION_CACHE_VERSION = '7.57.0';
46 // curl 7.83.1 added proxy TLS-SRP to the connection-reuse match
47 // (CVE-2022-27782); the proxy client certificate was matched from 7.52.0,
48 // so proxy TLS credentials are trusted from 7.83.1 onwards.
49 private const PROXY_TLS_CREDENTIAL_REUSE_VERSION = '7.83.1';
50 // curl 8.19.0 fixed proxy tunnel reuse after credential changes
51 // (CVE-2026-3784), but related proxy credential leak flaws were only
52 // fixed in 8.20.0, so connection reuse is trusted from 8.20.0 onwards.
53 private const PROXY_CREDENTIAL_REUSE_VERSION = '8.20.0';
54 // curl 7.69.0 started comparing SOCKS proxy credentials when matching
55 // connections for reuse (curl #4835); older libcurl matches a SOCKS proxy
56 // by type, host, and port only.
57 private const SOCKS_PROXY_CREDENTIAL_REUSE_VERSION = '7.69.0';
58 private const PROXY_HEADER_SEPARATION_VERSION = '7.37.0';
59 /**
60 * @var array{version: string, features: int}|false|null
61 */
62 private static $versionInfo;
63 private function __construct()
64 {
65 }
66 public static function supportsCurlHandler() : bool
67 {
68 $version = self::getVersion();
69 return $version !== null && \version_compare($version, self::MIN_VERSION, '>=');
70 }
71 public static function supportsTls12() : bool
72 {
73 $version = self::getVersion();
74 return self::supportsSsl() && \defined('CURL_SSLVERSION_TLSv1_2') && $version !== null && \version_compare($version, self::TLS_12_VERSION, '>=');
75 }
76 public static function supportsTls13() : bool
77 {
78 $version = self::getVersion();
79 return self::supportsSsl() && \defined('CURL_SSLVERSION_TLSv1_3') && $version !== null && \version_compare($version, self::TLS_13_VERSION, '>=');
80 }
81 public static function supportsHttp2() : bool
82 {
83 $versionInfo = self::getVersionInfo();
84 return self::supportsTls12() && \defined('CURL_VERSION_HTTP2') && $versionInfo !== null && 0 !== (\CURL_VERSION_HTTP2 & $versionInfo['features']);
85 }
86 public static function supportsMultiplex() : bool
87 {
88 $version = self::getVersion();
89 return \defined('CURLOPT_PIPEWAIT') && $version !== null && \version_compare($version, self::MULTIPLEX_VERSION, '>=');
90 }
91 public static function supportsHttpVersionReuseMatching() : bool
92 {
93 $version = self::getVersion();
94 if ($version === null || \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_VERSION, '<')) {
95 return \false;
96 }
97 return \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_REGRESSION, '<') || \version_compare($version, self::HTTP_VERSION_REUSE_MATCH_RESTORED, '>=');
98 }
99 public static function supportsConnectionCaps() : bool
100 {
101 $version = self::getVersion();
102 return \defined('CURLMOPT_MAX_HOST_CONNECTIONS') && \defined('CURLMOPT_MAX_TOTAL_CONNECTIONS') && $version !== null && \version_compare($version, self::CONNECTION_CAP_VERSION, '>=');
103 }
104 public static function ensureConnectionCapsSupported(string $option) : void
105 {
106 if (self::supportsConnectionCaps()) {
107 return;
108 }
109 throw new \InvalidArgumentException(\sprintf('The "%s" option requires PHP cURL support for CURLMOPT_MAX_HOST_CONNECTIONS and CURLMOPT_MAX_TOTAL_CONNECTIONS with libcurl %s or newer.', $option, self::CONNECTION_CAP_VERSION));
110 }
111 public static function supportsRequiredMultiplex() : bool
112 {
113 $version = self::getVersion();
114 return \defined('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE') && $version !== null && self::supportsHttp2() && \version_compare($version, self::REQUIRED_MULTIPLEX_VERSION, '>=');
115 }
116 public static function supportsHttpsProxy() : bool
117 {
118 $versionInfo = self::getVersionInfo();
119 // CURL_VERSION_HTTPS_PROXY is not defined on every supported PHP
120 // version; fall back to the curl.h bit value.
121 $httpsProxyFeature = \defined('CURL_VERSION_HTTPS_PROXY') ? \CURL_VERSION_HTTPS_PROXY : 1 << 21;
122 return $versionInfo !== null && \version_compare($versionInfo['version'], self::HTTPS_PROXY_VERSION, '>=') && 0 !== ($httpsProxyFeature & $versionInfo['features']);
123 }
124 public static function supportsNtlm() : bool
125 {
126 $versionInfo = self::getVersionInfo();
127 // CURL_VERSION_NTLM is not defined on every supported PHP version; fall
128 // back to the curl.h bit value.
129 $ntlmFeature = \defined('CURL_VERSION_NTLM') ? \CURL_VERSION_NTLM : 1 << 4;
130 return \defined('CURLAUTH_NTLM') && $versionInfo !== null && 0 !== ($ntlmFeature & $versionInfo['features']);
131 }
132 public static function supportsHandlerSharing() : bool
133 {
134 $version = self::getVersion();
135 return $version !== null && \version_compare($version, self::HANDLER_SHARING_VERSION, '>=');
136 }
137 public static function ensureHandlerSharingSupported() : void
138 {
139 if (!self::supportsHandlerSharing()) {
140 throw new \InvalidArgumentException(\sprintf('The "transport_sharing" option requires libcurl %s or higher for cURL share handles.', self::HANDLER_SHARING_VERSION));
141 }
142 }
143 public static function supportsSslSessionSharing() : bool
144 {
145 $version = self::getVersion();
146 return self::supportsSsl() && $version !== null && \version_compare($version, self::SSL_SESSION_SHARING_VERSION, '>=');
147 }
148 public static function ensureSslSessionSharingSupported() : void
149 {
150 if (!self::supportsSslSessionSharing()) {
151 throw new \InvalidArgumentException(\sprintf('The "transport_sharing" option requires libcurl %s or higher with SSL support for SSL session sharing.', self::SSL_SESSION_SHARING_VERSION));
152 }
153 }
154 public static function supportsShareConnectionCaches() : bool
155 {
156 $version = self::getVersion();
157 // An undetectable libcurl version is treated as capable so the
158 // opaque share safeguards fail closed.
159 return $version === null || \version_compare($version, self::SHARE_CONNECTION_CACHE_VERSION, '>=');
160 }
161 public static function supportsProxyTlsCredentialAwareConnectionReuse() : bool
162 {
163 $version = self::getVersion();
164 return $version !== null && \version_compare($version, self::PROXY_TLS_CREDENTIAL_REUSE_VERSION, '>=');
165 }
166 public static function supportsProxyCredentialAwareConnectionReuse() : bool
167 {
168 $version = self::getVersion();
169 return $version !== null && \version_compare($version, self::PROXY_CREDENTIAL_REUSE_VERSION, '>=');
170 }
171 public static function supportsSocksProxyCredentialAwareConnectionReuse() : bool
172 {
173 $version = self::getVersion();
174 return $version !== null && \version_compare($version, self::SOCKS_PROXY_CREDENTIAL_REUSE_VERSION, '>=');
175 }
176 public static function supportsProxyHeaderSeparation() : bool
177 {
178 $version = self::getVersion();
179 return $version !== null && \version_compare($version, self::PROXY_HEADER_SEPARATION_VERSION, '>=') && \defined('CURLOPT_PROXYHEADER') && \defined('CURLOPT_HEADEROPT') && \defined('CURLHEADER_SEPARATE');
180 }
181 private static function supportsSsl() : bool
182 {
183 $versionInfo = self::getVersionInfo();
184 return \defined('CURL_VERSION_SSL') && $versionInfo !== null && 0 !== (\CURL_VERSION_SSL & $versionInfo['features']);
185 }
186 public static function getVersion() : ?string
187 {
188 $versionInfo = self::getVersionInfo();
189 return $versionInfo === null ? null : $versionInfo['version'];
190 }
191 /**
192 * @return array{version: string, features: int}|null
193 */
194 private static function getVersionInfo() : ?array
195 {
196 if (self::$versionInfo === null) {
197 if (!\function_exists('curl_version')) {
198 self::$versionInfo = \false;
199 } else {
200 $versionInfo = \curl_version();
201 self::$versionInfo = \is_array($versionInfo) && isset($versionInfo['version'], $versionInfo['features']) && \is_string($versionInfo['version']) && \is_int($versionInfo['features']) ? ['version' => $versionInfo['version'], 'features' => $versionInfo['features']] : \false;
202 }
203 }
204 return self::$versionInfo === \false ? null : self::$versionInfo;
205 }
206 }
207