jeffersongoncalves/laravel-html-sanitizer
A Laravel package for sanitizing untrusted HTML before rendering. It helps remove dangerous tags and attributes to reduce XSS risks, with configurable rules for safely handling user-generated content in your applications.
Architecture fit. jeffersongoncalves/laravel-html-sanitizer is a thin Laravel service-provider wrapper around a PHP HTML sanitization engine. Architecturally it slots into the framework's standard package contract — auto-discovered provider, publishable config, facade/helper surface — so it does not impose a structural opinion on the application. That is the right shape for a cross-cutting concern like output sanitization: it is adoptable at the edges (controllers, form requests, Blade) without refactoring domain code. It is a utility package, not a platform component; treat it as one interchangeable dependency in the XSS-defense layer rather than as a trust boundary you build around.
Integration feasibility. Low. In a standard Laravel app this is a composer require plus php artisan vendor:publish plus an allowlist of permitted tags/attributes. No database migrations, no queue workers, no external services. A competent Laravel engineer can produce a working spike in under a day. The real effort is not integration but policy: deciding which content fields are rich text, what each field is allowed to contain, and whether sanitization happens on write (stored clean) or read (cleaned at render). That decision is product- and compliance-driven, not technical.
Technical risk. Three concerns, in order:
v1.1.1...v1.2.0 changelog URL — no description of added features, changed defaults, dependency bumps, or deprecations. Semver minor implies backwards-compatible additive change, but that is an inference, not a stated guarantee. We cannot assess upgrade safety from the artifact provided.Key questions. What sanitization engine does the package depend on, at what version constraint, and does v1.2.0 change that constraint? What exactly changed in v1.2.0 — configuration keys, default allowlist, facade signature, minimum Laravel/PHP? Does the config support multiple named profiles (e.g. comment vs article) or is it a single global allowlist? Is there a validation rule and a Blade directive, or only a facade? What is the Laravel/PHP support matrix, and is Laravel 12 / PHP 8.4 covered? Who is the upstream engine maintainer and what is its CVE history and release cadence? Does the package do anything beyond strip_tags-class filtering — specifically, does it neutralize javascript: URIs, on* handlers, and CSS-based vectors? Is the diff between v1.1.1 and v1.2.0 small enough for our engineer to review line-by-line before adoption?
Stack fit. Fits any conventional Laravel application using Blade or an API layer, and is particularly relevant where users submit HTML: rich-text editor fields, comment bodies, CMS article content, imported RSS/email HTML, and any field rendered unescaped via {!! !!}. It is not a substitute for contextual output escaping — Blade's {{ }} remains the default control, and this package only applies where raw HTML is a genuine product requirement. For a pure-JSON API with no rich-text surface, the package is unnecessary; do not add it speculatively.
Migration path. There is no migration — greenfield addition, and removal is a one-line composer change if it stays behind an internal boundary. Recommended: define an application-level interface (e.g. HtmlSanitizer contract with a clean(string $html, string $profile): string method) and bind the package's implementation to it in a service provider. Swapping to mews/purifier, Symfony's sanitizer, or a direct engine call then becomes a one-class change and the vendor package never appears in domain code.
Compatibility. Verify minimum PHP and Laravel versions against our support matrix before committing; a minor bump is the most likely point at which a minimum-version floor moves. Pin to ^1.2 rather than *, commit the lockfile, and read the v1.1.1→v1.2.0 diff in CI-gated review rather than accepting the changelog link at face value. If the diff touches the default allowlist, treat it as a behavior change requiring regression testing regardless of the version number.
Sequencing.
Maintenance. Low ongoing cost, but non-zero: a single dependency to patch, plus the underlying engine. Budget for a quarterly check of both. Because the maintainer publishes many small packages, expect occasional major-version churn across the portfolio and read every changelog rather than relying on automated minor-bumps. The v1.2.0 release, with no described contents, is a mild signal that release hygiene is informal — plan for the possibility that we are the ones documenting behavior for our own repo.
Support. Community-only. No SLA, no vendor, GitHub issues as the sole channel. Acceptable for a utility dependency, unacceptable if it were on the critical path of a regulated workflow. Ensure the internal abstraction and test suite mean an unanswered upstream issue never blocks a release on our side.
Scaling. Sanitization is CPU-bound string processing — negligible per request at typical volumes, measurable in bulk import or backfill jobs. Two mitigations: sanitize on write so the cost is paid once rather than on every render, and cache the cleaned output for high-traffic read paths. For large historical backfills, run through queued jobs with bounded batch sizes rather than a synchronous artisan command against a live table.
Failure modes. (a) Silent over-stripping — a config change quietly removes legitimate markup, discovered by users rather than tests. Mitigate with a golden-file test corpus of real content. (b) Under-sanitization — a new field added without a profile falls through to a permissive default; make the permissive default fail closed. (c) Engine CVE — an upstream advisory leaves us exposed until the wrapper releases; track the engine directly so we can patch independently. (d) Double-sanitization — cleaning both on input and on output mangles already-escaped entities; decide once and document it. (e) Config drift across environments if the config file is published and edited per-environment; keep allowlists in version control and deploy identically.
Ramp-up. Very low — a Laravel engineer is productive in hours, since the package follows framework conventions they already know. The genuine learning curve is the allowlist: understanding why each tag and attribute is present, and being disciplined enough not to widen it to make a rendering bug go away. That discipline, not the package, is what determines whether this integration improves or degrades the security posture. Assign one owner for the sanitization policy and route all allowlist changes through code review.
How can I help you explore Laravel packages today?