Make WordPress Core

Opened 7 days ago

Last modified 7 days ago

#66462 new defect (bug)

wp_read_image_metadata() uses a bare EXIF UserComment character code ("ASCII") as the title and caption

Reported by: lessbloat Owned by:
Priority: normal Milestone: 7.2
Component: Media Version: 6.5
Severity: normal Keywords: has-patch has-unit-tests
Cc: Focuses:

Description

Some JPEGs carry an EXIF UserComment that holds only part of the character code and nothing else. Uploading one gives the attachment the title "ASCII" and the caption "ASCII". The caption then appears in the image block, for example when uploading from the WordPress/Jetpack mobile apps or the block editor.

We found this with photos exported from Picasa. Their UserComment tag is 5 bytes long:

Tag 0x9286 (5 bytes, undef[5]):  41 53 43 49 49  [ASCII]

The EXIF spec says UserComment starts with an 8-byte character code (ASCII\0\0\0, UNICODE\0, JIS\0\0\0\0\0, or eight null bytes), followed by the comment. This tag has a truncated code and no comment, so it should be treated as empty.

PHP's exif_read_data() strips the character code only when the value is at least 8 bytes long. For this file it returns the raw value:

$exif['COMPUTED']['UserComment'] === 'ASCII'

Before [57267], wp_read_image_metadata() only read UserComment when ImageDescription was also present, so these files produced no title or caption. Since 6.5 the new elseif branch uses UserComment on its own, so the bare code becomes both the caption and, being under 80 characters, the title. When ImageDescription is present, the code ends up as the caption (when the title came from the description) or is appended to it ("My description ASCII").

Steps to reproduce

  1. Generate a test image with this script (requires GD):
<?php
// Writes bare-usercomment.jpg: a JPEG whose EXIF UserComment is the 5 bytes "ASCII".
$image = imagecreatetruecolor( 8, 8 );
ob_start();
imagejpeg( $image );
$jpeg = ob_get_clean();

$ifd0     = pack( 'n', 1 ) . pack( 'nnNN', 0x8769, 4, 1, 26 ) . pack( 'N', 0 );
$exif_ifd = pack( 'n', 1 ) . pack( 'nnNN', 0x9286, 7, 5, 44 ) . pack( 'N', 0 );
$exif     = "Exif\x00\x00MM\x00\x2a\x00\x00\x00\x08" . $ifd0 . $exif_ifd . 'ASCII';

file_put_contents( 'bare-usercomment.jpg', substr( $jpeg, 0, 2 ) . "\xff\xe1" . pack( 'n', strlen( $exif ) + 2 ) . $exif . substr( $jpeg, 2 ) );
  1. Upload bare-usercomment.jpg in Media > Add New, or insert it with an Image block.

Expected: the attachment's title is the file name and the caption is empty.

Actual: the title is "ASCII" and the caption is "ASCII".

You can also check directly:

require_once ABSPATH . 'wp-admin/includes/image.php';
$meta = wp_read_image_metadata( 'bare-usercomment.jpg' );
// $meta['title'] === 'ASCII', $meta['caption'] === 'ASCII'

Proposed fix

In wp_read_image_metadata(), discard a UserComment that is only a character code:

if ( ! empty( $exif['COMPUTED']['UserComment'] ) ) {
	$exif_usercomment = trim( $exif['COMPUTED']['UserComment'] );

	/*
	 * PHP returns a UserComment shorter than its 8-byte character code
	 * as the bare code, such as "ASCII". It holds no comment.
	 */
	if ( in_array( $exif_usercomment, array( 'ASCII', 'UNICODE', 'JIS' ), true ) ) {
		$exif_usercomment = '';
	}
}

trim() already removes the null padding, so this also covers codes that are partially padded (for example ASCII\0\0). Valid UserComments are unaffected, because PHP strips the full 8-byte code from them.

Tests would go in tests/phpunit/tests/image/meta.php, with a fixture like the one above in tests/phpunit/data/images/. Cases to cover:

  • bare code, no ImageDescription: title and caption are empty;
  • bare code with a short ImageDescription: the title and caption are the description;
  • bare code with an ImageDescription over 80 characters: the caption is the description, with no " ASCII" suffix;
  • a valid ASCII\0\0\0My comment: the title and caption are "My comment".

Related: [57267].

Attachments (1)

exif-user-comment-test.png​ (81.6 KB ) - added by azaozz 7 days ago.
EXIF user-comment test

Download all attachments as: .zip

Change History (4)

This ticket was mentioned in ​PR #15434 on ​WordPress/wordpress-develop by ​@sarequl.


7 days ago
#1

  • Keywords has-patch has-unit-tests added

Fixes a bare EXIF UserComment character code being used as the image title and caption.

See https://core.trac.wordpress.org/ticket/66462

What

A JPEG whose EXIF UserComment holds only a truncated character code — for example the five bytes ASCII — makes wp_read_image_metadata() set both the title and the caption to "ASCII". The same happens when the bare code is UNICODE or JIS.

Why

PHP's exif_read_data() returns a UserComment shorter than its eight-byte character code as the bare code itself. WordPress then treats that bare code as comment text, so a truncated header produces a meaningless title and caption on the attachment.

How

After trim(), the UserComment is now discarded when it is exactly one of the character codes ASCII, UNICODE, or JIS. A bare code holds no comment, so the metadata falls back to ImageDescription (or to nothing). Valid comments, including ones that contain a code word as part of a longer string, are unchanged.

Adds four regression tests and four JPEG fixtures covering the bare-code cases with and without a description, plus a valid UserComment.

Testing Instructions

  1. Upload the fixture tests/phpunit/data/images/bare-usercomment.jpg (EXIF UserComment = ASCII) to the Media Library. The attachment title and caption are empty, not ASCII. On trunk both are ASCII.
  2. Upload valid-usercomment.jpg (EXIF UserComment = My comment). The title and caption are My comment.
  3. Upload bare-usercomment-short-description.jpg (bare ASCII code plus ImageDescription = My description). The title and caption come from the description.
  4. Run the test: vendor/bin/phpunit tests/phpunit/tests/image/meta.php → OK (18 tests, 96 assertions). Reverting src/wp-admin/includes/image.php to the base commit makes three of the new tests fail.
  5. Broader regression: vendor/bin/phpunit tests/phpunit/tests/image/{functions,dimensions,size,alttext,header,meta}.php all pass.

Note

The fix uses the heuristic proposed in the ticket. A *valid* UserComment whose text is exactly ASCII, JIS, or UNICODE is also emptied, because PHP strips the code and the remaining text equals the bare code. This is inherent to the heuristic, and the probability of a real comment consisting solely of one of those words is negligible.

#2 @matt
7 days ago

I ran into this recently with photos from my Nikon, would love this to be fixed.

#3 @azaozz
7 days ago

  • Milestone Awaiting Review → 7.2

Can confirm using the ​example image from the PR.

The PR fixes it and seems good. Moving to next major.

Note: See TracTickets for help on using tickets.