Weave 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

Sanitizer Laravel Package

smorken/sanitizer

Sanitizer for Laravel and PHP applications that cleans untrusted input and output, stripping unsafe HTML and helping prevent XSS. Configurable filters make it easy to define allowed tags and attributes for safe, consistent data handling.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install (expect a VCS entry). With zero stars and no Packagist presence indicated, add the Bitbucket repo explicitly:

"repositories": [
  { "type": "vcs", "url": "https://bitbucket.org/scott_morken/laravel-sanitizer.git" }
],
"require": {
  "smorken/sanitizer": "dev-master"
}

2. Read the source before the README. For a package this small ("Sanitizer helper"), src/ is the documentation. Look for, in order:

  • The service provider (what it binds, whether it's auto-discovered via extra.laravel.providers).
  • The facade accessor and the underlying class — that class's public methods are the API.
  • Any config/*.php file — that tells you whether allowlists are static or configurable.

3. Run the test suite locally (vendor/bin/phpunit inside the package or via composer test) to confirm it still works on your Laravel/PHP version. This is the single most important step for a zero-dependent package — nobody else is telling you it's broken.

4. First use case: sanitizing user-authored HTML. The canonical job is a rich-text field (bio, comment, CMS body) where you want a safe subset of tags. Sanitize on the way out, at render time, so historical data is covered when you tighten rules:

{!! app('sanitizer')->sanitize($comment->body) !!}

Verify the actual method name (sanitize, clean, purify — check src/) before wiring it in.

If the package turns out to be a thin wrapper over ezyang/htmlpurifier or mews/purifier, note that early: you may be better off depending on the wrapper's dependency directly.

Implementation Patterns

Output sanitization is the default posture. Store raw input, sanitize at render. This lets you change allowlists without a data migration and keeps one code path instead of "sanitize on create, sanitize on update, forget on import."

Wrap it once, not everywhere. Don't scatter facade calls through Blade templates. Create a single seam:

// app/Support/Html.php
class Html
{
    public static function clean(?string $value): string
    {
        return $value === null ? '' : app('sanitizer')->sanitize($value);
    }
}

Then a Blade directive or a component so templates stay declarative:

Blade::directive('sanitized', fn ($expr) => "<?php echo \\App\\Support\\Html::clean($expr); ?>");

This seam is also your escape hatch — if the package dies, you reimplement one method.

Form Request integration for plain-text fields. For fields that should never contain markup (names, titles, search terms), strip tags on input so downstream code doesn't have to think about it:

protected function prepareForValidation(): void
{
    $this->merge([
        'name'  => strip_tags((string) $this->input('name')),
        'email' => trim((string) $this->input('email')),
    ]);
}

Prefer native strip_tags/filter_var here — a helper package earns its keep on HTML, not on trimming.

Eloquent cast or accessor for HTML fields. If a model column is always rendered as HTML, an accessor centralizes the policy:

public function getBodyAttribute($value): string
{
    return Html::clean($value);
}

Use this only when every consumer wants sanitized output. If you also need raw for diffs, search indexing, or an email plaintext part, use an explicit body_clean accessor instead — silent sanitization makes debugging content loss painful.

Middleware for blanket coverage. If the package exposes a configurable filter list, a middleware that walks $request->all() and sanitizes known HTML keys (not everything — sanitizing passwords, JSON payloads, or base64 blobs is destructive) gives you defense in depth without touching 40 controllers.

Testing pattern. Assert two things: allowed markup survives, and known XSS vectors don't. Keep a fixture file per rich-text field:

it('strips script tags', function () {
    $dirty = '<p>ok</p><script>alert(1)</script>';
    expect(Html::clean($dirty))->toBe('<p>ok</p>');
});

it('preserves links', function () {
    expect(Html::clean('<a href="https://example.com">x</a>'))
        ->toContain('href="https://example.com"');
});

These tests are also your migration path off the package later.

Gotchas and Tips

It is a zero-star, zero-dependent package. Treat it as unmaintained until proven otherwise. Consequences: no security advisories will reach you, Laravel major-version bumps may silently break it, and PHP 8.x deprecations may surface as warnings. If it survives your test run and does what you need, vendor the relevant class into app/Support/ rather than carrying the dependency. A sanitizer is typically a few dozen lines; the supply-chain risk outweighs the convenience.

Double-escaping is the #1 bug. {!! Html::clean($x) !!} is correct; {{ Html::clean($x) !!}} will render &lt;p&gt;. Conversely, sanitizing and then escaping means your allowlisted tags appear as literal text to users. Pick one policy per field and document it.

Sanitizing on input loses data irreversibly. If you ever change an allowlist (<iframe> for embeds, class attributes for a new layout), previously stored rows are already stripped. Input-side sanitization should be reserved for fields where markup is never legitimate.

Allowlist config is the whole security surface. Find where the permitted tags/attributes live and pin it in your own config, not the package default — package defaults may be permissive or may change on update. Explicitly forbid style, on*, and javascript:/data: URLs unless you have a specific reason.

Attribute-level holes are easy to miss. Tag allowlists don't stop <a href="javascript:..."> or target="_blank" without rel="noopener". Check whether the package does attribute filtering and URL scheme validation, or only strip_tags-level work. Test with real payloads, not <script>alert(1)</script> alone.

Watch for double-sanitization in pipelines. If middleware sanitizes and the accessor sanitizes again, HTML entities get re-encoded (&amp;lt;). Sanitizers are usually idempotent on tags but not always on entities — add a test asserting clean(clean($x)) === clean($x).

Laravel version compatibility. Check composer.json illuminate/* constraints. Facade auto-discovery (extra.laravel) only exists from 5.5+; older codebases need a manual config/app.php entry. If constraints cap below your Laravel version, composer require will fail — use --ignore-platform-reqs only as an experiment, never in production.

Extension points to look for (and to add if absent): a Sanitizer::macro() registration, a custom-filter/config callback, or a HtmlPurifier-style definition object. If none exist, do not fork — the wrapper seam from Implementation Patterns gives you the same extensibility with none of the upgrade risk.

Debugging content loss. When a user reports "my formatting disappeared," bisect: log the raw value, the sanitized value, and the rendered output. The loss is almost always an unlisted tag or attribute, not the package misbehaving. Keep the raw value stored so you can re-derive output after fixing the allowlist.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core