Skip to content

Commit

Permalink
Merge pull request #70 from krlxfm/profanity-rule
Browse files Browse the repository at this point in the history
Add a profanity rule
  • Loading branch information
tatebosler authored Jul 28, 2018
2 parents d55b4e8 + 9475060 commit 73d36a9
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 1 deletion.
3 changes: 2 additions & 1 deletion app/Http/Requests/ShowUpdateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace KRLX\Http\Requests;

use KRLX\Track;
use KRLX\Rules\Profanity;
use Illuminate\Foundation\Http\FormRequest;

class ShowUpdateRequest extends FormRequest
Expand Down Expand Up @@ -49,7 +50,7 @@ protected function baseRules()
{
$baseRules = [
'submitted' => ['boolean'],
'title' => ['string', 'min:3', 'max:200'],
'title' => ['string', 'min:3', 'max:200', new Profanity],
'content' => ['array'],
'scheduling' => ['array'],
'conflicts' => ['array', 'min:0'],
Expand Down
117 changes: 117 additions & 0 deletions app/Rules/Profanity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace KRLX\Rules;

use Illuminate\Contracts\Validation\Rule;

class Profanity implements Rule
{
/**
* The word that has been found.
*
* @var string|null
*/
public $word;

/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}

/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$target = preg_replace('/[@#\$%\^]/', '*', strtolower($value));
$words = explode(' ', $target);
foreach (array_merge(config('defaults.banned_words.full'), config('defaults.banned_words.partial')) as $bad_word) {
if (in_array($bad_word, $words) or in_array(str_plural($bad_word), $words)) {
$this->word = $bad_word;

return false;
}
}

return $this->partialWordsPass($target);
}

/**
* Check the partial words - these are words which could appear as they
* normally are, or in any number of creative derivatives.
*
* @param mixed $value
* @return bool
*/
protected function partialWordsPass($value)
{
$bad_words = $this->assembleDerivatives();
foreach ($bad_words as $word => $derivatives) {
if (! $this->singleWordDerivativesPass($word, $derivatives, $value)) {
return false;
}
}

return true;
}

/**
* Check if a single word's derivatives show up.
*
* @param string $word
* @param array $derivatives
* @param string $value
* @return bool
*/
private function singleWordDerivativesPass($word, $derivatives, $value)
{
foreach ($derivatives as $derivative) {
if (strpos($value, $derivative) !== false) {
$this->word = $word;

return false;
}
}

return true;
}

/**
* Assemble the derivatives list used in partial word validation.
*
* @return array
*/
protected function assembleDerivatives()
{
$bad_words = [];
foreach (config('defaults.banned_words.partial') as $bad_word) {
$bad_words[$bad_word] = [
$bad_word,
str_plural($bad_word),
$bad_word[0].str_repeat('*', strlen($bad_word) - 1),
$bad_word[0].str_repeat('*', strlen($bad_word) - 2).$bad_word[-1],
];
}

return $bad_words;
}

/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return "The word {$this->word} can't appear in the :attribute.";
}
}
17 changes: 17 additions & 0 deletions config/defaults.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
<?php

return [
'banned_words' => [
/*
* The following words are not allowed in show titles, or any other
* custom validation field that has a "profanity" flag on it. This list
* is adapted from the UK Office of Communications' 2010 study on
* acceptable language to use in broadcasting. Presence of any of these
* strings, even in longer words, will throw a validation fault.
*/
'partial' => ['shit', 'piss', 'fuck', 'cunt', 'cock', 'tit', 'bitch', 'bastard'],

/*
* These words must be present in isolation to trigger a fault (usually
* because there are "safe" words that include these strings, such as
* "pass" being a safe word)
*/
'full' => ['ass', 'asshole', 'pussy'],
],
'directory' => 'https://apps.carleton.edu/stock/ldapimage.php?id=',
'title' => 'KRLX Community',
'salt' => env('OAUTH_SALT', 'krlx'),
Expand Down
48 changes: 48 additions & 0 deletions tests/Feature/CustomRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Tests\Feature;

use KRLX\Show;
use KRLX\User;
use KRLX\Track;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
Expand Down Expand Up @@ -39,4 +41,50 @@ public function testValidationRuleValidationRule()
$this->assertEquals(422, $request->status(), "Did not receive HTTP 422 on rule $rule.");
}
}

/**
* Test the "Profanity" validation rule: Does a string contain bad words?
*
* @return void
*/
public function testProfanityRule()
{
$show = factory(Show::class)->create();
$user = factory(User::class)->create();
$show->hosts()->attach($user, ['accepted' => true]);

$partial = config('defaults.banned_words.partial')[0];
$full = config('defaults.banned_words.full')[0];
$bad_words = [
$partial,
$partial.'hole',
str_plural($partial),
$full,
str_plural($full),
'F***',
'F**k',
'F@$#',
];
$good_words = [
'prefix'.$full,
'hole',
'pals!!!',
];
foreach ($good_words as $word) {
$request = $this->actingAs($user, 'api')->json('PATCH', "/api/v1/shows/{$show->id}", [
'title' => $word,
]);
if ($request->status() == 500) {
dump($request->json());
}
$this->assertEquals(200, $request->status(), "Did not receive HTTP 200 with word $word.");
}
foreach ($bad_words as $word) {
$request = $this->actingAs($user, 'api')->json('PATCH', "/api/v1/shows/{$show->id}", [
'title' => $word,
]);
$this->assertEquals(422, $request->status(), "Did not receive HTTP 422 with word $word.");
$this->assertNotEquals('The word can\'t appear in the title', array_get($request->json(), 'errors.title.0'));
}
}
}

0 comments on commit 73d36a9

Please sign in to comment.