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 useshash_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.