Skip to content

wp core download` (PharData) truncates long file paths in WordPress ≥ 6.7 #347

Description

@OIITCONZ

Bug Report

wp core download (PharData) truncates long file paths in WordPress ≥ 6.7 tarballs — 25 core PHP files missing in 7.x

Reported: 2026-10-06
Affected: WordPress release tarballs (.tar.gz) 6.7 and later, when extracted with PHP's PharData — most commonly via WP-CLI wp core download
Severity (reporter's assessment): Core integrity / availability. Not a remote-exploitable vulnerability, but it leaves unexplained, malformed files inside wp-includes/ that security scanners flag, and it silently removes core PHP classes.


Summary

Since WordPress 6.7, the official release tarballs (wordpress-X.Y.Z.tar.gz) are written in POSIX/PAX tar format. Paths longer than 100 characters are stored in PAX extended headers, with only the first 100 characters of the path in the classic ustar name field.

PHP's PharData does not apply the PAX path record when extracting. Every entry whose path is longer than 100 characters is written to disk under its first 100 characters (including the leading wordpress/).

WP-CLI's wp core download downloads the .tar.gz by default and extracts it with PharData (WP_CLI\Extractor::extract_tarball()). It only falls back to system tar if PharData throws, and here it doesn't throw. The result is a WordPress install where:

  • the real files are missing;
  • truncated, extension-less (or partially-extensioned: .p, .ph, .wof, .w) copies exist in their place;
  • where several long paths share the same first 100 characters, they collide and all but one are lost.

WordPress 7.0 added wp-includes/php-ai-client/, which has 25 paths over 100 characters. Since 7.0, therefore, the problem affects core PHP class files, not just bundled theme fonts.

Environment

Item Version
WordPress 7.1.2 (also reproduced on 6.7.2, 6.8.3, 7.0)
WP-CLI 2.12.0
PHP 8.4.24 (CLI)
Environment DDEV v1.25.4 web container

Steps to reproduce

A. With WP-CLI

wp core download --version=7.1.2 --path=wp-test
wp core verify-checksums --path=wp-test

Result:

Warning: File doesn't exist: wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModelMetadataDirectory.php
...
Warning: File should not exist: wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModelMetada
...
Error: WordPress installation doesn't verify against checksums.

25 × "File doesn't exist", 21 × "File should not exist".

B. With PharData directly (no WP-CLI)

curl -O https://wordpress.org/wordpress-7.1.2.tar.gz
php -r '(new PharData("wordpress-7.1.2.tar.gz"))->extractTo("out-phar");'
mkdir out-tar && tar -xzf wordpress-7.1.2.tar.gz -C out-tar

diff <(cd out-tar && find . -type f | sort) <(cd out-phar && find . -type f | sort)

GNU tar extracts 3,782 files correctly. PharData extracts 3,776, with 33 truncated names in place of 39 real files.

Observed results — WordPress 7.1.2

Count
Archive entries with path > 100 chars 39
…in wp-includes/php-ai-client/ 25
…in bundled themes (twentytwentythree/four/five) 14
Truncated files written by PharData 33 (6 lost to name collisions)
Core files missing (verify-checksums) 25
Unexpected core files (verify-checksums) 21

Examples (archive path → file written to disk):

wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModelMetadataDirectory.php
  → wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModelMetada

wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/DiscoveryFailedException.php
  → wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/DiscoveryFailedException.ph

wordpress/wp-content/themes/twentytwentyfive/assets/fonts/roboto-slab/RobotoSlab-VariableFont_wght.woff2
  → wordpress/wp-content/themes/twentytwentyfive/assets/fonts/roboto-slab/RobotoSlab-VariableFont_wght.w

Collision example: all three of these become the single file .../OpenAiCompatibleImplementation/AbstractOpenAiCompa:

AbstractOpenAiCompatibleTextGenerationModel.php
AbstractOpenAiCompatibleImageGenerationModel.php
AbstractOpenAiCompatibleModelMetadataDirectory.php

Runtime effect

With wp-includes/php-ai-client/autoload.php loaded (as wp-settings.php does), these classes cannot be resolved on an affected install, but resolve correctly on a .zip install:

MISSING WordPress\AiClient\Providers\Models\TextGeneration\Contracts\TextGenerationModelInterface
MISSING WordPress\AiClient\Providers\OpenAiCompatibleImplementation\AbstractOpenAiCompatibleTextGenerationModel
MISSING WordPress\AiClient\Providers\ApiBasedImplementation\AbstractApiBasedModelMetadataDirectory

Any code path in the AI client (or a plugin built on it) that needs one of the 25 missing classes or interfaces will fail. The bundled block themes are missing the affected web font files.

Which releases are affected

Tested by extracting each official tarball with PharData and comparing with GNU tar:

Release Tar format Paths > 100 chars PharData result
6.6.2 GNU (ustar \0) 12 Correct — identical to GNU tar
6.7.2 POSIX/PAX (ustar\0 00) 14 (themes only) Truncated
6.8.3 POSIX/PAX 14 (themes only) Truncated
7.0 POSIX/PAX 39 (25 core + 14 themes) Truncated
7.1.2 POSIX/PAX 39 (25 core + 14 themes) Truncated

The tarball format changed from GNU to POSIX/PAX at 6.7. Since then, bundled theme fonts have been silently truncated on PharData-extracted installs. From 7.0, core PHP files are affected too.

Why updating from the Dashboard does not clean this up

The .zip packages are not affected: the full, no-content and en_NZ locale zips for 7.1.2 were all checked. A Dashboard update or re-install therefore restores the correct files. However, the core updater does not remove files it doesn't know about, so the truncated copies remain in wp-includes/ and wp-content/themes/.

We observed this on a production site that was originally set up from a wp core download install and later updated via the Dashboard. All correct files were present, plus the truncated copies, which a security scanner flagged as unknown files in core directories.

A fresh install made with our hosting panel's WordPress installer, and the same site after a Dashboard update, were both byte-identical to the official zip, so the panel installer and the updater are not the source.

Security relevance

We do not believe this is directly exploitable:

  • The truncated files hold genuine (public) WordPress code.
  • None of the truncated names in 7.1.2 end in an executable PHP extension.

It is, however, a security-hygiene problem:

  1. Unexplained files in core directories. File-integrity checks (wp core verify-checksums, security plugins, host malware scanners) report unknown files in wp-includes/. To an administrator these look exactly like injected malware. That creates false alarms, and with repeated false alarms admins learn to ignore real alerts.
  2. Integrity verification permanently fails until the files are removed by hand, so checksum verification can't be used to catch a real compromise.
  3. Missing core code. Core classes are silently absent, with no error at install time.
  4. Future risk. If a future release ever has a long path whose first 100 characters end in an executable extension (for example .php, .phtml, .phar), extraction would write an executable file with unexpected content and name.

Suggested fixes

These are suggestions for the relevant maintainers to evaluate.

  1. WordPress release packaging: produce the .tar.gz in GNU tar format (as releases up to 6.6.x appear to have been), or otherwise keep packaged paths within limits PHP's PharData handles. A build-time check could flag any path over 100 characters.
  2. WP-CLI (WP_CLI\Extractor): don't rely on PharData for archives with PAX headers. Options:
    • prefer the .zip package for core downloads (it already does this for --skip-content and nightly builds);
    • prefer system tar when available;
    • after extraction, verify against the core checksums and fail loudly if they don't match.
  3. PHP (PharData): honour the PAX path extended header when extracting.

Workarounds

Safe install methods (verified):

# WP-CLI, zip-based (verifies cleanly against checksums):
wp core download --skip-content

# Or extract the tarball with system tar instead of PHP:
curl -O https://wordpress.org/latest.tar.gz && tar -xzf latest.tar.gz

# Or use the .zip:
curl -O https://wordpress.org/latest.zip && unzip latest.zip

Detect an affected site (from the WordPress root):

wp core verify-checksums
# Theme files are not covered by core checksums. List files whose path is
# exactly 90 chars (= 100 inside the archive, which prefixes "wordpress/")
# and compare each against the official .zip; some are legitimately that long:
find . -type f -printf '%P\n' | awk 'length($0)==90'

Repair:

  1. Delete the files reported as "File should not exist" by wp core verify-checksums.
  2. Restore core from the .zip, for example with a Dashboard re-install or wp core download --skip-content --force.
  3. Restore or delete truncated files in the bundled twenty* themes from the official .zip.
  4. Re-run wp core verify-checksums and confirm it returns Success.

Activity

  1. OIITCONZ commented on Oct 6, 2026

    @OIITCONZ
    Author

    Alter ambiguous line.
    "Restore or delete truncated files in the bundled twenty* themes from the official .zip" can incorrectly be read as "the zip
    has truncated files".
    It should say: "In the bundled twenty* themes on the site, delete the truncated files and restore the missing originals from the
    official .zip."

  2. swissspidy commented on Oct 6, 2026

    @swissspidy
    Member

    Thanks for your report. We already fixed this in #333 and #334 and others. While a new release of WP-CLI is being worked on, you can update to the latest nightly with wp cli update --nightly to fix this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions