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

Svg Sanitize Laravel Package

enshrined/svg-sanitize

PHP SVG sanitizer based on DOMPurify that cleans untrusted SVG/XML, strips dangerous tags and remote references, supports custom whitelists, minification, and reports sanitization issues.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a single-responsibility, framework-agnostic PHP library that wraps ext-dom/ext-libxml to strip XSS vectors from user-supplied SVG. For a Laravel estate it is an edge dependency, not an architectural one: it constrains no layer, introduces no contracts into the domain model, and can be confined to an infrastructure concern behind an upload pipeline. That is exactly the profile a TPM wants from a security control — narrow blast radius, cheap to swap, no leakage into product code. It competes with nothing on the roadmap and replaces what would otherwise be a bespoke regex/DOM scrubber that no team should own.

Integration feasibility. High, with caveats. Composer install, a two-method happy path (sanitize(), getXmlIssues()), and well-documented options (removeRemoteReferences(), minify(), custom allowlists via TagInterface/AttributeInterface). Crucially, the library is already the sanitizer inside TYPO3 core (v9+) and underpins the WordPress Safe SVG plugin and the Drupal svg_sanitizer module. That is a strong, externally subsidised validation signal: millions of CMS uploads have exercised this code path, which materially de-risks our own penetration testing and threat-modelling effort. Treat "0 Packagist dependents" as a data artefact — it counts reverse dependencies, not adoption.

Technical risk. Four items deserve a risk register entry:

  1. Licensing (highest severity, lowest technical uncertainty). GPL-2.0 is copyleft. For a distributed Laravel product (shipped on-prem, sold as an appliance, included in a Docker image delivered to customers), obligations may attach to the combined work. For a purely hosted SaaS the practical exposure is usually lower, but legal must rule, not engineering. This is a ship-blocker on some product lines and a non-issue on others — segment the portfolio before committing.
  2. Release/maintenance signal is muddled. The supplied metadata lists a last release of 2026-10-20, which is in the future and therefore a data-quality defect in our source, not a fact. Independently, the 1.0.0 changelog shows recent, meaningful work (quadratic-time fix on element-dense SVGs, xlink:href case handling, PHP 8.5 in the test matrix, libxml deprecation fix) — evidence of active stewardship contradicted by low star count and sparse commit history. Resolve this contradiction before relying on the "mature" label.
  3. Allowlist surface area is a product decision, not a library default. SVG is enormous. The default policy will strip things legitimate customers expect (<use> cross-references, embedded fonts, gradient filters). Every relaxation reopens the threat model. Someone must own that policy for the life of the product — this is an ongoing cost most teams underestimate.
  4. Failure semantics are easy to get wrong. sanitize() returns false on parse failure; "clean" output is not the same as "renders". Both produce silent, user-invisible outcomes unless wired to telemetry. The 0.22.0 namespace-case bug is the template for this class of failure: it degraded quietly.

Key questions to close before the go/no-go gate.

  • Does our product portfolio distribute this artefact, or only operate it? Legal sign-off required either way, with the answer recorded.
  • Who is the named internal owner of the allowlist and the dependency, and what is the review cadence?
  • What is our measured throughput requirement (SVGs/sec at peak) against the 1.0.0 fixed-algorithm baseline — do we need queues on day one?
  • What is the acceptance criterion for "safe but invisible" SVGs — reject, accept, or render-verify?
  • Is there a client-side complement (DOMPurify at upload preview) or is server-side authoritative?
  • What is our exit cost to an alternative (e.g. a maintained fork, or a hosted scanning service) if maintenance stalls?

Integration Approach

Stack fit. Confine the library behind a first-party interface — a SvgSanitizer contract bound to a singleton in AppServiceProvider. Nothing beyond that boundary may call enshrined\svgSanitize\Sanitizer directly. This costs half a day and buys (a) instance reuse, which the library benefits from, (b) a single seam for error telemetry, and (c) an escape hatch if licensing forces a substitution. Wire it as a custom Rule object for form requests, plus a queued job for non-request paths (API imports, bulk migration, admin back-fill).

