stevebauman/purify
Laravel wrapper for HTMLPurifier to sanitize user-submitted HTML. Clean strings or arrays via a simple facade, with support for dynamic per-call configuration, published config, and caching options to keep output safe and consistent.
Installation:
composer require stevebauman/purify
php artisan vendor:publish --provider="Stevebauman\Purify\PurifyServiceProvider"
config/purify.php exists and is configured.First Use Case:
use Stevebauman\Purify\Facades\Purify;
$dirtyHtml = '<script>alert("XSS")</script><p>Hello</p>';
$cleanHtml = Purify::clean($dirtyHtml); // Returns: `<p>Hello</p>`
Stevebauman\Purify\Facades\Purify (for quick usage).config/purify.php (default rules, custom configs).Stevebauman\Purify\Casts\PurifyHtmlOnGet (Eloquent integration).Stevebauman\Purify\Definitions\* (extend HTML/CSS rules).Basic Sanitization:
// Single string
$clean = Purify::clean($userInput);
// Array of strings
$cleanArray = Purify::clean($userInputs);
Dynamic Configurations:
$customConfig = ['HTML.Allowed' => 'div,p,a[href]'];
$clean = Purify::config($customConfig)->clean($input);
Named Configs (via config/purify.php):
// config/purify.php
'configs' => [
'comments' => ['HTML.Allowed' => 'p,strong,em'],
];
// Usage
$clean = Purify::config('comments')->clean($comment);
Eloquent Integration:
// Model (Laravel 11+)
protected function casts(): array
{
return [
'content' => PurifyHtmlOnGet::class, // Uses default config
'description' => PurifyHtmlOnGet::class.':comments', // Uses 'comments' config
];
}
API Request Sanitization:
// Middleware or Controller
public function store(Request $request)
{
$request->merge([
'clean_content' => Purify::clean($request->content),
]);
}
request()->input() or request()->all() before storage.{{ $model->content }} (auto-sanitized via PurifyHtmlOnGet).return response()->json(['content' => Purify::clean($model->content)]);
Cache Stale Definitions:
definitions in config/purify.php, run:
php artisan purify:clear
Overly Restrictive Configs:
HTML.Allowed may break legitimate markup (e.g., style attributes).Html5Definition or use HTML.ForbiddenElements to whitelist.Performance in Loops:
'serializer' => null, // config/purify.php
Nested Config Overrides:
Purify::config() replaces default settings, not merges.$mergedConfig = array_merge(config('purify.configs.default'), ['HTML.Allowed' => '...']);
Purify::config($mergedConfig)->clean($input);
CSS Property Gaps:
CSS.AllowedProperties lacks modern values (e.g., text-align: start).CSSDefinition:
$definition->info['text-align'] = new \HTMLPurifier_AttrDef_Enum(['start', 'end', 'center']);
\Log::debug('Dirty HTML:', [$dirtyHtml]);
\Log::debug('Clean HTML:', [Purify::clean($dirtyHtml)]);
Custom Definitions:
Html5Definition for new elements/attributes (e.g., Trix editor):
class TrixDefinition implements Definition {
public static function apply($definition) {
Html5Definition::apply($definition);
$definition->addElement('figure', 'Inline', 'Flow', 'Common');
// ...
}
}
config/purify.php:
'definitions' => \App\TrixDefinition::class,
CSS Overrides:
gap or grid-template:
class CustomCssDefinition implements CssDefinition {
public static function apply($definition) {
$definition->info['gap'] = new \HTMLPurifier_AttrDef_Length(['px', 'em']);
}
}
config/purify.php:
'css-definitions' => \App\CustomCssDefinition::class,
Fallback for Missing Tags:
HTML.ForbiddenElements to whitelist instead of blacklisting:
'HTML.ForbiddenElements' => 'script,iframe,object',
$testCases = [
'<script>alert(1)</script>' => '<p>alert(1)</p>', // If 'p' is allowed
'<div style="background:red">' => '<div style="background:red">', // If CSS is allowed
];
$start = microtime(true);
Purify::clean($largeHtml);
$time = microtime(true) - $start;
\Log::info("Purify time: {$time}s");
PurifyHtmlOnSet for Input Sanitization:
protected $casts = [
'content' => PurifyHtmlOnSet::class, // Sanitize on save
];
How can I help you explore Laravel packages today?