Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filter Laravel Package

joomla/filter

Joomla Filter provides input sanitization and filtering utilities for PHP apps. Use InputFilter to allow/block specific HTML tags and attributes, and OutputFilter for safe output helpers like URL-safe strings. Composer installable, lightweight, framework-ready.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package via Composer:

composer require joomla/filter "~3.0"

For Laravel, register the package in config/app.php under providers (if not auto-discovered). The primary entry point is the Joomla\Filter\InputFilter class.

First use case: Sanitize user-generated HTML input (e.g., from a form or comment system) to prevent XSS attacks:

use Joomla\Filter\InputFilter;

// Basic HTML sanitization
$cleanHtml = InputFilter::clean($userInput, 'html');

// Customize allowed tags/attributes
$filter = new InputFilter(
    ['script', 'iframe'], // blockedTags
    ['onclick', 'onload'], // blockedAttributes
    InputFilter::ONLY_ALLOW_DEFINED_TAGS,
    InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
);
$cleanHtml = $filter->clean($userInput);

Key files to reference:

  • src/InputFilter.php (core filtering logic)
  • src/OutputFilter.php (URL/string sanitization)
  • tests/ (examples of edge cases and payloads)

Implementation Patterns

1. Request Sanitization Workflow

Integrate with Laravel’s request pipeline (e.g., middleware or form requests):

// In a middleware or FormRequest class
public function validateSanitizedInput(array $data)
{
    $filter = new InputFilter(
        ['script', 'style'], // blockedTags
        ['style', 'href'],   // blockedAttributes
        InputFilter::ONLY_ALLOW_DEFINED_TAGS,
        InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
    );

    foreach ($data as &$value) {
        if (is_string($value)) {
            $value = $filter->clean($value, 'html');
        }
    }
    return $data;
}

2. Dynamic Filter Rules

Store allowed tags/attributes in the database (e.g., for user roles) and hydrate the filter dynamically:

// In a service class
public function getRoleBasedFilter($role)
{
    $roleConfig = RoleConfig::where('name', $role)->first();
    return new InputFilter(
        $roleConfig->blocked_tags,
        $roleConfig->blocked_attributes,
        InputFilter::ONLY_ALLOW_DEFINED_TAGS,
        InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
    );
}

3. Blade Directives for Output

Create a Blade directive to auto-sanitize output:

// In AppServiceProvider::boot()
Blade::directive('sanitize', function ($expression) {
    return "<?php echo (new \\Joomla\\Filter\\InputFilter())->clean({$expression}, 'html'); ?>";
});

// Usage in Blade:
@sanitize($userComment)

4. Validation Rule Integration

Extend Laravel’s validation with custom rules:

use Joomla\Filter\InputFilter;

class SanitizeRule extends Rule
{
    public function passes($attribute, $value)
    {
        $filter = new InputFilter([], [], InputFilter::ONLY_ALLOW_DEFINED_TAGS);
        return $filter->clean($value, 'html') === $value;
    }
}

// Usage:
'comment' => ['required', new SanitizeRule],

5. URL Slug Generation

Use OutputFilter for SEO-friendly slugs (requires joomla/language for multibyte support):

use Joomla\Filter\OutputFilter;

$slug = OutputFilter::stringURLSafe($title);

Gotchas and Tips

Pitfalls

  1. Global State in Static Class: The static InputFilter::clean() method uses global configurations. Avoid it in long-running processes (e.g., queues, Swoole) or multi-tenant apps. Instantiate per-request:

    // ❌ Avoid (global state)
    $clean = InputFilter::clean($input);
    
    // ✅ Prefer (per-request)
    $filter = new InputFilter();
    $clean = $filter->clean($input);
    
  2. XSS Evasion Characters: Versions <2.0.6, <3.0.5, and <4.0.1 failed to strip common XSS evasion techniques (e.g., javascript:, &#x27;). Always pin to a patch version (e.g., ~3.0.6) and test with payloads like:

    $payloads = [
        '<img src=x onerror=alert(1)>',
        '<script>alert(1)</script>',
        'javascript:alert(1)',
        '&#x61;&#x6C;&#x65;&#x72;&#x74;(1)',
    ];
    
  3. Multibyte String Handling:

    • Pre-3.0.1: Used mb_* functions inconsistently, risking encoding issues.
    • Post-3.0.1: Defaults to non-multibyte methods. For multibyte input (e.g., CJK slugs), install joomla/language and use OutputFilter::stringURLSafe() with the transliterate option:
      $slug = OutputFilter::stringURLSafe($title, ['transliterate' => true]);
      
  4. Nested Tag Recursion Bug: Versions <3.0.2 and <2.0.5 had issues with recursive tags (e.g., <img style="..."/>). Test with:

    $html = '<div><img src="x" style="display:none"/></div>';
    $filter = new InputFilter(['img'], [], InputFilter::ONLY_BLOCK_DEFINED_TAGS);
    $clean = $filter->clean($html); // Should strip <img> in v3.0.2+
    
  5. HTML5 Media Tags: The stripImages/stripIframes methods may miss modern tags like <picture>, <video>, or <audio>. Extend the filter:

    $filter = new InputFilter(['picture', 'video', 'audio'], [], InputFilter::ONLY_BLOCK_DEFINED_TAGS);
    

Debugging Tips

  • Log Filtered Output: Compare input/output to spot missed payloads:
    \Log::debug("Input:", [$input]);
    \Log::debug("Output:", [$filter->clean($input)]);
    
  • Test with OWASP XSS Payloads: Use OWASP’s XSS Filter Evasion Cheat Sheet to validate edge cases.

Extension Points

  1. Custom Filter Types: Extend InputFilter to add new filter types (e.g., 'bbcode'):

    class CustomFilter extends InputFilter
    {
        protected function filter($value, $type)
        {
            if ($type === 'bbcode') {
                return $this->processBBCode($value);
            }
            return parent::filter($value, $type);
        }
    }
    
  2. Pre/Post-Filter Hooks: Override preFilter() or postFilter() for custom logic:

    class LoggingFilter extends InputFilter
    {
        protected function preFilter($value, $type)
        {
            \Log::debug("Filtering {$type}:", [$value]);
            return parent::preFilter($value, $type);
        }
    }
    
  3. Laravel Service Provider: Bind the filter to the container for dependency injection:

    // In AppServiceProvider::register()
    $this->app->bind(InputFilter::class, function () {
        return new InputFilter([], [], InputFilter::ONLY_ALLOW_DEFINED_TAGS);
    });
    

Performance Considerations

  • Reuse Instances: Instantiate InputFilter once per request (e.g., in middleware) and reuse it.
  • Avoid Over-Filtering: If you only allow <b>, <i>, and <p>, explicitly whitelist them instead of blacklisting everything else:
    $filter = new InputFilter([], [], InputFilter::ONLY_ALLOW_DEFINED_TAGS);
    $filter->setAllowedTags(['b', 'i', 'p']);
    

Security Validation

  • Regular Audits: Schedule tests with updated XSS payloads after dependency updates.
  • CVE Monitoring: Subscribe to Joomla’s security announcements for this package. The CVE-2022-23800 fix (v2.0.1+) is critical—ensure you’re on a patched version.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope