From 21bc51f26d677a07bf8672c353482afb9d4945ca Mon Sep 17 00:00:00 2001 From: theluckystrike <51033404+theluckystrike@users.noreply.github.com> Date: Tue, 6 Oct 2026 09:57:37 +0000 Subject: [PATCH] Match profanity words case-insensitively and across punctuation ProfanityCheck split content on single spaces and compared each piece with a case-sensitive in_array, so "Fuck", "fuck!", newline-separated words and the default multi-word entry "screw you" were never caught. Match each configured entry as a whole word or phrase, ignoring case and allowing any whitespace between the words of a phrase. Words that only contain an entry (e.g. "Dickens") are still allowed. --- app/Rules/ProfanityCheck.php | 16 ++++++++--- tests/Unit/Rules/ProfanityCheckTest.php | 36 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/Unit/Rules/ProfanityCheckTest.php diff --git a/app/Rules/ProfanityCheck.php b/app/Rules/ProfanityCheck.php index bec016fd..5bcc6d96 100644 --- a/app/Rules/ProfanityCheck.php +++ b/app/Rules/ProfanityCheck.php @@ -9,10 +9,20 @@ class ProfanityCheck implements Rule { public function passes($attribute, $value) { - $words = explode(' ', $value); + if (! is_string($value)) { + return true; + } + + foreach (app(GeneralSettings::class)->profanity_words as $profanityWord) { + $terms = preg_split('/\s+/u', trim((string) $profanityWord), -1, PREG_SPLIT_NO_EMPTY); + + if (empty($terms)) { + continue; + } + + $phrase = implode('\s+', array_map(fn (string $term) => preg_quote($term, '/'), $terms)); - foreach ($words as $word) { - if (in_array($word, app(GeneralSettings::class)->profanity_words)) { + if (preg_match('/(? ['fuck', 'asshole', 'dick', 'screw you'], + ]); +}); + +test('content containing a profanity word fails', function (string $content) { + expect((new ProfanityCheck())->passes('content', $content))->toBeFalse(); +})->with([ + 'exact word' => 'fuck', + 'different casing' => 'Fuck this feature', + 'trailing punctuation' => 'What the fuck!', + 'surrounded by punctuation' => '(asshole)', + 'separated by a newline' => "Nice idea\ndick", + 'multi word entry' => 'Screw you', + 'multi word entry across whitespace' => "screw\n you, really", +]); + +test('content without a profanity word passes', function (string $content) { + expect((new ProfanityCheck())->passes('content', $content))->toBeTrue(); +})->with([ + 'clean sentence' => 'Please add dark mode', + 'word containing a profanity word' => 'I love reading Dickens', + 'multi word entry split by other words' => 'Screw the lid on, thank you', +]); + +test('empty profanity entries are ignored', function () { + GeneralSettings::fake(['profanity_words' => ['', ' ']]); + + expect((new ProfanityCheck())->passes('content', 'Please add dark mode'))->toBeTrue(); +});