REST API: Normalize non-integer attachment filesize metadata#12611
REST API: Normalize non-integer attachment filesize metadata#12611apermo wants to merge 28 commits into
Conversation
Attachment metadata is untyped, so `filesize` can be stored as a string (for example, plugins that populate it from a remote storage API response such as the Google Drive `fileSize` field, which the API returns as a string). Returning it verbatim violated the `?int` return type of `WP_REST_Attachments_Controller::get_attachment_filesize()` and caused a fatal TypeError for non-numeric values. Only return the stored value when numeric, casting to int; otherwise fall through to recompute the size from the file via wp_filesize(). Fixes https://core.trac.wordpress.org/ticket/65670 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
@westonruter would you be able to review this? It fixes a fatal |
|
@westonruter Would you be so kind to have a look at it? Thanks :) |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Shorten the inline comment in get_attachment_filesize() to the essential rationale, per PR review feedback. See https://core.trac.wordpress.org/ticket/65670 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…HPStan issues in tests
There was a problem hiding this comment.
Pull request overview
This PR fixes a REST API fatal error in WP_REST_Attachments_Controller::get_attachment_filesize() by ensuring attachment filesize metadata (which is untyped postmeta) is normalized to an integer (or recomputed) before being returned under a strict ?int return type.
Changes:
- Normalize numeric
filesizemetadata to anint, and fall back to recomputing the file size when metadata is not usable. - Add REST API regression tests covering numeric-string and non-numeric
filesizemetadata cases. - Add an extra sanity assertion in an existing attachment REST test to ensure the factory returns an integer attachment ID.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/phpunit/tests/rest-api/rest-attachments-controller.php | Adds regression tests validating filesize normalization and non-numeric recovery behavior. |
| src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php | Hardens get_attachment_filesize() by validating/casting stored metadata before returning it. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Refine the filesize metadata guard per PR review: accept only a positive integer (an int or a digit-only string), rather than any numeric value. This avoids silently truncating float strings such as '123.4' via the int cast, and rejects zero and negative values, falling through to recompute the size from the file in those cases. Also document the method's positive-integer return with a @phpstan-return non-negative-int|null annotation. See https://core.trac.wordpress.org/ticket/65670 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Attachment metadata is untyped, so `filesize` may be a float, bool or other non-string scalar. Passing such a value to ctype_digit() is fragile (deprecated in PHP 8.1+), so guard the call with is_string(); non-string scalars now fall through to recompute the size from the file. Extend the invalid-value data provider with float and boolean cases. See https://core.trac.wordpress.org/ticket/65670 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
I fixed the typing of this function as otherwise get_attachment_filesize() would not be guaranteed to return a non-negative-int. This would warrant additional tests to ensure negative values get increased to zero.
Could there be any reason any plugin would filter pre_wp_filesize or wp_filesize to be negative?
There was a problem hiding this comment.
Besides the obvious "Developer did a bad job, or had some quirky idea"?
I don't think so, but that alone is already bad enough.
There was a problem hiding this comment.
No technically legitimate reason, but definitely a reason.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php:2369
is_numeric( $meta['filesize'] )allows non-integer numeric strings (e.g.'123.4','1e3') and floats; casting those to(int)can silently change the stored value. Sincefilesizeis a byte count (integer per REST schema), it’s safer to only accept a positiveintor a digit-only string and otherwise fall back towp_filesize().
if ( isset( $meta['filesize'] ) && is_numeric( $meta['filesize'] ) && $meta['filesize'] > 0 ) {
return (int) $meta['filesize'];
}
tests/phpunit/tests/rest-api/rest-attachments-controller.php:1108
- To prevent regressions where non-integer numeric values get truncated (or where non-string scalars would hit
ctype_digit()), consider adding'123.4','1e3', and123.0to the invalidfilesizeprovider so the test asserts the response falls back to the actual on-disk file size.
return array(
'non-numeric string' => array( 'corrupt' ),
'boolean' => array( true ),
'zero' => array( 0 ),
'zero string' => array( '0' ),
'negative integer' => array( -5 ),
State the zero-or-greater guarantee the same way in the function docblock as in the wp_filesize hook docblock, rather than phrasing it two different ways in one function. Describe the pre_wp_filesize short-circuit as accepting a non-negative number, since a non-negative numeric string bypasses the filesize call too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover a negative int, a negative numeric string, and a negative float, since the filtered value is now normalized with is_numeric() rather than is_int() and each of those types reaches the sign check differently. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| $schema['properties']['filesize'] = array( | ||
| 'description' => __( 'Attachment file size in bytes.' ), | ||
| 'type' => 'integer', | ||
| 'type' => array( 'integer', 'null' ), |
There was a problem hiding this comment.
Claude pointed out that null could be returned by the endpoint.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php:2369
get_attachment_filesize()currently treats any positiveis_numeric()metadata value as valid and then(int)-casts it. This accepts values like '123.4' and '1e3', which get silently truncated/normalized (123 and 1000) even though a byte size should be an integer. That can surface an incorrectfilesizein the REST response rather than falling back towp_filesize().
if ( isset( $meta['filesize'] ) && is_numeric( $meta['filesize'] ) && $meta['filesize'] > 0 ) {
return (int) $meta['filesize'];
}
tests/phpunit/tests/rest-api/rest-attachments-controller.php:1062
- This docblock says invalid
filesizevalues should not cause a "silently truncated size", but thedata_valid_filesize_meta()provider above explicitly asserts truncation/normalization for values like '123.4'. The documentation here should be updated to match the behavior under test.
* Ensures a `filesize` metadata value that is not a positive integer does
* not cause a fatal TypeError or a silently truncated size, falling back to
* the actual file size instead.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php:2369
is_numeric( $meta['filesize'] )will treat values like'123.4'and'1e3'as valid, and casting those to(int)silently changes the stored value (truncation / notation parsing). Sincefilesizerepresents a byte count, it should only accept integer values (int, integer-like float with no fractional part, or digit-only string) and otherwise fall back towp_filesize().
if ( isset( $meta['filesize'] ) && is_numeric( $meta['filesize'] ) && $meta['filesize'] > 0 ) {
return (int) $meta['filesize'];
}
tests/phpunit/tests/rest-api/rest-attachments-controller.php:1056
data_valid_filesize_meta()currently treats float/scientific-notation strings as “valid” and asserts truncation via(int)casting. Iffilesizeis meant to be an integer byte-count, these should be treated as invalid metadata and the controller should fall back to the real file size instead (avoids silent truncation).
return array(
'integer string' => array( '123456', 123456 ),
'float string' => array( '123.4', 123 ),
'scientific notation' => array( '1e3', 1000 ),
'float' => array( 123.0, 123 ),
);
…nt`. A negative file size is clearly impossible, and the return value of 0 is already documented as being the error case. * Values filtered by `pre_wp_filesize` and `wp_filesize` are cast to `int` if they are numeric. * Non-numeric values returned by the `wp_filesize` filter are discarded in favor of zero. * Negative values filtered by the `pre_wp_filesize` filter are treated the same as `null` (and do not short-circuit). * Negative values returned by the `wp_filesize` filter are clamped to be at least zero. Developed as part of #12611. Follow-up to r52837, r52932. Props westonruter, apermo. See #65670, #64898. git-svn-id: https://develop.svn.wordpress.org/trunk@62813 602fd350-edb4-49c9-b593-d223f7449a82
…nt`. A negative file size is clearly impossible, and the return value of 0 is already documented as being the error case. * Values filtered by `pre_wp_filesize` and `wp_filesize` are cast to `int` if they are numeric. * Non-numeric values returned by the `wp_filesize` filter are discarded in favor of zero. * Negative values filtered by the `pre_wp_filesize` filter are treated the same as `null` (and do not short-circuit). * Negative values returned by the `wp_filesize` filter are clamped to be at least zero. Developed as part of WordPress/wordpress-develop#12611. Follow-up to r52837, r52932. Props westonruter, apermo. See #65670, #64898. Built from https://develop.svn.wordpress.org/trunk@62813 git-svn-id: http://core.svn.wordpress.org/trunk@62093 1a063a9b-81f0-0310-95a4-ce76da25c4cd
* The return value of the `WP_REST_Attachments_Controller::get_attachment_filesize()` method is narrowed from `int|null` to `non-negative-int|null`. * The stored `filesize` attachment metadata is only returned if it is a numeric value and is greater than zero; it is cast to an `int`, preventing a `TypeError` if a non-numeric string was stored in metadata. * The `filesize` property of the attachments endpoint adds `null` as a possible value in addition to `integer`. Developed in #12611. Follow-up to r62813, r61703. Props apermo, westonruter, xate, mukesh27. Fixes #65670. git-svn-id: https://develop.svn.wordpress.org/trunk@62815 602fd350-edb4-49c9-b593-d223f7449a82
* The return value of the `WP_REST_Attachments_Controller::get_attachment_filesize()` method is narrowed from `int|null` to `non-negative-int|null`. * The stored `filesize` attachment metadata is only returned if it is a numeric value and is greater than zero; it is cast to an `int`, preventing a `TypeError` if a non-numeric string was stored in metadata. * The `filesize` property of the attachments endpoint adds `null` as a possible value in addition to `integer`. Developed in WordPress/wordpress-develop#12611. Follow-up to r62813, r61703. Props apermo, westonruter, xate, mukesh27. Fixes #65670. Built from https://develop.svn.wordpress.org/trunk@62815 git-svn-id: http://core.svn.wordpress.org/trunk@62095 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Trac ticket
https://core.trac.wordpress.org/ticket/65670
Problem
WP_REST_Attachments_Controller::get_attachment_filesize()(new in 7.0.0) returns thefilesizevalue from attachment metadata verbatim against a strict?intreturn type. Attachment metadata is untyped postmeta, so a non-numeric string value throws a fatalTypeErrorwhen the media endpoint is requested:A numeric string only survives because the file has no
strict_typesdeclaration (coercion). A non-numeric one fatals.Plugins populate
filesizefrom remote storage API responses — e.g. the Google DrivefileSizefield (returned as a string) via WP Media Folder, and the equivalent Dropbox/OneDrive addons — so this occurs on real sites.Fix
Only return the stored value when it is numeric, cast to
int; otherwise fall through to recompute the size from the file viawp_filesize()(which always casts to int), returningnullif unavailable. This matches the'type' => 'integer'REST schema.Testing
Two regression tests added to
tests/phpunit/tests/rest-api/rest-attachments-controller.php:filesizemeta → normalized tointin the response;filesizemeta → no fatal, falls back to the real file size.The non-numeric test reproduces the reported
TypeErroragainst unpatchedtrunk(red), and passes with the fix (green).Disclosure
Claude (Anthropic) assisted in diagnosing the root cause and drafting the patch and tests. I reviewed the change, verified the red/green test behavior locally, and take responsibility for it.