Skip to content

Commit b083f2c

Browse files
authored
fix: PwnedValidator cannot reach the HIBP API (#1372)
1 parent 09fdb19 commit b083f2c

6 files changed

Lines changed: 44 additions & 27 deletions

File tree

‎rector.php‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector;
1717
use Rector\CodeQuality\Rector\Empty_\SimplifyEmptyCheckOnEmptyArrayRector;
1818
use Rector\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector;
19-
use Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector;
2019
use Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector;
21-
use Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector;
2220
use Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector;
2321
use Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector;
2422
use Rector\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector;
@@ -31,15 +29,13 @@
3129
use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector;
3230
use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector;
3331
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
34-
use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector;
3532
use Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector;
3633
use Rector\Config\RectorConfig;
3734
use Rector\DeadCode\Rector\Cast\RecastingRemovalRector;
3835
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
3936
use Rector\DeadCode\Rector\If_\UnwrapFutureCompatibleIfPhpVersionRector;
4037
use Rector\DeadCode\Rector\MethodCall\RemoveNullArgOnNullDefaultParamRector;
4138
use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector;
42-
use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
4339
use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
4440
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;
4541
use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector;
@@ -52,7 +48,6 @@
5248
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
5349
use Rector\Set\ValueObject\LevelSetList;
5450
use Rector\Set\ValueObject\SetList;
55-
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
5651
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
5752
use Rector\ValueObject\PhpVersion;
5853

@@ -61,7 +56,7 @@
6156
SetList::DEAD_CODE,
6257
LevelSetList::UP_TO_PHP_81,
6358
PHPUnitSetList::PHPUNIT_CODE_QUALITY,
64-
PHPUnitSetList::PHPUNIT_100,
59+
PHPUnitSetList::COMPOSER_BASED,
6560
]);
6661

6762
$rectorConfig->parallel();
@@ -148,8 +143,6 @@
148143

149144
$rectorConfig->rule(SimplifyUselessVariableRector::class);
150145
$rectorConfig->rule(RemoveAlwaysElseRector::class);
151-
$rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class);
152-
$rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class);
153146
$rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class);
154147
$rectorConfig->rule(SimplifyStrposLowerRector::class);
155148
$rectorConfig->rule(CombineIfRector::class);
@@ -158,17 +151,14 @@
158151
$rectorConfig->rule(PreparedValueToEarlyReturnRector::class);
159152
$rectorConfig->rule(ShortenElseIfRector::class);
160153
$rectorConfig->rule(SimplifyIfElseToTernaryRector::class);
161-
$rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class);
162154
$rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class);
163155
$rectorConfig->rule(UnnecessaryTernaryExpressionRector::class);
164-
$rectorConfig->rule(SimplifyRegexPatternRector::class);
165156
$rectorConfig->rule(FuncGetArgsToVariadicParamRector::class);
166157
$rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class);
167158
$rectorConfig->rule(SimplifyEmptyArrayCheckRector::class);
168159
$rectorConfig->rule(SimplifyEmptyCheckOnEmptyArrayRector::class);
169160
$rectorConfig->rule(TernaryEmptyArrayArrayDimFetchToCoalesceRector::class);
170161
$rectorConfig->rule(EmptyOnNullableObjectToInstanceOfRector::class);
171-
$rectorConfig->rule(DisallowedEmptyRuleFixerRector::class);
172162
$rectorConfig->rule(StringClassNameToClassConstantRector::class);
173163
$rectorConfig->rule(PrivatizeFinalClassPropertyRector::class);
174164
$rectorConfig->rule(CompleteDynamicPropertiesRector::class);

‎src/Authentication/Actions/Email2FA.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function handle(IncomingRequest $request)
9494
$email->setSubject(lang('Auth.email2FASubject'));
9595
$email->setMessage($this->view(
9696
setting('Auth.views')['action_email_2fa_email'],
97-
['code' => $identity->secret, 'user' => $user, 'ipAddress' => $ipAddress, 'userAgent' => $userAgent, 'date' => $date],
97+
['code' => $identity->secret, 'user' => $user, 'ipAddress' => $ipAddress, 'userAgent' => $userAgent, 'date' => $date],
9898
['debug' => false],
9999
));
100100

‎src/Authentication/Actions/EmailActivator.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function show(): string
7070
$email->setSubject(lang('Auth.emailActivateSubject'));
7171
$email->setMessage($this->view(
7272
setting('Auth.views')['action_email_activate_email'],
73-
['code' => $code, 'user' => $user, 'ipAddress' => $ipAddress, 'userAgent' => $userAgent, 'date' => $date],
73+
['code' => $code, 'user' => $user, 'ipAddress' => $ipAddress, 'userAgent' => $userAgent, 'date' => $date],
7474
['debug' => false],
7575
));
7676

