Opened 12 years ago
Last modified 8 hours ago
#30495 new defect (bug)
Unicode character U+000B is not removed by sanitize_file_name
| Reported by: | Craxic | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Formatting | Version: | 4.0.1 |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: |
Description
It seems that the following expression is true:
json_encode(sanitize_file_name(json_decode('"\u000B"'))) == "\u000b"
On Google App Engine, for example, a file name with a U+000B character cannot be saved.
Since the description of the function states:
Removes special characters that are illegal in filenames on certain
operating systems and special characters requiring special escaping
to manipulate at the command line.
... then I think this is a bug.
Thanks!
Attachments (1)
Change History (8)
This ticket was mentioned in Slack in #core by jorbin. View the logs.
11 years ago
@
11 years ago
Adds a step to remove all control characters in the 1-31 then mops up white space. Has unit test.
#4
@
10 years ago
Regarding "then mops up white space" take a look at this changeset:
https://core.trac.wordpress.org/changeset/29715
This ticket was mentioned in Slack in #core-privacy by desrosj. View the logs.
8 years ago
#6
@
10 days ago
Reproduction test report
I reproduced this on WordPress 7.2-alpha-64042 using PHP 8.4.26 on Windows x64. The isolated installation uses SQLite 3.53.4 with SQLite Database Integration 1.8.0, Twenty Twenty-Five, and no active regular plugins. The tested function is independent of database queries.
Steps
Run the following in a bootstrapped WordPress environment:
$input = "menu\x0B2026.pdf"; $output = sanitize_file_name( $input ); echo bin2hex( $output );
Expected: the U+000B control character is removed or replaced so that it is absent from the sanitized filename.
Actual: 6d656e750b323032362e706466. U+000B remains in menu\x0B2026.pdf.
Additional coverage
I ran the same filename test for all 32 ASCII control values U+0000 through U+001F. U+0000, U+0009, U+000A and U+000D are removed/replaced; the remaining 28 controls are retained, including U+000B and U+000C.
I then attempted to write the actual sanitized filenames to an isolated local directory using Python 3.12 on Windows. All 28 filenames containing a retained control failed with OSError: [Errno 22] Invalid argument. The four filenames whose controls were removed/replaced were saved successfully. This confirms a current Windows filesystem impact, in addition to the Google App Engine case in the original report.
Controls with ordinary spaces, accented Latin characters and Korean characters were also tested. menu 2026.pdf becomes menu-2026.pdf, and café-menu.pdf becomes cafe-menu.pdf. Korean characters remain intact while the ordinary space becomes a dash.
Result: the issue still reproduces on current nightly. The C0 control range needs additional coverage; replacing Unicode space-separator characters alone does not address these controls. I did not test the historical attached patch.
AI assistance: OpenAI Codex prepared and executed the scripts and helped draft this report. The results above come from the stated local WordPress installation and actual Windows file-write attempts.
This ticket was mentioned in PR #14198 on WordPress/wordpress-develop by @salvatorecorsi.
8 hours ago
#7
sanitize_file_name( "menu\x0B2026.pdf" ) retains the vertical tab, so the result can still be an invalid filename on Windows. The ticket's recent reproduction also identifies 27 other C0 control characters that the default sanitizer retains.
Add these 28 characters to the default removal list before sanitize_file_name_chars, preserving filter customization. Existing NUL removal and TAB/LF/CR replacement with a dash remain intact. Tests cover all 32 C0 values, leading/trailing/mixed controls, accents and Unicode, multiple extensions, the removal-list override and the final filename filter.
This builds on the C0-control approach proposed in sanchothefat's historical patch on the ticket, while preserving the current whitespace and public-filter behavior.
Reproduction
Run sanitize_file_name( "menu\x0B2026.pdf" ). Before this change its hex output contains 0b: 6d656e750b323032362e706466. The expected result is menu2026.pdf, with that control removed. "menu\t2026.pdf" should continue to become menu-2026.pdf.
Validation
Native PHP 8.3.28, PHPUnit 9.6.38 and private MariaDB:
- Before changing the implementation, the 42 new cases produced 38 failures, including all 28 previously retained C0 values. The existing NUL and TAB/LF/CR cases passed.
- The complete filename test class passed 62 tests and 76 assertions.
- All
Tests_Formatting_*classes completed 1,735 tests and 2,558 assertions, with no failures and five PHPUnit 9 deprecation warnings from unchangedwpStripAllTagsandwpTrimExcerpttests. - PHPCS, PHP compatibility, PHP syntax checks for both files and
git diff --checkpassed.
Commands executed through the local serialized PHPUnit runner correspond to:
php vendor/bin/phpunit -c phpunit.xml.dist tests/phpunit/tests/formatting/sanitizeFileName.php php vendor/bin/phpunit -c phpunit.xml.dist --filter '^Tests_Formatting_' php vendor/bin/phpcs --standard=phpcs.xml.dist --parallel=1 src/wp-includes/formatting.php tests/phpunit/tests/formatting/sanitizeFileName.php php vendor/bin/phpcs --standard=phpcompat.xml.dist --parallel=1 src/wp-includes/formatting.php tests/phpunit/tests/formatting/sanitizeFileName.php
The complete Core suite, other PHP versions, multisite, asset builds and E2E were not run locally.
Use of AI Tools
AI assistance: Yes
Tool(s): OpenAI Codex
Model(s): GPT-6
Used for: Ticket investigation, implementation, regression tests, local validation, this description and independent automated review. No human review is claimed; this PR is submitted for contributor and maintainer review.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
There might be a benefit in a check inside sanitize_file_name to remove everything that matches [:space:]. This is going to need both unit tests and a patch to move forward.