Blasp Profanity Filter for Laravel

repository·main·Indexed 43 days ago

https://github.com/blaspsoft/blasp

An advanced, driver-based profanity filter for Laravel applications supporting regex, pattern, and phonetic detection strategies. It features multi-language support, severity scoring, and seamless integration via Eloquent models, middleware, a custom validation rule (blasp_check), and a @clean Blade directive. Blasp includes a result caching system, a fluent API for driver pipelines, and a comprehensive testing suite with fakes and Artisan commands.

Tokens
12K
Snippets
39
Records
44
Agent score
50%

What's inside Blasp

  1. Listen for profanity detection events

    main

    If 'events' => true is set in your configuration, Blasp fires the following events:

    EventFired WhenProperties
    ProfanityDetectedcheck() finds profanityresult, originalText
    ContentBlockedMiddleware detects profanityresult, request, field, action
    ModelProfanityDetectedBlaspable trait detects profanitymodel, attribute, result

    Note: ModelProfanityDetected always fires and is not gated by the events config setting.

  2. How different detection drivers work

    main

    Blasp uses different drivers to detect various types of profanity:

    • regex driver (Default): Detects obfuscated profanity including substitutions (f4ck), separators (f-u-c-k), doubled letters (ffuucckkiinngg), and combinations. It allows up to 3 separator characters between letters.
    • pattern driver: Performs fast, exact word-boundary matching only.
    • phonetic driver: Uses metaphone() and Levenshtein distance to catch sound-alike evasions (e.g., phuck, sheit). Sensitivity is configurable in config/blasp.php under drivers.phonetic.
    • pipeline driver: Chains multiple drivers together using union merge semantics. Text is flagged if any driver finds a match. If multiple drivers detect the same word at the same position, only the longest match is kept.
  3. Integrate Blasp with Eloquent models

    main

    Use the Blaspable trait to automatically check model attributes during save. By default, it operates in sanitize mode (masking profanity), but can be configured to reject models entirely.

    use Blaspsoft\Blasp\Blaspable;
    
    class Comment extends Model
    {
        use Blaspable;
    
        // Attributes to check
        protected array $blaspable = ['body', 'title'];
    
        // Options:
        // 'sanitize' (default) | 'reject'
        protected string $blaspMode = 'reject';
        
        // null = config default
        protected string $blaspLanguage = 'spanish';
        protected string $blaspMask = '#';
    }

    Reject Mode

    In reject mode, saving a model with profanity throws a ProfanityRejectedException. You can catch this to access the failed attribute, the result, and the model:

    use Blaspsoft\Blasp\Exceptions\ProfanityRejectedException;
    
    try {
        $comment = Comment::create(['body' => 'profane text']);
    } catch (ProfanityRejectedException $e) {
        $e->attribute; // 'body'
        $e->result;    // Result instance
        $e->model;     // The unsaved model
    }

    Disabling checks

    To bypass checks for a specific operation:

    Comment::withoutBlaspChecking(function () {
        Comment::create(['body' => 'unchecked content']);
    });
  4. Use Blasp Middleware to filter requests

    main

    Register the blasp middleware alias to filter incoming request fields. You can specify the action (reject or sanitize) and the minimum severity level.

    ```php
    // Using the short alias (rejects by default)
    Route::post('/comment', CommentController::class)
        ->middleware('blasp');
    
    // With parameters: action, severity
    Route::post('/comment', CommentController::class)
        ->middleware('blasp:sanitize,mild');
    
    // Using the class directly
    use Blaspsoft\Blasp\Middleware\CheckProfanity;
    Route::post('/comment', CommentController::class)
        ->middleware(CheckProfanity::class);

    Middleware Actions:

    • reject (default): Returns a 422 JSON response with field errors.
    • sanitize: Replaces profane fields in the request with masked versions and continues.
  5. Migrate from v3 to v4

    main

    When upgrading to v4, note the following changes:

    Namespace Changes

    • Blaspsoft\Blasp\ServiceProvider is now Blaspsoft\Blasp\BlaspServiceProvider.
    • The Facade namespace Blaspsoft\Blasp\Facades\Blasp remains unchanged.

    Configuration Key Changes

    v3 Keyv4 KeyNotes
    default_languagelanguagedefault_language still works as alias
    mask_charactermaskmask_character still works as alias
    cache_drivercache.drivercache_driver still works as alias

    Result API Changes (v4 preferred methods)

    v3 Methodv4 Method
    hasProfanity()isOffensive()
    getCleanString()clean()
    getSourceString()original()
    getProfanitiesCount()count()
    getUniqueProfanitiesFound()uniqueWords()

    Builder API Changes (v4 preferred methods)

    v3 Methodv4 Method
    maskWith($char)mask($char)
    allLanguages()inAllLanguages()
    language($lang)in($lang)
    configure($profanities, $falsePositives)block(...$words) / allow(...$words)
  6. Manage profanity cache

    main

    Blasp caches check() results by default to improve performance. If the same text is checked with the same configuration (language, driver, severity, allow/block lists), the cached result is returned immediately.

    Note: Result caching is automatically bypassed when using a CallbackMask because closures cannot be serialized.

    To clear caches:

    • Via Artisan: php artisan blasp:clear (clears both dictionary and result caches).
    • Programmatically: Dictionary::clearCache();
    php artisan blasp:clear
  7. Publish Blasp configuration and language files

    main

    After installation, publish the configuration and language files using Artisan. You can choose to publish everything, only the configuration, or only the language files.

    # Everything (config + language files)
    php artisan vendor:publish --tag="blasp"
    
    # Config only
    php artisan vendor:publish --tag="blasp-config"
    
    # Language files only
    php artisan vendor:publish --tag="blasp-languages"
  8. Quick Start: Check text for profanity

    main

    Use the Blasp facade to perform a basic profanity check. The check() method returns a Result object containing details about the detection and methods to clean the text.

    use Blaspsoft\
    Blasp\Facades\Blasp;
    
    $result = Blasp::check('This is a fucking sentence');
    
    $result->isOffensive();  // true
    $result->clean();         // "This is a ******* sentence"
    $result->original();      // "This is a fucking sentence"
    $result->score();         // 30
    $result->count();         // 1
    $result->uniqueWords();   // ['fucking']
    $result->severity();      // Severity::High
  9. Install Blasp via Composer

    main

    Install the Blasp profanity filter for Laravel using Composer. Requires PHP 8.2+ and Laravel 8.0+.

    composer require blaspsoft/blasp
  10. Configure the Blasp profanity filter

    main

    The config/blasp.php file controls the behavior of the profanity filter. Key configuration sections include:

    • default: The driver to use ('regex', 'pattern', 'phonetic', or 'pipeline').
    • language: The default language (e.g., 'english').
    • mask: The default character used for masking (e.g., '*').
    • severity: The minimum severity level to trigger detection ('mild', 'moderate', 'high', or 'extreme').
    • events: Boolean to enable/disable global events.
    • cache: Settings for result caching, including enabled, driver, ttl, and results (to cache check() results by content hash).
    • middleware: Configuration for request-level filtering, including action ('reject' or 'sanitize'), fields to check, except to skip, and severity threshold.
    • model: Configuration for the Blaspable trait, specifically the mode ('sanitize' or 'reject').
    • drivers.phonetic: Settings for phonetic detection like phonemes (2-8), min_word_length, max_distance_ratio (0.3-0.8), and false_positives.
    • allow / block: Global allow-lists and block-lists.
    • separators / substitutions / false_positives: Custom character handling and word exclusions.
    return [
        'default'   => env('BLASP_DRIVER', 'regex'),       // 'regex' | 'pattern' | 'phonetic' | 'pipeline'
        'language'  => env('BLASP_LANGUAGE', 'english'),    // Default language
        'mask'      => '*',                                 // Default mask character
        'severity'  => 'mild',
        'events'    => false,
    
        'cache' => [
            'enabled' => true,
            'driver'  => env('BLASP_CACHE_DRIVER'),
            'ttl'     => 86400,
            'results' => true,
        ],
    
        'middleware' => [
            'action'   => 'reject',
            'fields'   => ['*'],
            'except'   => ['password', 'email', '_token'],
            'severity' => 'mild',
        ],
    
        'model' => [
            'mode' => env('BLASP_MODEL_MODE', 'sanitize'),  // 'sanitize' | 'reject'
        ],
    
        'drivers' => [
            'pipeline' => [
                'drivers' => ['regex', 'phonetic'],    // Sub-drivers to chain
            ],
            'phonetic' => [
                'phonemes' => 4,
                'min_word_length' => 3,
                'max_distance_ratio' => 0.6,
                'supported_languages' => ['english'],
                'false_positives' => ['fork', '...'],
            ],
        ],
    
        'allow'  => [],
        'block'  => [],
    
        'separators'      => [...],
        'substitutions'   => [...],
        'false_positives' => [...],
    ];
  11. Use PipelineDriver to chain multiple detection drivers

    main

    The PipelineDriver is a composite driver that allows you to run multiple profanity detection drivers in a single pass. It executes each configured driver, collects all detected MatchedWord instances, and resolves overlaps using a greedy, longest-first approach.

    When using a PipelineDriver, the resulting Result object will contain the union of all matches from the sub-drivers, deduplicated so that overlapping words are not double-masked. The final score is recalculated based on the unique set of matches found across all drivers.

    use Blaspsoft\Blasp\Drivers\PipelineDriver;
    // Assuming $otherDrivers is an array of DriverInterface implementations
    $pipeline = new PipelineDriver($otherDrivers);
    
    $result = $pipeline->detect($text, $dictionary, $mask, $options);
  12. Test profanity detection with Fakes

    main

    In your test suites, you can use Blasp::fake() to prevent actual detection logic from running. This allows you to simulate clean or offensive results.

    Common Assertions:

    • Blasp::assertChecked(): Asserts that a check was performed.
    • Blasp::assertCheckedTimes(int $times): Asserts the number of checks performed.
    • Blasp::assertCheckedWith(string $text): Asserts that a specific text was checked.
    use Blaspsoft\Blasp\Facades\Blasp;
    use Blaspsoft\Blasp\Core\Result;
    
    // Replace with a fake — all checks return clean by default
    Blasp::fake();
    
    // Pre-configure specific responses
    Blasp::fake([
        'bad text'   => Result::withMatches(['fuck']),
        'clean text' => Result::none('clean text'),
    ]);
    
    $result = Blasp::check('bad text');
    $result->isOffensive(); // true
    
    // Assertions
    Blasp::assertChecked();
    Blasp::assertCheckedTimes(1);
    Blasp::assertCheckedWith('bad text');