From eedce8fb32b8a03e5e6c0733b8f5d645e83fd60d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:09:04 +0000 Subject: [PATCH] Use Punycode filenames for non-ASCII TLD suffix data files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some systems and tools (e.g., certain archive extractors, Windows environments, or CI pipelines) do not properly handle non-ASCII characters in file paths. The public suffix data files for internationalized TLDs (such as ישראל, СРБ, 香港, and ไทย) were stored using their native Unicode names, which caused installation failures on those systems. This commit converts those filenames to their Punycode equivalents (e.g., XN--4DBRK0CE.php instead of ישראל.php) using `idn_to_ascii()`. Both the data generation command (`UpdateDomainSuffixesCommand`) and the runtime validator (`PublicDomainSuffix`) are updated to use the same Punycode-based file lookup, ensuring consistency. A polyfill dependency (`symfony/polyfill-intl-idn`) is added so that `idn_to_ascii()` is available even when the `intl` PHP extension is not installed. Assisted-by: Claude Code (Claude Opus 4.6) Co-authored-by: Henrique Moody --- composer.json | 1 + composer.lock | 392 +++++++++--------- .../{ישראל.php => XN--4DBRK0CE.php} | 0 .../public-suffix/{СРБ.php => XN--90A3AC.php} | 0 .../{香港.php => XN--J6W193G.php} | 0 .../public-suffix/{ไทย.php => XN--O3CW4H.php} | 0 .../Commands/UpdateDomainSuffixesCommand.php | 8 +- src/Validators/PublicDomainSuffix.php | 10 +- 8 files changed, 213 insertions(+), 198 deletions(-) rename data/domain/public-suffix/{ישראל.php => XN--4DBRK0CE.php} (100%) rename data/domain/public-suffix/{СРБ.php => XN--90A3AC.php} (100%) rename data/domain/public-suffix/{香港.php => XN--J6W193G.php} (100%) rename data/domain/public-suffix/{ไทย.php => XN--O3CW4H.php} (100%) diff --git a/composer.json b/composer.json index ff2ae155..0514cc4d 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "psr/container": "^2.0", "respect/string-formatter": "^1.6", "respect/stringifier": "^3.0", + "symfony/polyfill-intl-idn": "^1.33", "symfony/polyfill-mbstring": "^1.33" }, "require-dev": { diff --git a/composer.lock b/composer.lock index acbb3bdf..3a9bafa7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "049a227c54ff9b04b7f415bbc9cf6924", + "content-hash": "fa9f29c82a049d9f491ccfc5af541b2d", "packages": [ { "name": "laravel/serializable-closure", @@ -250,16 +250,16 @@ }, { "name": "respect/string-formatter", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/Respect/StringFormatter.git", - "reference": "3696f0f79dfb62572ea85fc678ca5c9bcde5c8bd" + "reference": "4c3bfd069c0704f38715b6208d8b22fa4fcc8376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Respect/StringFormatter/zipball/3696f0f79dfb62572ea85fc678ca5c9bcde5c8bd", - "reference": "3696f0f79dfb62572ea85fc678ca5c9bcde5c8bd", + "url": "https://api.github.com/repos/Respect/StringFormatter/zipball/4c3bfd069c0704f38715b6208d8b22fa4fcc8376", + "reference": "4c3bfd069c0704f38715b6208d8b22fa4fcc8376", "shasum": "" }, "require": { @@ -302,9 +302,9 @@ "description": "A powerful and flexible way of formatting and transforming strings", "support": { "issues": "https://github.com/Respect/StringFormatter/issues", - "source": "https://github.com/Respect/StringFormatter/tree/1.6.0" + "source": "https://github.com/Respect/StringFormatter/tree/1.6.1" }, - "time": "2026-02-09T12:35:58+00:00" + "time": "2026-02-09T12:41:42+00:00" }, { "name": "respect/stringifier", @@ -363,6 +363,178 @@ }, "time": "2026-01-19T10:24:52+00:00" }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.33.0", @@ -627,16 +799,16 @@ }, { "name": "brick/math", - "version": "0.14.6", + "version": "0.14.7", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3" + "reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/32498d5e1897e7642c0b961ace2df6d7dc9a3bc3", - "reference": "32498d5e1897e7642c0b961ace2df6d7dc9a3bc3", + "url": "https://api.github.com/repos/brick/math/zipball/07ff363b16ef8aca9692bba3be9e73fe63f34e50", + "reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50", "shasum": "" }, "require": { @@ -675,7 +847,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.6" + "source": "https://github.com/brick/math/tree/0.14.7" }, "funding": [ { @@ -683,7 +855,7 @@ "type": "github" } ], - "time": "2026-02-05T07:59:58+00:00" + "time": "2026-02-07T10:57:35+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", @@ -916,29 +1088,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.5", + "version": "1.1.6", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" + "phpunit/phpunit": "<=7.5 || >=14" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -958,9 +1130,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" }, - "time": "2025-04-07T20:06:18+00:00" + "time": "2026-02-07T07:09:04+00:00" }, { "name": "doctrine/lexer", @@ -5623,178 +5795,6 @@ ], "time": "2025-06-27T09:58:17+00:00" }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-10T14:38:51+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, { "name": "symfony/process", "version": "v8.0.5", @@ -6440,5 +6440,5 @@ "php": ">=8.5" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/data/domain/public-suffix/ישראל.php b/data/domain/public-suffix/XN--4DBRK0CE.php similarity index 100% rename from data/domain/public-suffix/ישראל.php rename to data/domain/public-suffix/XN--4DBRK0CE.php diff --git a/data/domain/public-suffix/СРБ.php b/data/domain/public-suffix/XN--90A3AC.php similarity index 100% rename from data/domain/public-suffix/СРБ.php rename to data/domain/public-suffix/XN--90A3AC.php diff --git a/data/domain/public-suffix/香港.php b/data/domain/public-suffix/XN--J6W193G.php similarity index 100% rename from data/domain/public-suffix/香港.php rename to data/domain/public-suffix/XN--J6W193G.php diff --git a/data/domain/public-suffix/ไทย.php b/data/domain/public-suffix/XN--O3CW4H.php similarity index 100% rename from data/domain/public-suffix/ไทย.php rename to data/domain/public-suffix/XN--O3CW4H.php diff --git a/src-dev/Commands/UpdateDomainSuffixesCommand.php b/src-dev/Commands/UpdateDomainSuffixesCommand.php index 9e33949f..475b19d1 100644 --- a/src-dev/Commands/UpdateDomainSuffixesCommand.php +++ b/src-dev/Commands/UpdateDomainSuffixesCommand.php @@ -25,6 +25,7 @@ use function dirname; use function explode; use function file_get_contents; use function glob; +use function idn_to_ascii; use function is_dir; use function mb_strtoupper; use function mkdir; @@ -37,6 +38,9 @@ use function str_starts_with; use function trim; use function unlink; +use const IDNA_DEFAULT; +use const INTL_IDNA_VARIANT_UTS46; + #[AsCommand( name: 'update:domain-suffixes', description: 'Update list of public domain suffixes', @@ -103,11 +107,13 @@ final class UpdateDomainSuffixesCommand extends Command continue; } + $punycodedTld = idn_to_ascii($tld, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46) ?: $tld; + $this->dataSaver->save( $suffixList, '2007–22 Mozilla Foundation', 'MPL-2.0-no-copyleft-exception', - sprintf('domain/public-suffix/%s.php', $tld), + sprintf('domain/public-suffix/%s.php', $punycodedTld), ); $progressBar->advance(); diff --git a/src/Validators/PublicDomainSuffix.php b/src/Validators/PublicDomainSuffix.php index 5e0cbf93..ac483476 100644 --- a/src/Validators/PublicDomainSuffix.php +++ b/src/Validators/PublicDomainSuffix.php @@ -19,11 +19,15 @@ use Respect\Validation\Validators\Core\Simple; use function array_pop; use function explode; +use function idn_to_ascii; use function in_array; use function is_scalar; use function mb_strtoupper; use function strtoupper; +use const IDNA_DEFAULT; +use const INTL_IDNA_VARIANT_UTS46; + #[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)] #[Template( '{{subject}} must be a public domain suffix', @@ -41,8 +45,12 @@ final class PublicDomainSuffix extends Simple $parts = explode('.', (string) $input); $tld = array_pop($parts); + if ($tld === '') { + return true; + } - $dataSource = DataLoader::load('domain/public-suffix/' . mb_strtoupper($tld) . '.php'); + $punycodedTld = idn_to_ascii($tld, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46) ?: $tld; + $dataSource = DataLoader::load('domain/public-suffix/' . mb_strtoupper($punycodedTld) . '.php'); if ($this->isUndefined($input) && $dataSource === []) { return true; }