Skip to content

Security

Ghostwire runs under a strict CSP — no inline <script>, no eval, no new Function. The compiled resources/dist/ghostwire.js bundle never uses either. The @ghostwireStyles/@ghostwireScripts Blade directives both accept an optional nonce argument for apps that thread a per-request CSP nonce through their asset pipeline.

Verified in CI: the package’s own test gallery runs under the strictest CSP each Livewire line actually supports — no 'unsafe-eval' at all on Livewire 4 with csp_safe enabled, and 'unsafe-eval' granted only on Livewire 3, whose Alpine can’t evaluate wire:* expressions without it — not just a documented claim.

Livewire 4 offers a CSP-safe Alpine build, but it’s opt-in: set 'csp_safe' => true in your config/livewire.php (it defaults to false). With it enabled, your policy needs no 'unsafe-eval'. Left at the default, a Livewire 4 app’s policy still needs 'unsafe-eval' — same as Livewire 3, where that same flag has no effect and the violation is still recorded regardless. Either way, this is a Livewire/Alpine requirement, not Ghostwire’s: Ghostwire’s own bundle needs no eval on either line.

  • Dependency auditingcomposer audit runs in CI on every PR (.github/workflows/security.yml)
  • Reproducible dist/ build — the committed resources/dist/ghostwire.js/.css are verified in CI to match a fresh js/build.mjs (esbuild) run from source, so what ships is provably what’s in the repo, not something hand-edited post-build
  • Server→client transport — the data-ghost payload (compact keys m/o/x/d/h/r/p/s/l/g/n) is schema-validated on the client: an unknown key discards the whole payload rather than partially trusting it, and a malformed value falls back to the package default for that field, never to an unvalidated pass-through
  • Learning store — validated on every read, capped at a fixed 256 KB with least-recently-used eviction, and holds structural bone-tree shape only — never textual DOM content
  • ghost:export — path-traversal-hardened: component names are charset-validated, the destination is resolved canonically and refused outside resources/views, and a symlinked or directory target is refused regardless of --force. See Learning for the full flow.

No data from the user’s DOM ever leaves the browser. Learning, when enabled (GHOSTWIRE_LEARNING env var, off by default) and refused server-side in production regardless of that setting, is entirely local — nothing is ever transmitted. Zero telemetry, full stop.

See SECURITY.md in the repository. Response target: under 72 hours (GOV-04).