Migration path. Three phases: contain (instrument existing upload endpoints to detect and quarantine SVGs without blocking, establishing a baseline of what users actually upload), enforce (turn on rejection for new uploads once false-positive rate is known), remediate (re-scan the historical asset store off-peak, since pre-0.22 namespace-case SVGs may have been stored unsanitised). The quarantine-then-enforce ordering matters: shipping a hard block first turns a security fix into a support incident.

Compatibility. Requires PHP 7.2+ with ext-dom/ext-libxml; pin to ^1.0 to inherit the namespace-normalisation and complexity fixes. No known conflicts with the common Laravel upload/media stack. Verify the platform PHP version and that libxml_use_internal_errors() is not globally manipulated elsewhere in the application — global libxml state is a shared resource and a classic cross-package surprise.

Sequencing. (1) Legal/licence ruling and dependency-approval ticket. (2) Spike: 1 day to bind the singleton, add the validator, and prove the happy path plus a false path on a malformed file. (3) Threat-model session with security to agree the allowlist and remote-reference policy. (4) Shadow-mode telemetry for one to two weeks. (5) Enforce + alerting. (6) Historical back-fill. (7) Document ownership and hand to run. Gates at (1) and (4); everything between is engineering time measured in days, not sprints.

Operational Impact

Maintenance. The dependency itself is low-churn, but it is a security control, so it is not zero-touch: it needs someone to watch upstream releases and CVEs, and to re-validate the allowlist whenever the product adds SVG-heavy features (icon libraries, theming, brand asset managers). Budget a small, recurring line item rather than a one-off integration cost. Licence stewardship is a separate, legal-owned obligation with its own review cadence.

Support. No vendor, no SLA — community issues only. That is acceptable for a library this narrow but means first-line support must be able to distinguish "we rejected your file" (policy) from "the file was broken" (parsing) — both currently surface as one failure to the customer. Invest in a user-facing error message early; getXmlIssues() gives you the raw material but not the wording.

Scaling. The operation is CPU-bound and single-threaded per call. The 1.0.0 quadratic-time fix materially changes the ceiling for element-dense files, so any capacity model built on pre-1.0 numbers is invalid — re-benchmark. Above roughly tens of SVGs per second, move to queues with a dedicated worker pool, cap file size before sanitising (never sanitise an unbounded stream), reuse the singleton, and cache results keyed by content hash for repeat assets. removeRemoteReferences(true) costs a measurable overhead and should be justified by an SSRF/egress rationale rather than switched on reflexively.

Failure modes. Enumerate them and instrument each: parse failure returning false (must be logged with libxml_get_errors() captured before the call, plus user and path context); the sanitizer throwing or erroring mid-request (wrap in try/catch, fail closed); silent over-stripping that breaks legitimate rendering (detect via render-smoke tests in staging, not in production); and quiet success on a hostile file (detect only via periodic red-team fixtures in CI). The dangerous pattern is a try { } catch { } wrapper added by a well-meaning developer under deadline — make "fail closed, always log" an explicit review checkpoint and a lint-level convention.

Ramp-up. Genuinely low — a competent Laravel developer ships the basic integration in under a day, aided by the README, the live demo (excellent for showing stakeholders exactly what is stripped), and the CLI scanner for local triage. The real ramp is organisational: security must agree the policy, legal must clear the licence, and support must be briefed on the new rejection path. Sequence those conversations in parallel with the spike, not after it, or the technical work will sit finished and undeployable while approvals queue.

Success measures. Rejection rate within an agreed band post-enforcement; zero unreviewed false returns in production logs; p95 sanitisation latency inside the upload budget; allowlist changes reviewed and versioned; licence ruling recorded in the decision log; and a documented, tested exit path to an alternative implementation.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels