From 8ea6aca807c20a15727b573c751f389665b6fe6c Mon Sep 17 00:00:00 2001 From: albertlast Date: Mon, 10 Aug 2026 06:52:24 +0200 Subject: [PATCH 1/2] Reserves the names on the reserved names list A fresh forum reserves nothing. Admin, Webmaster, Guest and root are all free to register, and so is every name an admin adds afterwards until they open the settings page and press Save. The default list comes from a language string that spells its separators as the two characters backslash and n. 2.1 fed that into an SQL literal, where both MySQL and PostgreSQL's E'' turned them into newlines. 3.0 defines its schema in PHP and inserts the value as a query parameter, so nothing unescapes it and the whole list is stored as one name that nobody would ever type. checkReservedName() explodes it on "\n" and gets a single element back. The admin page has carried its own str_replace for this since 2.1, so the list has always looked right in the textarea while none of it was in force. Seeds real newlines, and reads either form, so a forum already installed is fixed without having to touch its settings. Co-Authored-By: Claude Opus 5 Signed-off-by: albertlast --- Sources/Db/Schema/Table.php | 7 ++++++- Sources/Unicode/SpoofDetector.php | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sources/Db/Schema/Table.php b/Sources/Db/Schema/Table.php index c91e3e83225..5294a861968 100644 --- a/Sources/Db/Schema/Table.php +++ b/Sources/Db/Schema/Table.php @@ -563,7 +563,12 @@ public function populate(bool $replace = false): int } } - $replacements['{$default_reserved_names}'] = strtr($replacements['{$default_reserved_names}'], ['\\\\n' => '\\n']); + // The language string spells the separators as the two characters + // backslash and n, because 2.1 fed this list to an SQL literal and let + // the database turn them into newlines. Nothing does that here - the + // value goes in as a query parameter - so do it now, or the entire list + // is stored as one name that nobody would ever type. + $replacements['{$default_reserved_names}'] = strtr($replacements['{$default_reserved_names}'], ['\\\\n' => "\n", '\\n' => "\n"]); // Replace any placeholders in the initial data. foreach ($this->initial_data as &$row) { diff --git a/Sources/Unicode/SpoofDetector.php b/Sources/Unicode/SpoofDetector.php index 598462873a5..1f914a8b89b 100644 --- a/Sources/Unicode/SpoofDetector.php +++ b/Sources/Unicode/SpoofDetector.php @@ -242,7 +242,12 @@ public static function checkReservedName(string $name, bool $fatal = false): boo // This will hold all the names that are similar to $name. $homograph_names = []; - $reserved_names = explode("\n", Config::$modSettings['reserveNames']); + // A forum installed before the seeding was fixed has the separators as + // the two characters backslash and n rather than newlines, so take + // either. The admin page has always had its own str_replace for the + // same reason, which is why the list looked right there while nothing + // on it was ever reserved. + $reserved_names = preg_split('~\R|\\\\n~', Config::$modSettings['reserveNames']); // Check each name in the list... foreach ($reserved_names as $reserved) { From 46a3ebecab7390f4446ff2b1dd65456b1d106096 Mon Sep 17 00:00:00 2001 From: Jon Stovell Date: Sat, 29 Aug 2026 17:16:25 -0600 Subject: [PATCH 2/2] Apply batched suggestions from code review Co-authored-by: Jon Stovell --- Sources/Db/Schema/Table.php | 5 ----- Sources/Unicode/SpoofDetector.php | 5 ----- 2 files changed, 10 deletions(-) diff --git a/Sources/Db/Schema/Table.php b/Sources/Db/Schema/Table.php index 5294a861968..c133376053c 100644 --- a/Sources/Db/Schema/Table.php +++ b/Sources/Db/Schema/Table.php @@ -563,11 +563,6 @@ public function populate(bool $replace = false): int } } - // The language string spells the separators as the two characters - // backslash and n, because 2.1 fed this list to an SQL literal and let - // the database turn them into newlines. Nothing does that here - the - // value goes in as a query parameter - so do it now, or the entire list - // is stored as one name that nobody would ever type. $replacements['{$default_reserved_names}'] = strtr($replacements['{$default_reserved_names}'], ['\\\\n' => "\n", '\\n' => "\n"]); // Replace any placeholders in the initial data. diff --git a/Sources/Unicode/SpoofDetector.php b/Sources/Unicode/SpoofDetector.php index 1f914a8b89b..8a9d155023a 100644 --- a/Sources/Unicode/SpoofDetector.php +++ b/Sources/Unicode/SpoofDetector.php @@ -242,11 +242,6 @@ public static function checkReservedName(string $name, bool $fatal = false): boo // This will hold all the names that are similar to $name. $homograph_names = []; - // A forum installed before the seeding was fixed has the separators as - // the two characters backslash and n rather than newlines, so take - // either. The admin page has always had its own str_replace for the - // same reason, which is why the list looked right there while nothing - // on it was ever reserved. $reserved_names = preg_split('~\R|\\\\n~', Config::$modSettings['reserveNames']); // Check each name in the list...