Commit f4d6215eec for wordpress.org
commit f4d6215eece693d363490de9429572b4fe9668d5
Author: SergeyBiryukov <sergeybiryukov@git.wordpress.org>
Date: Mon Sep 7 17:16:50 2026 +0000
External Libraries: Update getID3 to version 1.9.26.
The release hardens parsing of malformed or truncated files (ID3v2 frame size clamped to filesize, no negative seeks for APE/Lyrics3 on small files, bounds check in the QuickTime ES descriptor length parser), replaces the error-suppressed `@list()` destructuring with `array_pad()`, switches `is_integer()` to `is_int()`, and escapes command paths for the Windows `vorbiscomment.exe` helper.
Notably, 1.9.26 also incorporates the change from r61889 (removing `LIBXML_NOENT` from `GETID3_LIBXML_OPTIONS`) upstream, so `getid3.lib.php` no longer needs a local patch. All 16 bundled files are now identical to upstream v1.9.26 again.
Only the bundled audio/video subset was updated. The new upstream modules (`module.audio-video.isobmff.php`, `module.graphic.jpegxl.php`, …) are not included, consistent with prior updates, none of the bundled modules gained a new hard dependency.
References:
* [https://github.com/JamesHeinrich/getID3/releases/tag/v1.9.26 getID3 1.9.26 release notes]
* [https://github.com/JamesHeinrich/getID3/compare/v1.9.25...v1.9.26 Full list of changes in getID3 1.9.26]
Developed in https://github.com/WordPress/wordpress-develop/pull/13420.
Follow-up to r47601, r48278, r52254, r54376, r56975, r61253, r61889, r62087.
Props Soean, mukesh27.
Fixes #66059.
Built from https://develop.svn.wordpress.org/trunk@63520
git-svn-id: http://core.svn.wordpress.org/trunk@62696 1a063a9b-81f0-0310-95a4-ce76da25c4cd
diff --git a/wp-includes/ID3/getid3.php b/wp-includes/ID3/getid3.php
index 121ff2faa2..4e7ccf3356 100644
--- a/wp-includes/ID3/getid3.php
+++ b/wp-includes/ID3/getid3.php
@@ -387,7 +387,7 @@ class getID3
*/
protected $startup_warning = '';
- const VERSION = '1.9.25-202603080933';
+ const VERSION = '1.9.26-202609042051';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
@@ -1328,6 +1328,26 @@ class getID3
'fail_ape' => 'ERROR',
),
+ // JXL - still image - JPEG XL (bare codestream)
+ 'jpegxl' => array(
+ 'pattern' => '^\\xFF\\x0A',
+ 'group' => 'graphic',
+ 'module' => 'jpegxl',
+ 'mime_type' => 'image/jxl',
+ 'fail_id3' => 'ERROR',
+ 'fail_ape' => 'ERROR',
+ ),
+
+ // JXL - still image - JPEG XL (ISO BMFF container)
+ 'jpegxlbmff' => array(
+ 'pattern' => '^\\x00\\x00\\x00\\x0CJXL\\x20\\x0D\\x0A\\x87\\x0A',
+ 'group' => 'graphic',
+ 'module' => 'jpegxl',
+ 'mime_type' => 'image/jxl',
+ 'fail_id3' => 'ERROR',
+ 'fail_ape' => 'ERROR',
+ ),
+
// PCD - still image - Kodak Photo CD
'pcd' => array(
'pattern' => '^.{2048}PCD_IPI\\x00',
@@ -1820,7 +1840,7 @@ class getID3
if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
- $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
+ $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp);
$VorbisCommentError = shell_exec($commandline);
} else {
diff --git a/wp-includes/ID3/module.audio-video.quicktime.php b/wp-includes/ID3/module.audio-video.quicktime.php
index 7598e0a616..161702ba18 100644
--- a/wp-includes/ID3/module.audio-video.quicktime.php
+++ b/wp-includes/ID3/module.audio-video.quicktime.php
@@ -153,7 +153,7 @@ class getid3_quicktime extends getid3_handler
foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
$ISO6709parsed = array('latitude'=>false, 'longitude'=>false, 'altitude'=>false);
if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
- @list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches;
+ list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = array_pad($matches, 11, '');
if (strlen($lat_deg) == 2) { // [+-]DD.D
$ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * (float) (ltrim($lat_deg, '0').$lat_deg_dec);
@@ -318,7 +318,7 @@ $this->error('HEIF files not currently supported');
// some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
$allnumericnames = true;
foreach ($atom_structure['subatoms'] as $subatomarray) {
- if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
+ if (!is_int($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
$allnumericnames = false;
break;
}
@@ -1681,10 +1681,10 @@ $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in
case "\xA9".'xyz': // GPS latitude+longitude+altitude
$atom_structure['data'] = $atom_data;
if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
- @list($all, $latitude, $longitude, $altitude) = $matches;
+ list($all, $latitude, $longitude, $altitude) = array_pad($matches, 4, '');
$info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
$info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
- if (!empty($altitude)) { // @phpstan-ignore-line
+ if (!empty($altitude)) {
$info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
}
} else {
@@ -2371,6 +2371,9 @@ $this->error('fragmented mp4 files not currently supported');
$num_bytes = 0;
$length = 0;
do {
+ if ($offset >= strlen($data)) { // https://github.com/JamesHeinrich/getID3/issues/477#issuecomment-4351311805
+ break;
+ }
$b = ord(substr($data, $offset++, 1));
$length = ($length << 7) | ($b & 0x7F);
} while (($b & 0x80) && ($num_bytes++ < 4));
diff --git a/wp-includes/ID3/module.audio-video.riff.php b/wp-includes/ID3/module.audio-video.riff.php
index 64e4ce52f3..2bf1ec515e 100644
--- a/wp-includes/ID3/module.audio-video.riff.php
+++ b/wp-includes/ID3/module.audio-video.riff.php
@@ -446,11 +446,11 @@ class getid3_riff extends getid3_handler
if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
$thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
- @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
+ list($numerator, $denominator) = array_pad(explode('/', $parsedXML['SPEED']['MASTER_SPEED']), 2, '');
$thisfile_riff_WAVE['iXML'][0]['master_speed'] = (int) $numerator / ($denominator ? $denominator : 1000);
}
if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
- @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
+ list($numerator, $denominator) = array_pad(explode('/', $parsedXML['SPEED']['TIMECODE_RATE']), 2, '');
$thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = (int) $numerator / ($denominator ? $denominator : 1000);
}
if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
@@ -476,7 +476,7 @@ class getid3_riff extends getid3_handler
$thisfile_riff['guano'] = array();
foreach (explode("\n", $thisfile_riff_WAVE_guan_0['data']) as $line) {
if ($line) {
- @list($key, $value) = explode(':', $line, 2);
+ list($key, $value) = array_pad(explode(':', $line, 2), 2, '');
if (substr($value, 0, 3) == '[{"') {
if ($decoded = @json_decode($value, true)) {
if (count($decoded) === 1) {
@@ -2048,6 +2048,7 @@ class getid3_riff extends getid3_handler
'ISTD'=>'productionstudio',
'ISTR'=>'starring',
'ITCH'=>'encoded_by',
+ 'ITRK'=>'track_number',
'IWEB'=>'url',
'IWRI'=>'writer',
'____'=>'comment',
diff --git a/wp-includes/ID3/module.audio.mp3.php b/wp-includes/ID3/module.audio.mp3.php
index bc414c3911..f035581c5a 100644
--- a/wp-includes/ID3/module.audio.mp3.php
+++ b/wp-includes/ID3/module.audio.mp3.php
@@ -748,6 +748,7 @@ class getid3_mp3 extends getid3_handler
$thisfile_mpeg_audio_lame['numeric_version'] = $matches[1];
}
if (strlen($thisfile_mpeg_audio_lame['numeric_version']) > 0) {
+ $thisfile_mpeg_audio_lame['integer_version'] = array(0, 0); // initialize to prevent undefined array keys later in case of malformed numeric_version -- https://github.com/JamesHeinrich/getID3/issues/477
foreach (explode('.', $thisfile_mpeg_audio_lame['numeric_version']) as $key => $number) {
$thisfile_mpeg_audio_lame['integer_version'][$key] = intval($number);
}
diff --git a/wp-includes/ID3/module.tag.apetag.php b/wp-includes/ID3/module.tag.apetag.php
index f47e354eb0..1c4c6231b1 100644
--- a/wp-includes/ID3/module.tag.apetag.php
+++ b/wp-includes/ID3/module.tag.apetag.php
@@ -53,21 +53,21 @@ class getid3_apetag extends getid3_handler
if ($this->overrideendoffset == 0) {
- $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
- $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
-
- //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
- if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
-
- // APE tag found before ID3v1
+ // don't seek before the start of the file on tiny files
+ $scanlength = min($id3v1tagsize + $apetagheadersize + $lyrics3tagsize, $info['filesize']);
+ $this->fseek(0 - $scanlength, SEEK_END);
+ $APEfooterID3v1 = $this->fread($scanlength);
+ $buffersize = strlen($APEfooterID3v1);
+
+ // APE footer immediately before a trailing ID3v1 tag: /APETAGEX.{24}TAG.{125}$/i
+ $APEbeforeID3v1 = ($buffersize >= ($id3v1tagsize + $apetagheadersize)) && (substr($APEfooterID3v1, $buffersize - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX');
+ // APE footer at the very end of the file: /APETAGEX.{24}$/i
+ $APEatEndOfFile = ($buffersize >= $apetagheadersize) && (substr($APEfooterID3v1, $buffersize - $apetagheadersize, 8) == 'APETAGEX');
+
+ if ($APEbeforeID3v1) {
$info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
-
- //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
- } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
-
- // APE tag found, no ID3v1
+ } elseif ($APEatEndOfFile) {
$info['ape']['tag_offset_end'] = $info['filesize'];
-
}
} else {
@@ -276,7 +276,7 @@ class getid3_apetag extends getid3_handler
$this->warning('APEtag "'.$item_key.'" should be flagged as Binary data, but was incorrectly flagged as UTF-8');
$thisfile_ape_items_current['data'] = implode("\x00", $thisfile_ape_items_current['data']);
}
- list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2);
+ list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = array_pad(explode("\x00", $thisfile_ape_items_current['data'], 2), 2, '');
$thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00");
$thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
diff --git a/wp-includes/ID3/module.tag.id3v2.php b/wp-includes/ID3/module.tag.id3v2.php
index 981d4d442d..51a6b54104 100644
--- a/wp-includes/ID3/module.tag.id3v2.php
+++ b/wp-includes/ID3/module.tag.id3v2.php
@@ -136,6 +136,7 @@ class getid3_id3v2 extends getid3_handler
if (!empty($thisfile_id3v2_flags['isfooter'])) {
$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
}
+ $sizeofframes = min($sizeofframes, $this->getid3->info['filesize'] - $this->ftell());
if ($sizeofframes > 0) {
$framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable
@@ -1998,7 +1999,7 @@ class getid3_id3v2 extends getid3_handler
// <Optional embedded sub-frames>
$frame_offset = 0;
- @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
+ list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$parsedFrame['time_begin'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
$frame_offset += 4;
@@ -2066,7 +2067,7 @@ class getid3_id3v2 extends getid3_handler
$parsedFrame['subframes'][] = $subframe;
break;
case 'WXXX':
- @list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2);
+ list($subframe['chapter_url_description'], $subframe['chapter_url']) = array_pad(explode("\x00", $encoding_converted_text, 2), 2, '');
$parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url'];
$parsedFrame['subframes'][] = $subframe;
break;
@@ -2122,7 +2123,7 @@ class getid3_id3v2 extends getid3_handler
// <Optional embedded sub-frames>
$frame_offset = 0;
- @list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
+ list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$frame_offset += strlen($parsedFrame['element_id']."\x00");
$ctoc_flags_raw = ord(substr($parsedFrame['data'], $frame_offset, 1));
$frame_offset += 1;
diff --git a/wp-includes/ID3/module.tag.lyrics3.php b/wp-includes/ID3/module.tag.lyrics3.php
index 01f942d005..dcd50e3cf8 100644
--- a/wp-includes/ID3/module.tag.lyrics3.php
+++ b/wp-includes/ID3/module.tag.lyrics3.php
@@ -32,11 +32,12 @@ class getid3_lyrics3 extends getid3_handler
return false;
}
- $this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
+ $scanlength = min(128 + 9 + 6, $info['filesize']); // end - ID3v1 - "LYRICSEND" - [Lyrics3size];
+ $this->fseek(0 - $scanlength, SEEK_END);
$lyrics3offset = null;
$lyrics3version = null;
$lyrics3size = null;
- $lyrics3_id3v1 = $this->fread(128 + 9 + 6);
+ $lyrics3_id3v1 = $this->fread($scanlength);
$lyrics3lsz = (int) substr($lyrics3_id3v1, 0, 6); // Lyrics3size
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
diff --git a/wp-includes/ID3/readme.txt b/wp-includes/ID3/readme.txt
index c1b3d47bff..426889a4bb 100644
--- a/wp-includes/ID3/readme.txt
+++ b/wp-includes/ID3/readme.txt
@@ -120,6 +120,7 @@ Reads & parses (to varying degrees):
* BMP
* GIF
* JPEG
+ * JPEG XL
* PNG
* TIFF
* SWF (Flash)
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 4d8289925f..2785f4bc23 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
-$wp_version = '7.2-alpha-63519';
+$wp_version = '7.2-alpha-63520';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.