Skip to content

fix: block case-insensitive duplicate usernames while preserving original casing#531

Merged
darknoon29 merged 2 commits intodevelopfrom
copilot/fix-duplicate-username-issue
Apr 13, 2026
Merged

fix: block case-insensitive duplicate usernames while preserving original casing#531
darknoon29 merged 2 commits intodevelopfrom
copilot/fix-duplicate-username-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

The ogspy_user table uses utf8mb4_bin collation, making UNIQUE KEY on name case-sensitive. This allowed Dardar and dardar to coexist as distinct accounts.

Approach

Per @darknoon29: preserve the user's chosen casing on write, but enforce case-insensitive uniqueness on duplicate checks.

Changes

  • model/User_Model.phpselect_is_user_name(): Apply mb_strtolower to input and use LOWER(\name`)` in the SQL predicate.
  • model/User_Model.phpselect_is_other_user_name(): Same treatment for rename duplicate checks.
  • add_new_user() and set_user_pseudo() are unchanged — stored casing is preserved.
// Before
$request = "SELECT * FROM ogspy_user WHERE `name` = '$username'";

// After
$username = mb_strtolower($username, 'UTF-8');
$request  = "SELECT * FROM ogspy_user WHERE LOWER(`name`) = '$username'";
  • tests/unit/UserModelTest.php: 4 new tests covering case-insensitive match detection and SQL LOWER() usage for both methods.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/PHPMailer/PHPMailer/zipball/ebf1655bd5b99b3f97e1a3ec0a69e5f4cd7ea088
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/components/jquery/zipball/8edc7785239bb8c2ad2b83302b856a1d61de60e7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/iamceege/tooltipster/zipball/0daaeffdc7da578f4048c4cf9ad2f37a474144f0
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/environment/zipball/7b8842c2d8e85d0c3a5831236bf5869af6ab2a11
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b015312f28dd75b75d3422ca37dff2cd1a565e8d
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b2429f58ae75cae980b5bb9873abe4de6aac8b58
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)
  • https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4
    • Triggering command: /usr/bin/php8.3 /usr/bin/php8.3 -n -c /tmp/407w1F /usr/bin/composer install --no-interaction (http block)

If you need me to access, download, or install something from one of these locations, you can either:

…l casing

Agent-Logs-Url: https://github.com/OGSteam/ogspy/sessions/3f68b93a-39e3-4fe3-9193-aebfb9b0835f

Co-authored-by: darknoon29 <13015521+darknoon29@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue with case-sensitive duplicate usernames fix: block case-insensitive duplicate usernames while preserving original casing Apr 13, 2026
Copilot AI requested a review from darknoon29 April 13, 2026 19:45
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@darknoon29 darknoon29 marked this pull request as ready for review April 13, 2026 19:49
Copilot AI review requested due to automatic review settings April 13, 2026 19:49
@darknoon29 darknoon29 merged commit 8a9b9cc into develop Apr 13, 2026
5 of 6 checks passed
@darknoon29 darknoon29 deleted the copilot/fix-duplicate-username-issue branch April 13, 2026 19:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a schema/behavior mismatch where ogspy_user.name is stored with a case-sensitive collation (utf8mb4_bin), allowing case-variant duplicate accounts (e.g., Dardar vs dardar). It updates the model-level “does this username already exist?” checks to treat usernames case-insensitively while keeping the originally-entered casing on write.

Changes:

  • Update User_Model::select_is_user_name() to normalize input with mb_strtolower() and compare via LOWER(name) in SQL.
  • Update User_Model::select_is_other_user_name() similarly for rename/“other user” duplicate checks.
  • Add PHPUnit coverage for case-insensitive matching behavior and for the LOWER() predicate usage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
model/User_Model.php Makes duplicate-name checks case-insensitive by lowercasing input and comparing against LOWER(name) in SQL.
tests/unit/UserModelTest.php Adds unit tests validating the case-insensitive duplicate detection behavior and query construction.

Comment thread model/User_Model.php
Comment on lines +47 to 51
$username = mb_strtolower($username, 'UTF-8');
$username = $this->db->sql_escape_string($username);

$request = "SELECT * FROM " . TABLE_USER . " WHERE `name` = '$username'";
$request = "SELECT * FROM " . TABLE_USER . " WHERE LOWER(`name`) = '$username'";
$result = $this->db->sql_query($request);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using LOWER(name) in the predicate provides a case-insensitive check, but it also bypasses the existing UNIQUE index on name and cannot fully prevent case-variant duplicates under concurrency (two simultaneous create/rename requests can still both pass the check and insert, because the DB constraint remains case-sensitive). If the goal is to truly block case-insensitive duplicates, consider enforcing this at the database level (e.g., case-insensitive collation for name, or a generated/stored lowercase column with a UNIQUE index) and keep the query sargable.

Copilot uses AI. Check for mistakes.
Comment thread model/User_Model.php
Comment on lines +64 to 69
$username = mb_strtolower($username, 'UTF-8');
$username = $this->db->sql_escape_string($username);
$user_id = (int) $user_id;

$request = "SELECT * FROM " . TABLE_USER . " WHERE `name` = '" . $username . "' AND `id` <> " . $user_id;
$request = "SELECT * FROM " . TABLE_USER . " WHERE LOWER(`name`) = '" . $username . "' AND `id` <> " . $user_id;
$result = $this->db->sql_query($request);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as select_is_user_name(): LOWER(name) makes the lookup non-sargable (won’t use the existing UNIQUE index on name) and still can’t guarantee case-insensitive uniqueness under concurrent updates/inserts while the DB constraint stays case-sensitive. A DB-level case-insensitive unique constraint (collation change or generated lowercase column + UNIQUE index) would make this robust and performant.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Les noms d'utilisateurs dupliqués (différence de casse) ne sont pas bloqués

3 participants