Repository navigation
fix: Unicode support for NothingPersonalValidator
#1406
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,7 @@ | |
| */ | ||
| public function check(string $password, ?User $user = null): Result | ||
| { | ||
| $password = strtolower($password); | ||
| $password = mb_strtolower($password); | ||
|
Check warning on line 34 in src/Authentication/Passwords/NothingPersonalValidator.php
|
||
|
|
||
| if ($valid = $this->isNotPersonal($password, $user)) { | ||
| $valid = $this->isNotSimilar($password, $user); | ||
|
|
@@ -45,8 +45,6 @@ | |
| } | ||
|
|
||
| /** | ||
| * isNotPersonal() | ||
| * | ||
| * Looks for personal information in a password. The personal info used | ||
| * comes from CodeIgniter\Shield\Entities\User properties username and email. | ||
| * | ||
|
|
@@ -58,13 +56,12 @@ | |
| * | ||
| * public $personalFields = ['firstname', 'lastname']; | ||
| * | ||
| * isNotPersonal() returns true if no personal information can be found, or false | ||
| * if such info is found. | ||
| * @return bool `true` if no personal information can be found, or `false` if such info is found. | ||
| */ | ||
| protected function isNotPersonal(string $password, ?User $user): bool | ||
| { | ||
| $userName = strtolower($user->username ?? ''); | ||
| $email = strtolower($user->email); | ||
| $userName = mb_strtolower($user->username ?? ''); | ||
| $email = mb_strtolower($user->email ?? ''); | ||
| $valid = true; | ||
|
|
||
| // The most obvious transgressions | ||
|
|
@@ -95,10 +92,11 @@ | |
|
|
||
| foreach ($personalFields as $value) { | ||
| if (($user->{$value} ?? '') !== '') { | ||
| $needles[] = strtolower($user->{$value}); | ||
| $needles[] = mb_strtolower($user->{$value}); | ||
| } | ||
| } | ||
|
|
||
| $needles = array_unique($needles, SORT_LOCALE_STRING); | ||
|
Check warning on line 99 in src/Authentication/Passwords/NothingPersonalValidator.php
|
||
| $trivial = [ | ||
| 'a', | ||
| 'an', | ||
|
|
@@ -121,12 +119,12 @@ | |
| $haystacks = $this->strip_explode($password); | ||
|
|
||
| foreach ($haystacks as $haystack) { | ||
| if (in_array($haystack, $trivial, true) || mb_strlen($haystack, 'UTF-8') < 3) { | ||
| if (in_array($haystack, $trivial, true) || mb_strlen($haystack) < 3) { | ||
| continue; // ignore trivial words | ||
| } | ||
|
|
||
| foreach ($needles as $needle) { | ||
| if (in_array($needle, $trivial, true) || mb_strlen($needle, 'UTF-8') < 3) { | ||
| if (in_array($needle, $trivial, true) || mb_strlen($needle) < 3) { | ||
| continue; | ||
| } | ||
|
|
||
|
|
@@ -139,6 +137,7 @@ | |
| } | ||
| } | ||
| } | ||
|
|
||
| if ($valid) { | ||
| return true; | ||
| } | ||
|
|
@@ -150,17 +149,19 @@ | |
| } | ||
|
|
||
| /** | ||
| * notSimilar() uses $password and $userName to calculate a similarity value. | ||
| * Method uses password and username to calculate a similarity value. | ||
| * Similarity values equal to, or greater than CodeIgniter\Shield\Config::maxSimilarity | ||
| * are rejected for being too much alike and false is returned. | ||
| * Otherwise, true is returned, | ||
| * | ||
| * A $maxSimilarity value of 0 (zero) returns true without making a comparison. | ||
| * In other words, 0 (zero) turns off similarity testing. | ||
| * | ||
| * @return bool `true` if a similarity is found, or `false` if such info is found. | ||
| */ | ||
| protected function isNotSimilar(string $password, ?User $user): bool | ||
| { | ||
| if ($user->username === null) { | ||
| if (! $user instanceof User || $user->username === null) { | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -173,9 +174,10 @@ | |
| } | ||
|
|
||
| if ($maxSimilarity > 0) { | ||
| $userName = strtolower($user->username); | ||
| $userName = mb_strtolower($user->username); | ||
|
|
||
| similar_text($password, $userName, $similarity); | ||
|
|
||
| if ($similarity >= $maxSimilarity) { | ||
| $this->error = lang('Auth.errorPasswordTooSimilar'); | ||
| $this->suggestion = lang('Auth.suggestPasswordTooSimilar'); | ||
|
|
@@ -188,16 +190,14 @@ | |
| } | ||
|
|
||
| /** | ||
| * strip_explode($str) | ||
| * | ||
| * Replaces all non-word characters and underscores in $str with a space. | ||
| * Then it explodes that result using the space for a delimiter. | ||
| * | ||
| * @return array<int, string> | ||
| */ | ||
| protected function strip_explode(string $str): array | ||
|
Check warning on line 198 in src/Authentication/Passwords/NothingPersonalValidator.php
|
||
| { | ||
| $stripped = preg_replace('/[\W_]+/', ' ', $str); | ||
| $stripped = preg_replace('/[\W_]+/u', ' ', $str); | ||
| $parts = explode(' ', trim((string) $stripped)); | ||
|
|
||
| // If it's not already there put the untouched input at the top of the array | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MBString mutation suggests the tests did not cover multibyte passwords, which this change is anchoring on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven’t used Infection. Does this mean that my version of the tests works in both strtolower() variants?
Perhaps it will work if the exact strings are the same. But we understand that there is a difference:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, your test passes in both variants. infection must see your test fails when it changes things to say you covered the mutant