From cb8ae827d1837885527a3d9c3749ce9cfdb8ce65 Mon Sep 17 00:00:00 2001 From: Wes Copeland Date: Fri, 6 Sep 2024 19:13:34 -0400 Subject: [PATCH 1/3] fix: require one regular character to submit posts, comments, and messages --- .../Livewire/Forms/ForumTopicCommentForm.php | 12 +++-- app/Community/Requests/MessageRequest.php | 8 ++- .../Rules/ContainsRegularCharacter.php | 19 +++++++ lang/en/validation.php | 2 +- public/request/comment/create.php | 8 ++- public/request/forum-topic-comment/create.php | 8 ++- .../Rules/ContainsRegularCharacterTest.php | 54 +++++++++++++++++++ 7 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 app/Support/Rules/ContainsRegularCharacter.php create mode 100644 tests/Unit/Rules/ContainsRegularCharacterTest.php diff --git a/app/Community/Livewire/Forms/ForumTopicCommentForm.php b/app/Community/Livewire/Forms/ForumTopicCommentForm.php index e93076e55d..9716b434d1 100644 --- a/app/Community/Livewire/Forms/ForumTopicCommentForm.php +++ b/app/Community/Livewire/Forms/ForumTopicCommentForm.php @@ -5,10 +5,10 @@ namespace App\Community\Livewire\Forms; use App\Models\ForumTopicComment; +use App\Support\Rules\ContainsRegularCharacter; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Http\RedirectResponse; use Livewire\Attributes\Locked; -use Livewire\Attributes\Validate; use Livewire\Features\SupportRedirects\Redirector; use Livewire\Form; @@ -16,7 +16,6 @@ class ForumTopicCommentForm extends Form { use AuthorizesRequests; - #[Validate('required|max:60000')] public string $body = ''; #[Locked] @@ -31,7 +30,14 @@ public function setForumTopicComment(ForumTopicComment $forumTopicComment): void public function update(): RedirectResponse|Redirector { $this->authorize('update', [ForumTopicComment::class, $this->forumTopicComment]); - $this->validate(); + $this->validate([ + 'body' => [ + 'required', + 'string', + 'max:60000', + new ContainsRegularCharacter(), + ], + ]); editTopicComment($this->forumTopicComment->id, $this->body); diff --git a/app/Community/Requests/MessageRequest.php b/app/Community/Requests/MessageRequest.php index 4108243fbc..4375f7ae10 100644 --- a/app/Community/Requests/MessageRequest.php +++ b/app/Community/Requests/MessageRequest.php @@ -4,6 +4,7 @@ namespace App\Community\Requests; +use App\Support\Rules\ContainsRegularCharacter; use Illuminate\Foundation\Http\FormRequest; class MessageRequest extends FormRequest @@ -11,7 +12,12 @@ class MessageRequest extends FormRequest public function rules(): array { return [ - 'body' => 'required|string|max:60000', + 'body' => [ + 'required', + 'string', + 'max:60000', + new ContainsRegularCharacter(), + ], 'recipient' => 'required_without:thread_id|exists:UserAccounts,User', 'thread_id' => 'nullable|integer', 'title' => 'required_without:thread_id|string|max:255', diff --git a/app/Support/Rules/ContainsRegularCharacter.php b/app/Support/Rules/ContainsRegularCharacter.php new file mode 100644 index 0000000000..6c1b299799 --- /dev/null +++ b/app/Support/Rules/ContainsRegularCharacter.php @@ -0,0 +1,19 @@ +translate(); + } + } +} diff --git a/lang/en/validation.php b/lang/en/validation.php index 529a37b66a..c02fee2953 100755 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -133,7 +133,7 @@ /* * Strict validation rules */ - + 'contains_regular_character' => 'The :attribute must contain at least one regular character.', 'ctype_alnum' => 'The :attribute must only contain unaccented letters and numbers.', /* diff --git a/public/request/comment/create.php b/public/request/comment/create.php index 29b9073b38..43e43d0dea 100644 --- a/public/request/comment/create.php +++ b/public/request/comment/create.php @@ -6,6 +6,7 @@ use App\Models\Comment; use App\Models\Ticket; use App\Models\User; +use App\Support\Rules\ContainsRegularCharacter; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; @@ -14,7 +15,12 @@ } $input = Validator::validate(Arr::wrap(request()->post()), [ - 'body' => 'required|string|max:2000', + 'body' => [ + 'required', + 'string', + 'max:2000', + new ContainsRegularCharacter(), + ], 'commentable_id' => 'required|integer', 'commentable_type' => 'required|integer', ]); diff --git a/public/request/forum-topic-comment/create.php b/public/request/forum-topic-comment/create.php index f94131ae5a..9e6f89d357 100644 --- a/public/request/forum-topic-comment/create.php +++ b/public/request/forum-topic-comment/create.php @@ -2,6 +2,7 @@ use App\Models\ForumTopic; use App\Models\User; +use App\Support\Rules\ContainsRegularCharacter; use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; @@ -11,7 +12,12 @@ $input = Validator::validate(Arr::wrap(request()->post()), [ 'topic' => 'required|integer|exists:ForumTopic,ID', - 'body' => 'required|string|max:60000', + 'body' => [ + 'required', + 'string', + 'max:60000', + new ContainsRegularCharacter(), + ], ]); $userModel = User::firstWhere('User', $user); diff --git a/tests/Unit/Rules/ContainsRegularCharacterTest.php b/tests/Unit/Rules/ContainsRegularCharacterTest.php new file mode 100644 index 0000000000..31fef821aa --- /dev/null +++ b/tests/Unit/Rules/ContainsRegularCharacterTest.php @@ -0,0 +1,54 @@ + 'This is a valid comment with letters and symbols!']; + + $validator = Validator::make($data, [ + 'body' => ['required', 'string', new ContainsRegularCharacter()], + ]); + + $this->assertFalse($validator->fails()); + } + + public function testItFailsWhenInputOnlyContainsControlCharacters(): void + { + $data = ['body' => "\u{200B}\u{200E}\u{200F}"]; + + $validator = Validator::make($data, [ + 'body' => ['required', 'string', new ContainsRegularCharacter()], + ]); + + $this->assertTrue($validator->fails()); + } + + public function testItFailsWhenInputIsEmpty(): void + { + $data = ['body' => '']; + + $validator = Validator::make($data, [ + 'body' => ['required', 'string', new ContainsRegularCharacter()], + ]); + + $this->assertTrue($validator->fails()); + } + + public function testItPassesWhenInputIsOnlySymbols(): void + { + $data = ['body' => '***!!!']; + + $validator = Validator::make($data, [ + 'body' => ['required', 'string', new ContainsRegularCharacter()], + ]); + + $this->assertFalse($validator->fails()); + } +} From 0542e8107e61e18d9a3ffcb274fee85622db3656 Mon Sep 17 00:00:00 2001 From: Wes Copeland Date: Fri, 6 Sep 2024 19:15:35 -0400 Subject: [PATCH 2/3] chore: fix typo --- app/Support/Rules/ContainsRegularCharacter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Rules/ContainsRegularCharacter.php b/app/Support/Rules/ContainsRegularCharacter.php index 6c1b299799..d55e64f2fc 100644 --- a/app/Support/Rules/ContainsRegularCharacter.php +++ b/app/Support/Rules/ContainsRegularCharacter.php @@ -13,7 +13,7 @@ public function validate(string $attribute, mixed $value, Closure $fail): void $requireOneRegularCharacter = '/[\p{L}\p{N}\p{P}\p{S}]/u'; if (!preg_match($requireOneRegularCharacter, $value)) { - $fail('validation.contains_regular_characters')->translate(); + $fail('validation.contains_regular_character')->translate(); } } } From b9af93a43e291ac406fb0bccbadef3ec55b78500 Mon Sep 17 00:00:00 2001 From: Wes Copeland Date: Sat, 7 Sep 2024 18:17:05 -0400 Subject: [PATCH 3/3] fix: address pr feedback --- app/Support/Rules/ContainsRegularCharacter.php | 4 +++- tests/Unit/Rules/ContainsRegularCharacterTest.php | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/Support/Rules/ContainsRegularCharacter.php b/app/Support/Rules/ContainsRegularCharacter.php index d55e64f2fc..0bddbc03af 100644 --- a/app/Support/Rules/ContainsRegularCharacter.php +++ b/app/Support/Rules/ContainsRegularCharacter.php @@ -9,10 +9,12 @@ class ContainsRegularCharacter implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void { + $decodedValue = html_entity_decode($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + // Check for at least one letter, number, punctuation mark, or symbol. $requireOneRegularCharacter = '/[\p{L}\p{N}\p{P}\p{S}]/u'; - if (!preg_match($requireOneRegularCharacter, $value)) { + if (!preg_match($requireOneRegularCharacter, $decodedValue)) { $fail('validation.contains_regular_character')->translate(); } } diff --git a/tests/Unit/Rules/ContainsRegularCharacterTest.php b/tests/Unit/Rules/ContainsRegularCharacterTest.php index 31fef821aa..16dd69bb6c 100644 --- a/tests/Unit/Rules/ContainsRegularCharacterTest.php +++ b/tests/Unit/Rules/ContainsRegularCharacterTest.php @@ -51,4 +51,15 @@ public function testItPassesWhenInputIsOnlySymbols(): void $this->assertFalse($validator->fails()); } + + public function testItFailsWhenInputContainsOnlyHtmlEscapeCodes(): void + { + $data = ['body' => '​‎‏']; + + $validator = Validator::make($data, [ + 'body' => ['required', 'string', new ContainsRegularCharacter()], + ]); + + $this->assertTrue($validator->fails()); + } }