Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions extensions/nicknames/src/Api/UserResourceFields.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ public function __invoke(): array
$regex = "/$regex/";
}

// Settings are always returned as strings from the DB. Coerce and only
// apply the length constraint when the admin has configured a non-zero
// value; an empty field saved from the admin panel would otherwise be
// stored as '' and cast to 0, making maxLength(0) reject every value.
$min = (int) $this->settings->get('flarum-nicknames.min');
$max = (int) $this->settings->get('flarum-nicknames.max');

return [
Schema\Str::make('nickname')
->visible(false)
Expand All @@ -43,8 +50,8 @@ public function __invoke(): array
// may render as hyperlinks in notification emails.
->rule('not_regex:/[\[\]()<>]/')
->regex($regex ?? '', ! empty($regex))
->minLength($this->settings->get('flarum-nicknames.min'))
->maxLength($this->settings->get('flarum-nicknames.max'))
->minLength($min, $min > 0)
->maxLength($max, $max > 0)
->unique('users', 'nickname', true, (bool) $this->settings->get('flarum-nicknames.unique'))
->unique('users', 'username', true, (bool) $this->settings->get('flarum-nicknames.unique'))
->validationMessages([
Expand Down
41 changes: 41 additions & 0 deletions extensions/nicknames/tests/integration/api/EditUserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,45 @@ public static function nicknamesWithInjectionChars(): array
'html attribute inject' => ['"><img src=x>'],
];
}

// Regression test for the TypeError raised by UserResourceFields when an
// admin saves the nicknames config form with an empty min or max field —
// the empty string is persisted to the settings table and then passed
// straight to Schema\Str::minLength(int)/maxLength(int), which rejects
// non-numeric strings under PHP's default type coercion rules.
//
// TypeError: Str::maxLength(): Argument #1 ($length) must be of type int, string given
//
// Numeric strings like '3' coerce silently, so the bug only surfaces when
// a field is cleared. The int defaults from the Settings extender mask it
// until the admin saves once.
#[Test]
public function request_succeeds_when_min_or_max_setting_is_empty_string(): void
{
$this->setting('flarum-nicknames.min', '');
$this->setting('flarum-nicknames.max', '');

$this->prepareDatabase([
'group_permission' => [
['permission' => 'user.editOwnNickname', 'group_id' => Group::MEMBER_ID],
],
]);

$response = $this->send(
$this->request('PATCH', '/api/users/2', [
'authenticatedAs' => 2,
'json' => [
'data' => [
'type' => 'users',
'attributes' => [
'nickname' => 'jane.smith',
],
],
],
])
);

$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
$this->assertEquals('jane.smith', User::find(2)->nickname);
}
}
Loading