Skip to main content

Security

Log Lens is local-first and single-user. Treat a running instance as equivalent to shell access to the logs it can reach. This page is the security policy; to report something, see Reporting a vulnerability.

Trust model

  • Bind to loopback, or put it behind an authenticating proxy/VPN. Never expose the port directly to a network.
  • A local-directory connector can read any directory the PHP process can reach, and its files become readable through the API. This is intentional for a single-user tool - understand it before exposing the app to anyone else.

API key (standalone)

Authentication is optional. Set LOG_LENS_TOKEN (or auth.token in config) and every API request must present it:

X-Log-Lens-Token: <token>
# or
Authorization: Bearer <token>
  • Empty token to unauthenticated (the local-first default).
  • Wrong/missing token to 401. Comparison uses hash_equals (constant-time).
  • The dashboard prompts for the key once and stores it in the browser (localStorage); a header key button lets you change or clear it.

The key is the server's secret; each client (browser, curl, the AI skills) presents its own copy. The UI shell is served unauthenticated (it's just static markup) - only the data API is gated.

Cross-origin guard (CSRF)

State-changing requests (POST/PUT/PATCH/DELETE) carrying a browser Origin header are rejected with 403 unless the origin matches the request's own origin. Browsers can't forge or suppress Origin, so this blocks cross-site request forgery and DNS-rebinding from other pages. Non-browser clients send no Origin and are unaffected.

Comparison is on host and port - a page on localhost:3000 cannot drive a Log Lens on localhost:8080, which matters on a development machine where every site shares the localhost hostname. The scheme is deliberately not compared, so a TLS-terminating proxy that doesn't set X-Forwarded-Proto still works. A LOG_LENS_URL you have pinned is always an accepted origin. Full detail: cross-origin protection.

Response headers (standalone)

The standalone front controller sends a strict Content-Security-Policy (scripts and XHR from 'self' only), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer, a Permissions-Policy denying every browser capability the dashboard doesn't use, and Cross-Origin-Opener-Policy: same-origin. Turn the set off with security.headers_enabled = false if a proxy sets its own, or replace just the policy with security.content_security_policy.

HSTS is opt-in (security.hsts_max_age, seconds; 0 = off) and is only ever sent over TLS. It is off by default because HSTS is a promise about the whole host, not about Log Lens: once a browser has seen it, every plaintext request to that host - including every other site sharing it, and every subdomain if you add security.hsts_include_subdomains - is refused for the whole max-age, and a browser can't be told to forget early. Turn it on once the entire host is served over HTTPS.

The Laravel embed inherits its host application's response headers instead; none of this applies there.

Outbound requests (SSRF)

An alert channel URL is fetched by the server, so it must be a public address. Log Lens resolves the host and rejects loopback, RFC1918, link-local, and reserved ranges - which also rules out the cloud metadata endpoint at 169.254.169.254. That check runs twice: once when the channel is saved, so the error appears in the form, and again immediately before every delivery attempt, where the validated addresses are pinned into the connection (CURLOPT_RESOLVE). The second check is the one that holds, because DNS can answer differently between saving and delivering. Install ext-curl: the stream fallback can't pin, so a rebinding window remains on that path.

Laravel mode

In the Laravel adapter there is no API key - access is gated by the host app's auth via LogLens::auth(), a viewLogLens gate, or auth middleware, and can be killed entirely with LOG_LENS_ENABLED=false. The default is open in local and denied elsewhere. See Laravel Package.

Data sensitivity

Log messages and context may contain credentials, PII, URLs, SQL, headers, or payloads. Log Lens never transmits log content anywhere; keep it that way - don't pipe API output to third-party services, and redact before pasting excerpts into issues or chat.

Reporting a vulnerability

Open a private security advisory on cliqthemes/log-lens rather than a public issue. That repository is the tracker for both packages.