‎src/Authentication/Passwords/NothingPersonalValidator.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function check(string $password, ?User $user = null): Result
3333
{
3434
$password = strtolower($password);
3535

36-
if ($valid = $this->isNotPersonal($password, $user) === true) {
36+
if ($valid = $this->isNotPersonal($password, $user)) {
3737
$valid = $this->isNotSimilar($password, $user);
3838
}
3939

‎src/Authentication/Passwords/PwnedValidator.php‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
*/
3333
class PwnedValidator extends BaseValidator implements ValidatorInterface
3434
{
35+
private const API_URL = 'https://api.pwnedpasswords.com/range/';
36+
3537
/**
3638
* Checks the password against the online database and
3739
* returns false if a match is found. Returns true if no match is found.
@@ -47,12 +49,10 @@ public function check(string $password, ?User $user = null): Result
4749
$searchHash = substr($hashedPword, 5);
4850

4951
try {
50-
$client = Services::curlrequest([
51-
'base_uri' => 'https://api.pwnedpasswords.com/',
52-
]);
52+
$client = Services::curlrequest();
5353

5454
$response = $client->get(
55-
'range/' . $rangeHash,
55+
self::API_URL . $rangeHash,
5656
['headers' => ['Accept' => 'text/plain']],
5757
);
5858
} catch (HTTPException $e) {

‎tests/Unit/PwnedValidatorTest.php‎

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,20 @@ public function testCheckFalseOnPwnedPassword(): void
5555
$response = new Response(new App());
5656
$response->setBody($body);
5757

58+
$password = 'admin123';
59+
5860
$curlrequest = $this->createMock('CodeIgniter\HTTP\CURLRequest');
5961

60-
$curlrequest->method('get')->willReturn($response);
62+
$curlrequest->expects($this->once())
63+
->method('get')
64+
->with(
65+
'https://api.pwnedpasswords.com/range/' . $this->rangeHash($password),
66+
['headers' => ['Accept' => 'text/plain']],
67+
)
68+
->willReturn($response);
6169

6270
Services::injectMock('curlrequest', $curlrequest);
6371

64-
$password = 'admin123';
65-
6672
$result = $this->validator->check($password);
6773

6874
$this->assertFalse($result->isOK());
@@ -75,14 +81,20 @@ public function testCheckFalseOnPwnedLastInRange(): void
7581
$response = new Response(new App());
7682
$response->setBody($body);
7783

84+
$password = 'ziplock';
85+
7886
$curlrequest = $this->createMock('CodeIgniter\HTTP\CURLRequest');
7987

80-
$curlrequest->method('get')->willReturn($response);
88+
$curlrequest->expects($this->once())
89+
->method('get')
90+
->with(
91+
'https://api.pwnedpasswords.com/range/' . $this->rangeHash($password),
92+
['headers' => ['Accept' => 'text/plain']],
93+
)
94+
->willReturn($response);
8195

8296
Services::injectMock('curlrequest', $curlrequest);
8397

84-
$password = 'ziplock';
85-
8698
$result = $this->validator->check($password);
8799

88100
$this->assertFalse($result->isOK());
@@ -105,14 +117,20 @@ public function testCheckTrueOnNotFound(): void
105117
$response = new Response(new App());
106118
$response->setBody($body);
107119

120+
$password = '!!!gerard!!!abootylicious';
121+
108122
$curlrequest = $this->createMock('CodeIgniter\HTTP\CURLRequest');
109123

110-
$curlrequest->method('get')->willReturn($response);
124+
$curlrequest->expects($this->once())
125+
->method('get')
126+
->with(
127+
'https://api.pwnedpasswords.com/range/' . $this->rangeHash($password),
128+
['headers' => ['Accept' => 'text/plain']],
129+
)
130+
->willReturn($response);
111131

112132
Services::injectMock('curlrequest', $curlrequest);
113133

114-
$password = '!!!gerard!!!abootylicious';
115-
116134
$result = $this->validator->check($password);
117135

118136
$this->assertTrue($result->isOK());
@@ -132,4 +150,13 @@ public function testCheckCatchesAndRethrowsCurlExceptionAsAuthException(): void
132150
$this->expectException(AuthenticationException::class);
133151
$this->validator->check('opensesame');
134152
}
153+
154+
/**
155+
* The first 5 characters of the uppercased SHA-1 hash, as sent
156+
* to the HIBP range endpoint.
157+
*/
158+
private function rangeHash(string $password): string
159+
{
160+
return substr(strtoupper(sha1($password)), 0, 5);
161+
}
135162
}

0 commit comments

Comments
 (0)