Skip to content

Security: vaishnavigunti/litmus

Security

SECURITY.md

Security

Litmus drives a real headless browser against user-submitted URLs and persists data per user, so it's built with a few specific threats in mind. This document describes the protections in place and how to report issues.

Reporting a vulnerability

Please don't open a public issue for security problems. Email the maintainer instead and allow reasonable time for a fix before disclosure.

Threat model & protections

SSRF (server-side request forgery) — the primary risk

The server fetches whatever URL a user submits (Playwright navigation + the broken-link checker). Without guards, an attacker could point it at internal services or the cloud metadata endpoint (169.254.169.254 → IAM credentials).

  • Every submitted URL passes assertPublicUrl before the browser launches: it rejects non-http(s) schemes, blocks localhost/*.local/*.internal/metadata hostnames, resolves the hostname and rejects any private/loopback/link-local/reserved IP (IPv4, IPv6, and IPv4-mapped).
  • The broken-link checker skips any link pointing at a private host.
  • For local development you can opt out with LITMUS_ALLOW_PRIVATE_HOSTS=true.

Secrets

  • The Supabase service-role key is server-only (SUPABASE_SERVICE_ROLE_KEY, never NEXT_PUBLIC_). The public anon/publishable key is safe in the browser only because RLS is enforced.
  • No secrets are committed — .env.local is gitignored; only .env.example (placeholders) is tracked.
  • Errors are sanitized before reaching clients; raw stack traces and internal details are logged server-side only.

Authorization (RLS)

Row Level Security is enabled on every user-owned table (profiles, runs, shared_reports). A user cannot read or modify another user's data. Public shared reports are readable by id; the view-count bump uses a security definer RPC so anonymous viewers can't write to the table.

Other measures

  • Rate limiting on the test (/api/test), crawl (/api/crawl), and share (/api/share) endpoints (in-memory; a shared store is the multi-instance scaling step).
  • Open-redirect protection on the OAuth callback — next is resolved to a same-origin relative path.
  • Path-traversal-safe local video route — the id is stripped to [a-z0-9-].
  • No XSS sinks — all tested-page content is rendered through React (escaped); there is no dangerouslySetInnerHTML. The tested page's HTML is only inspected, never executed as the app's own markup.
  • Security headers on every response (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Strict-Transport-Security, Permissions-Policy); X-Powered-By is disabled and the unused Next image optimizer is turned off.
  • Auth is handled by Supabase with secure HttpOnly cookies via @supabase/ssr; no hand-rolled JWTs.

Known dependency advisories

npm audit reports issues in dev-only / transitive dependencies whose only fixes are breaking major upgrades:

  • esbuild/vite/vitest — affects the test dev server only, not production.
  • glob via eslint-config-next — the glob CLI command-injection; Litmus never invokes the glob CLI.
  • next — image-optimizer DoS (mitigated here by disabling the optimizer) and an RSC DoS; keep Next updated on a maintained release for production.

These are tracked and revisited as non-breaking fixes become available.

There aren't any published security advisories