FitLens is a local-first tool. It fetches attacker-controlled URLs and sends their contents to a language model, so its security posture is documented here in full rather than summarized.
FitLens has not cut a stable release. Only the current main branch receives
security fixes.
Report privately through GitHub's private vulnerability reporting on this repository. Please do not open a public issue for an unfixed vulnerability.
Include the affected component, reproduction steps, and what an attacker gains. Expect an initial response within 7 days. This is a personal project maintained in spare time; there is no bug bounty and no guaranteed remediation timeline.
FitLens is designed to run on localhost, used by the single person who
started it.
Do not expose FitLens to untrusted users or the public internet. The following are deliberate design choices that are safe locally and unsafe when hosted:
- No authentication or authorization. Anyone who can reach the port has full use of the tool and the configured model credentials.
- Rate limits are process-local counters, not a distributed limiter. They bound accidental load, not a determined attacker.
- Browser storage is not a secret vault. API keys entered in the UI live in
sessionStorage; reports live in IndexedDB with alocalStoragefallback. This is appropriate for a single-user local tool only. - HSTS is intentionally absent, because local use is over HTTP.
Binding the server to a non-loopback interface, or placing it behind a proxy, changes the trust model and is unsupported without real authentication and network policy.
Fetching user-supplied URLs is the tool's core function, so lib/source.ts
treats every submitted URL and every redirect target as hostile:
- Only HTTP and HTTPS URLs without embedded credentials are accepted.
- DNS is resolved before each request. Any private, loopback, link-local, reserved, multicast, or unspecified IPv4/IPv6 answer rejects the host.
- The validated address set is immutable and is supplied directly to the HTTP connection lookup, so the socket connects to the addresses that policy approved. This closes the DNS-rebinding (TOCTOU) window between policy evaluation and socket creation.
- Redirects are manual, capped at five hops, and each hop repeats the full process with a fresh destination-specific dispatcher.
Authorizationis stripped on cross-origin redirects.- Content types are allowlisted per route.
- Streamed bytes are capped by actual count;
Content-Lengthis not trusted.
Opt-in browser rendering gives Chromium the initial HTML offline and re-fetches external scripts, styles, and data through the same guarded Node transport. Media, fonts, WebSockets, service workers, non-GET requests, and bounded-resource overages are blocked.
Defense in depth: if you run FitLens anywhere other than a trusted workstation, enforce an outbound firewall or egress proxy as well. Application -level SSRF controls are a mitigation, not a substitute for network policy.
Collected pages are attacker-controlled text handed to a model, so injection is treated as expected input rather than an edge case:
- Source material is serialized only under
UNTRUSTED_SOURCE_DATA, structurally separate fromTRUSTED_USER_REQUIREMENTS. - Provider instructions forbid those values from changing rules, scoring, candidate order, or the response schema.
- Adversarial tests keep embedded page commands out of the instruction channel.
- Structured output and cross-field validation enforce the contract after generation.
Known limitation: prompt isolation reduces injection risk but cannot guarantee that every model ignores every adversarial source. Treat FitLens output as research to review, not as a decision to execute. The model never receives browser history, saved notes, trial results, or other reports.
- Provider keys are read from the environment or entered per session in the UI. They are never written to a report, an export, or a run manifest.
- Remote provider base URLs require HTTPS. Unauthenticated HTTP is permitted only on loopback.
- Provider errors map to stable public codes; upstream bodies, stack traces, and secret-bearing messages are not retained.
fitlens doctorredacts diagnostics before printing.- Share-safe exports strip private context, notes, trials, revisions, and manual evidence.
FITLENS_DISABLE_LIVE_ANALYSIS=1 is a fail-closed switch: the request is
schema-validated, then returns the missing-credentials boundary before any
source collection or provider activity.
In scope: SSRF and URL/DNS/redirect policy bypass, prompt injection that escapes the untrusted-data channel, credential leakage into reports, exports, manifests, logs, or diagnostics, and sandbox escapes in guarded rendering.
Out of scope: anything that requires the operator to have already exposed FitLens to untrusted users, since that is documented above as unsupported.