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.