Skip to content

Commit 5b0aa9c

Browse files
authored
[2.x] fix(nicknames): cast min/max settings to int before passing to Schema\Str (#4599)
* test(nicknames): reproduce TypeError when min/max settings are empty strings UserResourceFields passes the raw settings value to Schema\Str::minLength(int) and maxLength(int). Numeric strings ('3', '150') coerce silently under PHP's default weak typing, but an empty string — the result of an admin clearing the min or max field in the nicknames config and saving — triggers: Str::maxLength(): Argument #1 ($length) must be of type int, string given From then on every forum request 500s because UserResourceFields is invoked during JSON:API resource resolution. Fresh installs are unaffected because the Extend\Settings() int defaults bypass the DB path. Test reproduces the exact production stack trace. Fix follows. * fix(nicknames): handle non-int min/max length settings Cast the persisted setting to int before handing it to Schema\Str's minLength/maxLength, and only apply the constraint when the admin has configured a positive value. An empty field saved from the admin panel was otherwise stored as '' — triggering a TypeError in PHP's default weak coercion — and, after the naive cast, coerced to 0, which made maxLength(0) reject every nickname. Reveals a gap in Schema\Str: the int-only signature offers no way to "disable" a length constraint via the condition argument without a guard at the call site. Keeping the guard local for this RC fix.
1 parent a0803ab commit 5b0aa9c

2 files changed

Lines changed: 50 additions & 2 deletions

File tree

extensions/nicknames/src/Api/UserResourceFields.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ public function __invoke(): array
3232
$regex = "/$regex/";
3333
}
3434

35+
// Settings are always returned as strings from the DB. Coerce and only
36+
// apply the length constraint when the admin has configured a non-zero
37+
// value; an empty field saved from the admin panel would otherwise be
38+
// stored as '' and cast to 0, making maxLength(0) reject every value.
39+
$min = (int) $this->settings->get('flarum-nicknames.min');
40+
$max = (int) $this->settings->get('flarum-nicknames.max');
41+
3542
return [
3643
Schema\Str::make('nickname')
3744
->visible(false)
@@ -43,8 +50,8 @@ public function __invoke(): array
4350
// may render as hyperlinks in notification emails.
4451
->rule('not_regex:/[\[\]()<>]/')
4552
->regex($regex ?? '', ! empty($regex))
46-
->minLength($this->settings->get('flarum-nicknames.min'))
47-
->maxLength($this->settings->get('flarum-nicknames.max'))
53+
->minLength($min, $min > 0)
54+
->maxLength($max, $max > 0)
4855
->unique('users', 'nickname', true, (bool) $this->settings->get('flarum-nicknames.unique'))
4956
->unique('users', 'username', true, (bool) $this->settings->get('flarum-nicknames.unique'))
5057
->validationMessages([

extensions/nicknames/tests/integration/api/EditUserTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,45 @@ public static function nicknamesWithInjectionChars(): array
178178
'html attribute inject' => ['"><img src=x>'],
179179
];
180180
}
181+
182+
// Regression test for the TypeError raised by UserResourceFields when an
183+
// admin saves the nicknames config form with an empty min or max field —
184+
// the empty string is persisted to the settings table and then passed
185+
// straight to Schema\Str::minLength(int)/maxLength(int), which rejects
186+
// non-numeric strings under PHP's default type coercion rules.
187+
//
188+
// TypeError: Str::maxLength(): Argument #1 ($length) must be of type int, string given
189+
//
190+
// Numeric strings like '3' coerce silently, so the bug only surfaces when
191+
// a field is cleared. The int defaults from the Settings extender mask it
192+
// until the admin saves once.
193+
#[Test]
194+
public function request_succeeds_when_min_or_max_setting_is_empty_string(): void
195+
{
196+
$this->setting('flarum-nicknames.min', '');
197+
$this->setting('flarum-nicknames.max', '');
198+
199+
$this->prepareDatabase([
200+
'group_permission' => [
201+
['permission' => 'user.editOwnNickname', 'group_id' => Group::MEMBER_ID],
202+
],
203+
]);
204+
205+
$response = $this->send(
206+
$this->request('PATCH', '/api/users/2', [
207+
'authenticatedAs' => 2,
208+
'json' => [
209+
'data' => [
210+
'type' => 'users',
211+
'attributes' => [
212+
'nickname' => 'jane.smith',
213+
],
214+
],
215+
],
216+
])
217+
);
218+
219+
$this->assertEquals(200, $response->getStatusCode(), $response->getBody()->getContents());
220+
$this->assertEquals('jane.smith', User::find(2)->nickname);
221+
}
181222
}

0 commit comments

Comments
 (0)