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

Security Core Laravel Package

graham-campbell/security-core

Core security utilities for Laravel, providing a lightweight foundation for sanitizing user input, escaping output, and defending against XSS. Powers the graham-campbell/security package and can be used standalone in any PHP or Laravel application.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. security-core is a thin adapter over voku/anti-xss, positioned at exactly one place in our architecture: the trust boundary where untrusted HTML is either accepted at ingest or rendered as raw markup. It is deliberately not a platform component — the entire public surface is clean(), isClean(), a constructor config array (evil), and a handful of setters forwarding to the underlying sanitizer. Architecturally it should sit behind an internal HtmlSanitizer interface so it reads as a swappable hygiene control rather than a dependency the product is built on. That framing matters more than the code: it determines whether we can replace it in hours or weeks when requirements tighten.

Integration feasibility. Low effort. Composer install, one container binding, and a FormRequest::prepareForValidation() hook gets a working control in a day. The important integration decision is packaging, not code: in a Laravel app we install graham-campbell/security (service provider, facade, config) and consume Core transitively; Core is only a direct dependency for non-Laravel wiring, CLI tools, or package-level reuse. The PHP 8.1 floor introduced in v5.0.0 is the only hard prerequisite and is a platform question, not an engineering one.

Technical risk.

  • Blacklist, not allowlist. anti-xss strips known-bad patterns (script tags, javascript: URIs, event handlers). It will not catch novel obfuscation, and it gives no per-tag/per-attribute policy control. For high-value rich text (CMS bodies, anything editors touch) it is insufficient on its own.
  • clean() is not reliably idempotent. Sanitizing in two places (middleware plus a model mutator) yields path-dependent stored values. This is a design constraint we must document and enforce, not a bug we can wait out.
  • Dependency health. Single maintainer, zero dependents, low community signal, no vendor support channel and no security response process auditors can point to. Majors appear to track PHP runtime support rather than features, which means upgrades are forced by the platform calendar.
  • Transitive fragility. The meaningful risk isn't this package, it's voku/anti-xss: sanitizer behavior shifts between minor versions. Both need deliberate pinning and a re-test gate.
  • Documentation/metadata inconsistency. The repository description claims "shared contracts and result formatters for checking Composer dependencies against vulnerability databases," while the code and usage guidance describe an anti-XSS wrapper. That's a stale or templated description. Treat package metadata as unreliable, validate against the actual source before adopting, and don't let it inflate the perceived scope.

Key questions for the team.

  1. Which specific fields carry untrusted HTML, and is each one a low-value string or an editor-grade rich-text surface? (The answer splits the estate between this package and a policy purifier.)
  2. Do we sanitize at ingest or at render? Pick one per field, document it, and enforce it in review.
  3. Is any service or CI matrix entry still on PHP 8.0? If yes, that's a sequenced prerequisite, not a blocker to defer silently.
  4. Does any audit or contractual control require an allowlist sanitizer or an attested dependency? If so, this package is the wrong answer regardless of cost.
  5. Who owns the version pin, the fallback plan, and the payload regression corpus?
  6. Do we require evidence of a maintained security response process? This dependency doesn't provide one.

Verdict. Adopt as a cheap, contained control for generic untrusted-HTML cleanup — behind an interface, at a single boundary, with pinned versions. Do not adopt it as the sanitization strategy for rich-text content.

Integration Approach

Stack fit. Laravel 9/10/11 on PHP 8.1+ is the primary target, consumed via graham-campbell/security. The framework-agnostic core also serves console commands, importers, and queue workers, which is where a non-HTTP sanitization need usually surfaces first. Everything renders through Blade {{ }} and a strict CSP as primary controls — this package is a complement, never a substitute, and it does not do contextual encoding for attribute, URL, JS, or CSS contexts.

Migration path. There is usually nothing to migrate from except hand-rolled regex filtering, which is a net improvement wherever it exists. The realistic path:

  1. Introduce an internal HtmlSanitizer interface with a sanitize(string $html): string contract.
  2. Implement a SecurityCoreSanitizer adapter that delegates to Security::clean(). Nothing else in the codebase references the vendor type.
  3. Register a singleton binding driven from config, keyed per field group (e.g. security.comments.evil vs security.pages.evil) so operations can tune the evil list without a deploy.
  4. Convert one field, ship it, then widen. Field-by-field conversion keeps blast radius small and makes "it ate my content" incidents attributable.

