tgalopin/html-sanitizer-bundle
Symfony bundle integrating the html-sanitizer library so you can define and configure HTML sanitizers as services, clean untrusted markup safely, and use customizable sanitizer profiles throughout your application.
Architecture fit: This is a Symfony bundle (tgalopin/html-sanitizer-bundle) that wires the framework-agnostic tgalopin/html-sanitizer library into Symfony's DI container and configuration system. It is not a Laravel package — there is no service provider, no publishable config, no facade. For a Laravel codebase the realistic architecture is: depend on the underlying sanitizer library directly, bind a configured HtmlSanitizerInterface singleton in a small SanitizerServiceProvider, and drive allowlists from config/sanitizer.php. The bundle's release activity is still useful signal — it tells us the upstream sanitizer API is stable and receiving compatibility work — but the bundle artifact itself should not be installed in a Laravel app.
Integration feasibility: High for the library, low for the bundle. The 1.4.0 changes are relevant in three ways: (1) Symfony 6 support confirms the package is still maintained against modern PHP/Symfony component versions, which matters indirectly because Laravel pulls in symfony/console, symfony/http-foundation, etc.; (2) the missing-return-type deprecation fix reduces noise on PHP 8.1+; (3) the env-override fix for the config key is genuinely useful for 12-factor deployments — sanitizer profiles can now differ per environment without config edits. None of these are blockers or enablers for a Laravel integration.
Technical risk: The dominant risk is upstream lifecycle, not this release. The underlying tgalopin/html-sanitizer line has been superseded in the ecosystem by symfony/html-sanitizer, and a bundle that only just reached Symfony 6 support in 1.4.0 suggests reactive rather than proactive maintenance. Secondary risks: the new details extension expands the allowed-markup surface (the open attribute and nested summary content are new vectors to validate); the env-override change alters configuration precedence, so any environment relying on the old behaviour will silently change; and the return-type fix implies the codebase previously emitted deprecations, so a E_DEPRECATED-as-error CI policy could have been masking issues.
Key questions:
tgalopin/html-sanitizer formally deprecated, and if so what is our exit path to symfony/html-sanitizer?details extension permit attribute injection via open, and has it been fuzzed?Stack fit: Target Laravel 9/10/11 on PHP 8.1+. Consume tgalopin/html-sanitizer (or symfony/html-sanitizer) as a library. Implement a SanitizerServiceProvider that reads config/sanitizer.php, constructs a HtmlSanitizer with our extension set, and registers it as a singleton behind an interface so it can be swapped or mocked. Do not install the bundle — its service definitions will not load and it adds a transitive Symfony framework constraint for no benefit.
Migration path: Inventory every current sanitization call site (strip_tags, htmlspecialchars on trusted-HTML fields, any HTMLPurifier usage, Blade {!! !!} sinks). Classify each as a read path (render) or write path (persist). Migrate write paths first so stored data is clean, then read paths. Build a golden-file test suite of representative and malicious payloads before swapping any call site, and run old and new sanitizers in parallel on production traffic (log-only) to diff output for a week.
Compatibility: Symfony 6 support in the bundle is irrelevant to a Laravel install; what matters is the sanitizer library's PHP constraint and its dependency on symfony/css-selector/masterminds/html5. Verify our Laravel version's Symfony component pins do not conflict. The return-type fix removes a deprecation that would surface under PHP 8.1+ with strict error reporting, so 1.4.0 is a mild compatibility improvement even for library-only consumers.
Sequencing:
details/summary cases if we enable that extension.Maintenance: Small dependency surface and low release cadence — roughly four PRs in this cycle, all small. Cost is dominated by allowlist curation, not code. The real maintenance burden is governance: we must track whether the upstream sanitizer is archived and hold a documented migration path to symfony/html-sanitizer. Budget a recurring quarterly review rather than continuous work.
Support: No first-party Laravel support and no vendor SLA. Ownership sits with the platform/backend team. Internal documentation should cover how to add an allowed tag or attribute, since that will be the most frequent request and the most security-sensitive change. Expect ad-hoc requests from content and marketing teams for markup we deliberately disallow.
Scaling: Sanitization is in-process and CPU-bound on string parsing; it scales horizontally with the app tier and adds no external dependency, which is a genuine advantage over a sidecar or API-based sanitizer. Two caveats: cache the constructed sanitizer/config per process (do not rebuild per request), and cap input size before parsing — pathological HTML is a CPU-exhaustion vector. For bulk import or backfill jobs, run sanitization in queue workers with a memory limit rather than in web requests.
Failure modes:
details extension misconfiguration → unexpected attributes surviving; validate before enabling.Ramp-up: A mid-level Laravel developer can integrate the library in under a day. The non-obvious learning is the extension model — writing a custom extension to permit a specific tag/attribute combination, and understanding that an extension granting an attribute on an element effectively grants it everywhere that element appears. Allocate one to two days of security-oriented pairing for whoever owns the allowlist, and require a second reviewer on any change to it.
How can I help you explore Laravel packages today?