graham-campbell/security
A Laravel package from Graham Campbell that helps harden your app by adding configurable security headers to HTTP responses. It provides a simple middleware-based setup for common browser protections, making it easy to improve response security with minimal configuration.
Build vs. buy for output sanitisation. This package is the "buy" option for a specific, narrow job: cleaning untrusted HTML that must render as HTML. It does not replace Blade's {{ }} escaping, which already covers plain-text output. The decision it informs is whether the team writes its own sanitiser wrapper or adopts this one rather than a heavier alternative. Framed correctly, it is a ~2–4 engineer-day build-vs-buy call, not a security strategy.
Whether rich-text / UGC can ship at all. Comment bodies, bios, WYSIWYG fields, markdown previews, imported content — any surface where {!! !!} is unavoidable needs a sanitiser. If it is on the roadmap, this package is a candidate prerequisite; if UGC is not near-term, the decision costs nothing to defer.
Risk appetite on the XSS critical path. The honest trade-off: a thin wrapper (composite score 0.3, zero listed dependents, single maintainer) versus mews/purifier (HTMLPurifier) or symfony/html-sanitizer — both more widely adopted. That is a genuine product-risk judgement about dependency concentration on a security-sensitive boundary, not a technical nicety. Note the evidence caveat: zero stars/dependents here reads more like sparse telemetry than a bad package; the maintainer is a long-standing Laravel ecosystem contributor. Use a spike, not adoption metrics, to decide.
Consistency vs. bespoke code. A facade plus config gives one reviewable choke point for untrusted HTML — an easier story for security reviews and code-review checklists than "each team remembers to escape." The value is consistency and reduced bespoke code, not immunity.
Roadmap sequencing against framework upgrades. v11.2.0 adds Laravel 11 support and nothing else. If the app is on or moving to Laravel 11, this is a "still maintained" signal; if Laravel 12 is the target, it is a lagging one. Fold adoption in after a framework upgrade lands — never as the critical path.
Read-time vs. write-time sanitisation policy. This is a product decision with user-visible consequences: write-time sanitisation stores lossy, irreversible content; read-time lets policy change later. Default recommendation: sanitise on write into a separate cleaned column, render escaped.
Consider adopting or keeping it when:
Look elsewhere, or build deliberately, when:
mews/purifier / symfony/html-sanitizer before standardising.composer.json constraints lock you out — check whether the 11.x line narrows to ^11.0 and drops Laravel 9/10, at adoption time rather than at upgrade time.Open questions to close before committing: exact php and illuminate/* constraints; whether v11.2.0 merely widens Composer constraints or actually runs against Laravel 11 in CI; which upstream sanitiser is wrapped and whether it is actively patched for bypass CVEs; whether clean is allowlist-based and per-field configurable or a global fixed policy; whether escape differs semantically from Laravel's e() (if not, that half of the API is redundant); and whether a security policy exists.
For executives: "This is a small, maintained package that centralises escaping of untrusted content — the class of bug that produces XSS incidents. Its latest release confirms it works on the current in-line Laravel version, so it is not a blocker for our platform upgrade. The trade-off to be aware of: it is a low-adoption, single-maintainer component. Putting it on a critical path means accepting some vendor-concentration risk in exchange for not building and maintaining our own sanitiser. If we are not shipping rich-text user content soon, we can defer the decision at no cost — and if we are, the decision is whether to adopt this lightweight wrapper or standardise on a more widely adopted sanitiser."
For engineering: "Treat v11.2.0 as a compatibility signal, not a feature release — it adds Laravel 11 support and nothing else. If we adopt: (1) confirm PHP 8.2+ and Laravel 11; (2) check the version constraint covers whichever framework line we're on; (3) prove Composer resolves cleanly against our other Laravel packages; (4) confirm it works through Laravel 11's slim skeleton (bootstrap/app.php, auto-discovery). Then run a 1–2 day spike on one real field — does it sanitise as advertised, what happens to legitimate markup (links, tables, emoji, non-Latin/RTL text), does it break existing content. Wire it into one low-risk code path first, behind a single internal helper, with a diff-based content-regression check. Never let the facade be called ad hoc across the codebase. Regression-test our own usage; the one-line changelog is not evidence of a full test matrix."
Framing to avoid: do not position this as a security win by itself. Its value is consistency and reduced bespoke code for one boundary — escaping untrusted output — and its cost is a low-adoption dependency on a sensitive path. The honest pitch is "a small, low-risk utility with a known maintenance signal," not "an XSS solution." Success measures: a completed spike with a documented verdict, one sanitised surface with no content-loss regressions, one helper as the sole call site, and a CSP that does not assume sanitisation succeeded. Budget a short internal write-up on escaping ≠ sanitisation — without it the package gets used inconsistently and the security benefit is illusory.
How can I help you explore Laravel packages today?