Compatibility. Pin graham-campbell/security-core to ^5.0 and pin voku/anti-xss explicitly rather than floating it under the wrapper. Confirm the Laravel sibling (graham-campbell/security) has caught up to the same PHP constraint before bumping either — a mismatch here is the most likely source of a broken composer update. Run composer why-not php 8.1 before any major bump. If a service must stay on PHP 8.0, pin ^4.0 with a named owner and a review date; a silent pin is worse than a deliberate one.

Sequencing.

  1. Platform gate — confirm PHP 8.1+ across runtime images and CI matrix. This is the long pole; start it first even if it's someone else's backlog.
  2. Interface + adapter — hours of work, and it caps the downside of the entire decision.
  3. Boundary decision — pick ingest or render per field, write it down. Enforce in code review from day one.
  4. Regression corpus — check in a set of known XSS payloads and a unit test that runs the real instance against it. This is the gate for every future upgrade of either package.
  5. Feature-test fake — bind a fake in feature tests and assert clean() is invoked, so controllers stay decoupled.
  6. Observability — debug-level logging of input/output around clean(), so over-stripping is diagnosable in minutes.
  7. Escalation path — for editor-grade rich text, add a policy purifier (HTML Purifier or Symfony HtmlSanitizer) as a second stage. The interface from step 2 makes this additive, not a rework.

Operational Impact

Maintenance. Low but non-zero, and it recurs. Two packages to track (security-core and voku/anti-xss), with upgrades gated on re-running the payload corpus. Behavior changes arrive on minor versions of the underlying sanitizer, so automated dependency bumps must not auto-merge. Budget a small recurring maintenance line rather than treating this as install-and-forget. The single-maintainer profile means we should assume a future where the package goes quiet and we either fork the thin wrapper or swap the adapter — both are hours of work only if the interface exists.

Support. No vendor. No SLA. No security response process we can cite to an auditor. Community issues only. This means internal ownership is mandatory: a named owner for the pin, the corpus, and the fallback plan, recorded in the dependency register. Auditors should be shown the interface, the corpus, and the documented boundary decision — not the package name.

Scaling. Computationally cheap relative to request cost; regex passes over submitted HTML are not a throughput concern at typical UGC volumes. Two scaling caveats: (1) for bulk imports and queue jobs, avoid per-row instantiation and avoid sanitizing fields that never render as HTML; (2) large rich-text payloads multiply the cost, which is another reason to scope sanitization to fields that actually need it. Applying it broadly adds cost and, worse, manufactures a false sense of a defended boundary. On the product side, "can we ship comments/reviews/forum/tickets this quarter?" becomes a yes rather than a bespoke security project — that's the real scaling win.

Failure modes to plan for.

  • Over-stripping legitimate content — the most likely day-to-day incident. Symptom: users report missing formatting. Cause is nearly always a tag present on both the allow-list and the evil list. Mitigation: debug-level input/output logging and bisecting the evil array; per-field config so one tuning change doesn't affect every surface.
  • Double-cleaning divergence — two sanitization points producing different stored values for the same input. Mitigation: single documented boundary, enforced in review.
  • False confidence — stakeholders reading "XSS protection" as complete. Mitigation: narrative discipline from the start — one layer alongside templating and CSP, not a checkbox.
  • Bypass via encoding — double-encoded entities and mixed charsets slipping past regexes. Mitigation: don't pre-decode entities before cleaning; escalate sensitive surfaces to a policy purifier.
  • Constrained upgrade — a major bump propagating a PHP floor requirement through the graph and breaking a service or CI job mid-sprint. Mitigation: platform gate first, pins explicit, composer why-not as a pre-merge check.
  • Abandonment — low-severity given the thin surface, but only if the swap path was built on day one.

Ramp-up. Half a day for a mid-level engineer to be productive: the constructor shape, clean()/isClean(), and the evil config array are the whole learning curve. The non-obvious parts are the ones that bite in production — idempotency, the blacklist (not allowlist) semantics, and the two-package split between Core and the Laravel wrapper. The genuine investment is the payload corpus and the boundary documentation, which is where a TPM should place the acceptance criteria rather than on the install itself.

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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle