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

Htmlpurifier Laravel Package

ezyang/htmlpurifier

HTML Purifier is a PHP library that filters untrusted HTML using robust whitelists and aggressive parsing to prevent XSS attacks while ensuring standards-compliant output. Ideal for safely rendering rich, CSS-enabled documents from untrusted sources.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. HTML Purifier is a framework-agnostic, self-contained PHP library — no runtime dependencies beyond PHP and its bundled CSSTidy. It does not present as a Laravel package: no service provider, no facade, no publishable config, no Artisan commands. Adopting it means we own the integration layer as a deliverable in its own right: a singleton binding, per-context configuration objects (comment, rich_text, profile_bio, admin_html), a HtmlSanitizer wrapper service, and a writable definition-cache path. Estimate 150–300 lines of glue plus tests. Treat the vendor library as the trivial part and the wrapper as the workstream.

Integration feasibility. High and well understood. The integration surface is narrow: one sanitization call per untrusted HTML field, config-per-trust-context, and a cache directory. Two structural traps to design out from day one: (1) sanitization is mutation, not validation — it must never live inside FormRequest::rules() or any code path that returns a boolean; (2) direct new HTMLPurifier() calls from controllers or Blade hard-code the library into the application and make any future swap expensive. Enforce the wrapper boundary via code review plus a static-analysis rule (PHPStan/Psalm forbidden-call rule).

Technical risk.

  • LGPL-2.1 is the gating item and precedes every technical decision. Unmodified Composer consumption is generally acceptable for hosted SaaS, but obligations change materially if we ship a bundled or on-prem artifact, or link modifications. Legal sign-off must land before sprint commitment; a late reversal forces a rebuild against a permissive alternative (symfony/html-sanitizer, HTMLawed) plus a full re-run of the content-regression corpus. This is the single highest-impact unknown.
  • CPU cost and hostile-input surface. Purifier is deliberately slower than strip_tags() or regex approaches. v4.19.0 patched catastrophic backtracking in Core.AggressivelyFixLt; v4.19.1 patched PHP 8.5 deprecations and a lexer null-parameter issue. Payload size caps, request timeouts, and async processing for large submissions are requirements, not hardening.
  • Silent content mutation is a product bug. The filter removes; it does not reject. Config changes quietly delete legitimate embeds, tables, attributes, and CSS. Every allowlist change needs a curated payload corpus and a golden-file diff in CI.
  • Cache coupling. v4.19.1 made cache-directory chmod best-effort, which helps on restricted filesystems, but read-only container images and ephemeral pods still need an explicit writable path (storage/app/htmlpurifier) warmed at build time. Cold cache on first request is a measurable latency spike. Definition cache must be invalidated on config change (DefinitionRev), or stale definitions silently persist.
  • Maintainer concentration. Semantic-release cadence and PHP 8.4/8.5 tracking signal health, but the project is effectively single-maintainer. The "0 dependents" figure is a data artifact — this library is transitively ubiquitous — and must not be used as adoption evidence.

Key questions.

  1. Do we distribute any built artifact containing the dependency (on-prem, desktop, white-label, source escrow)? This determines whether LGPL is a blocker.
  2. Which product fields carry untrusted rich HTML, and what is the per-request latency budget for each?
  3. How much legacy, unsanitized HTML already sits in the database, and can we backfill?
  4. Who owns the allowlist policy — product, security, or platform — and what is the change-review process?
  5. Are embeds (iframes, video) in scope? That drives the HTML.SafeIframe / URI.SafeIframeRegexp configuration surface.
  6. Is output divergence from editor input acceptable to UX stakeholders?

Integration Approach

Stack fit. Laravel 9–12 on PHP 8.1–8.5. Install via composer require ezyang/htmlpurifier; the library has no Laravel-version coupling, so it will not constrain framework upgrades. Configuration is driven through HTMLPurifier_Config arrays held in config/htmlpurifier.php, with per-context definitions resolved by a factory in the service provider.

Migration path. Three phases. Phase 1 — shadow mode: sanitize on read behind a feature flag, log diffs against current output, and build the golden-file corpus from real payloads. No user-visible change. Phase 2 — sanitize-on-write: apply at the trust boundary (form request → service → persistence) for new and edited content, keeping shadow-mode read sanitization as defense in depth for legacy rows. Phase 3 — backfill and retire: run a queued backfill over historical content, then remove the shadow flag. Store sanitized output; do not sanitize solely at render time, or output correctness becomes coupled to config drift.

Compatibility. Pin the minor version (^4.19) and review the changelog on every bump — semver has historically carried behavior changes (default image widths, universal CSS values, tel URI commas). Verify the definition cache serialization format across upgrades; a version bump should force a cache namespace change. Confirm the PHP version matrix against our deployment targets, including 8.5 if that is on the roadmap.

Sequencing. (1) Legal review of LGPL obligations — blocking. (2) Wrapper service + provider + config skeleton, with unit tests. (3) Trust-context allowlist definitions reviewed by security and product. (4) Shadow-mode instrumentation and corpus capture. (5) Write-path cutover behind a flag. (6) Backfill job. (7) Decommission shadow mode. Do not parallelize (1) with (5); the reversal cost is a full rebuild.

Operational Impact

Maintenance. Low ongoing engineering cost once the wrapper exists — the library is stable, semantic-release driven, and actively patched for PHP deprecations. The real maintenance burden is policy, not code: allowlists drift as product adds features (embeds, math, tables), and each drift is a config change requiring corpus validation. Assign a named owner for the allowlist.

Support. No commercial vendor support and no SLA. Security response depends on upstream maintainer availability; a single-maintainer project means we must be prepared to patch or pin ourselves. Track upstream advisories and CVEs through our dependency scanning pipeline, and keep a documented fallback path to an alternative sanitizer so a support failure is a switch, not a rewrite.

Scaling. Purifier is CPU-bound and scales horizontally with PHP workers, not with database or cache tiers. Budget for it: sanitizing a few KB of HTML is single-digit milliseconds warm-cache, materially worse cold. Two scaling rules: never sanitize in a hot read loop for content already sanitized at write time, and push large or batched submissions (imports, bulk edits, API ingestion) to queues. Cap input size at the edge — a 1 MB HTML body should be rejected before it reaches the lexer.

Failure modes. (a) Unwritable cache directory — the classic container deployment failure; falls back to reparsing per request and can take the service down under load. Mitigate with a startup health check and build-time warm. (b) Stale definition cache after a config change — content silently filtered by an old policy. Mitigate with a DefinitionRev bump in the same commit as any allowlist edit. (c) Over-aggressive allowlist — legitimate user content disappears; detected only by users. Mitigate with the golden-file corpus and a canary on config rollout. (d) Backtracking on pathological input — mitigated by size caps, timeouts, and staying current on patch releases. (e) Silent config regression from a careless merge — mitigated by treating config as reviewed code.

Ramp-up. Moderate. The library's own documentation lives in docs/ and INSTALL rather than a modern site, and the configuration surface is large (hundreds of directives across HTML, CSS, URI, Core, AutoFormat). Expect roughly one engineer-week for the wrapper and provider, plus one to two weeks of corpus-building and allowlist tuning before write-path cutover. The WYSIWYG doc is the right starting point if editors like TinyMCE are in scope, since editor output and purifier output must be reconciled deliberately rather than discovered in production.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource