Changeset 63251
- Timestamp:
- 08/12/2026 04:35:03 PM (3 days ago)
- Location:
- branches/5.0
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
src/wp-includes/class-wp-image-editor-imagick.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/5.0
-
branches/5.0/src/wp-includes/class-wp-image-editor-imagick.php
r41288 r63251 72 72 'flopimage', 73 73 'readimage', 74 'readimageblob', 74 75 ); 75 76 … … 131 132 return true; 132 133 133 if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) 134 $is_stream = preg_match( '|^https?://|', $this->file ); 135 $is_file = ! $is_stream && is_file( $this->file ); 136 137 // Only allow loading files or HTTP streams. 138 if ( ! $is_file && ! $is_stream ) 134 139 return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); 140 141 // Establish the provided filename based on the kind of resource being loaded. 142 $given_filename = $this->file; 143 if ( 0 === strncasecmp( $given_filename, 'file://', 7 ) ) { 144 $given_filename = basename( substr( $given_filename, 7 ) ); // 7 is the strlen of 'file://'. 145 } elseif ( 1 === preg_match( '~^https?://~i', $this->file ) ) { 146 /* 147 * For URLs, it will be the final path segment. 148 * 149 * Example: 150 * 151 * https://wordpress.org/i/happy.png?size=40px 152 * ╰───────╯ 153 * this is the given filename 154 * 155 * If the stream returns a `Content-Disposition` header it would 156 * provide an alternative name, but this is used as a reasonable 157 * proxy to avoid adding the additional complexity of reading and 158 * parsing the returned HTTP headers. 159 */ 160 $url_path = wp_parse_url( $this->file, PHP_URL_PATH ); 161 162 // This URL can not be parsed, so it is not a valid image resource. 163 if ( false === $url_path ) { 164 return new WP_Error( 'error_loading_image', __( 'File is not an image.' ), $this->file ); 165 } 166 167 /** 168 * The URL has an empty path, so continue with an empty string. 169 * 170 * This is the case with a URL such as `https://example.com?file_id=123` 171 */ 172 if ( null === $url_path ) { 173 $url_path = ''; 174 } 175 176 $last_path_at = strrpos( $url_path, '/' ); 177 $given_filename = is_int( $last_path_at ) ? substr( $url_path, $last_path_at + 1 ) : $url_path; 178 $given_filename = rawurldecode( $given_filename ); 179 } 180 181 /* 182 * Strip off any potential `Imagick` format specifiers. 183 * 184 * If a real file exists with the identified format specifier, then 185 * `Imagick` may not treat it as a format, but WordPress will reject 186 * it anyway to avoid adding more complexity into this detection. 187 * 188 * `Imagick` reads only the first `FORMAT:` specifier on a name, but 189 * stripping a segment would promote a second specifier to the front 190 * of the name handed to `Imagick`, which would then honor it. 191 * 192 * Loop to capture all format specifiers for comparison. 193 * 194 * Exclude Windows drive-letter prefixes from here. 195 */ 196 $imagick_formats = array(); 197 while ( 198 false !== ( $format_ends_at = strpos( $given_filename, ':' ) ) && 199 1 !== preg_match( '~^[a-z]:~i', $given_filename ) 200 ) { 201 $imagick_formats[] = strtoupper( substr( $given_filename, 0, $format_ends_at ) ); 202 $given_filename = substr( $given_filename, $format_ends_at + 1 ); 203 } 204 205 $file_extension = strtolower( pathinfo( $given_filename, PATHINFO_EXTENSION ) ); 135 206 136 207 /* … … 140 211 wp_raise_memory_limit( 'image' ); 141 212 213 /** 214 * Read the resource header for MIME sniffing. 215 * 216 * For files, which will be passed into Imagick by their file names, avoid 217 * eagerly loading the entire contents into PHP memory. For streams, however, 218 * it’s more important to avoid validating a separate copy of the file data 219 * than is later fetched by Imagick, so go ahead and load the entire payload, 220 * then pass it to Imagick as the data blob itself. 221 * 222 * @link https://mimesniff.spec.whatwg.org/#reading-the-resource-header 223 */ 224 try { 225 if ( $is_file ) { 226 $file_data = file_get_contents( $this->file, false, null, 0, 1445 ); 227 } else { 228 $file_data = file_get_contents( $this->file ); 229 } 230 } catch ( Exception $e ) { 231 $file_data = false; 232 } 233 if ( false === $file_data ) { 234 return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file ); 235 } 236 237 $pdf_extensions = array( 238 'ai', 239 'epdf', 240 'pdf', 241 'pdfa', 242 'pocketmod', 243 ); 244 245 // Reject files claiming to be PDFs which lack the required signature. 246 $has_pdf_extension = in_array( $file_extension, $pdf_extensions, true ); 247 $has_pdf_signature = 0 === strncmp( $file_data, '%PDF-', 5 ); 248 if ( $has_pdf_extension && ! $has_pdf_signature ) { 249 return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); 250 } 251 252 $ps_formats = array( 253 'DPS', 254 'EPI', 255 'EPS', 256 'EPSF', 257 'EPSI', 258 'PS', 259 'WPG', 260 ); 261 262 $ps_extensions = array( 263 'dps', 264 'epi', 265 'eps', 266 'eps2', 267 'eps3', 268 'epsf', 269 'epsi', 270 'ept', 271 'ept2', 272 'ept3', 273 'ps', 274 'ps2', 275 'ps3', 276 'wpg', 277 ); 278 279 // Reject files which Imagick will parse as PostScript. 280 if ( 281 array() !== array_intersect( $imagick_formats, $ps_formats ) || 282 in_array( $file_extension, $ps_extensions, true ) || 283 0 === strncmp( $file_data, '%!', 2 ) || 284 0 === strncmp( $file_data, "\x04%!", 3 ) || 285 0 === strncmp( $file_data, "\xC5\xD0\xD3\xC6", 4 ) || 286 0 === strncmp( $file_data, "\xFFWPC", 3 ) 287 ) { 288 return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); 289 } 290 291 $compressed_extensions = array( 292 'gz', 293 'bz2', 294 'svgz', 295 'z', 296 'wmz', 297 ); 298 299 // Reject compressed archives that Imagick will transparently decompress. 300 if ( 301 in_array( $file_extension, $compressed_extensions, true ) || 302 0 === strncmp( $file_data, "\x1F\x8B\x08", 3 ) || // gzip 303 0 === strncmp( $file_data, 'BZh', 3 ) || // bzip2 304 0 === strncmp( $file_data, "\x1F\x9D", 2 ) // compress 305 ) { 306 return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file ); 307 } 308 142 309 try { 143 310 $this->image = new Imagick(); 144 $file_extension = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); 145 $filename = $this->file; 146 147 if ( 'pdf' == $file_extension ) { 311 $filename = $this->file; 312 313 if ( $has_pdf_signature ) { 148 314 $filename = $this->pdf_setup(); 149 315 } 150 316 151 // Reading image after Imagick instantiation because `setResolution` 152 // only applies correctly before the image is read. 153 $this->image->readImage( $filename ); 317 if ( $is_file ) { 318 $this->image->readImage( $filename ); 319 } else { 320 $this->image->readImageBlob( $file_data, $given_filename ); 321 } 154 322 155 323 if ( ! $this->image->valid() )
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)