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.
Whether you can ship user-generated content (UGC) this quarter without a bespoke security workstream. The concrete decision on the table is "can we launch comments, reviews, forum posts, support tickets, profile bios, or CMS/WYSIWYG fields?" This package gives you a thin, maintained sanitization layer over voku/anti-xss so the team isn't hand-rolling regex XSS filtering — a historically reliable way to ship a vulnerability. It turns a multi-sprint security question into a dependency decision.
Build vs. buy, priced honestly. security-core is deliberately small: clean(), isClean(), and a handful of setters forwarding to the underlying sanitizer. It saves integration hours, not weeks. The real comparison is "call voku/anti-xss directly" versus "adopt this wrapper (plus the Laravel sibling)". If your stack is Laravel, the answer is the sibling package — you get the service provider, facade, and config, and this arrives transitively.
Security remediation roadmap and audit closure. It's a concrete, low-effort control to close a recurring class of pen-test and audit findings ("untrusted HTML is sanitized before render") and to document input-handling controls for SOC 2 / ISO 27001 evidence. That can be enough to deprioritize a larger templating or content-model rewrite.
Defense-in-depth investment, framed correctly. It complements — never replaces — Blade auto-escaping ({{ }}), contextual output encoding, and CSP. Keeping that framing in roadmap conversations prevents anyone treating sanitization as a checkbox that eliminates XSS risk.
Dependency governance and platform-version planning. The current major (v5.0.0) drops PHP 8.0 support, which forces the "are we still running PHP 8.0 anywhere?" question across services, Docker base images, and CI matrices. PHP 8.1+ becomes a prerequisite for the current line; sequence that runtime upgrade (or deliberately pin to 4.x with an owner and review date) as part of the plan.
Risk posture for hygiene dependencies. It sets the pattern for how much single-maintainer, low-adoption risk you'll absorb for a non-strategic control: pin the version, wrap it behind an internal interface, own the swap. This is a small bet, not a platform commitment — and the major-version cadence just demonstrated why pinning matters.
Consider it when:
graham-campbell/security and get this transitively. security-core is the framework-agnostic engine and is almost never the right direct dependency for a Laravel app — that's only for manual wiring, non-Laravel PHP, or a deliberately minimal integration surface.Look elsewhere when:
4.x only as a documented, owned, dated pin — or pick a dependency whose support window matches your runtime.For executives: This is a small, low-cost hygiene layer that reduces our exposure to cross-site scripting anywhere customers submit content. It is not a silver bullet — it sits alongside our templating safeguards and browser security policy — but it's cheap insurance that closes a recurring audit finding. Lock-in is negligible: the wrapper is thin, the underlying library replaceable, and switching cost is measured in hours, not quarters. Two caveats to track. First, dependency health: it's a single-maintainer package with little adoption, so we pin versions and keep an internal fallback. Second, the latest major dropped PHP 8.0 support, so adopting it commits those services to PHP 8.1+ — routine, but a real platform-sequencing task to schedule rather than assume.
For engineering: Thin wrapper around voku/anti-xss; on Laravel, use the graham-campbell/security sibling for the service provider, facade, and config, and pull this transitively. The current major requires PHP 8.1+ — verify the floor in composer.json and your CI matrix before bumping; if any service is still on 8.0, upgrade first or pin the previous major with a dated follow-up. Sanitize at a single trust boundary — ingest or render, pick one and stay consistent, because clean() isn't reliably idempotent and double-cleaning produces inconsistent stored values. Keep output encoding and CSP as primary controls, log input and output around clean() at debug level when legitimate content gets stripped, and build a checked-in corpus of known XSS payloads as your upgrade regression suite. Treat it as a swappable adapter behind a small internal interface (e.g. HtmlSanitizer::sanitize(string $html): string) so a policy-based purifier can replace it as requirements tighten. Pin voku/anti-xss deliberately — its behavior shifts between minor versions.
For security/compliance: Adds a documented input-sanitization control to the input-handling section of our control set, giving us reproducible evidence that untrusted markup is filtered before storage or render. It should be documented as one layer in a defense-in-depth model — alongside auto-escaping templates, contextual output encoding, and CSP — and explicitly not represented as a complete XSS mitigation. Because the underlying mechanism is blacklist-based, we should scope the control statement to "known-dangerous markup is stripped" rather than "all HTML is validated against policy"; the latter requires an allowlist purifier and a separate decision. We maintain the payload corpus and pin the sanitizer version so behavior changes are reviewed, not absorbed silently.
How can I help you explore Laravel packages today?