From 679f91da2c8bd5bbd83a6f787cfa5fef45c232dd Mon Sep 17 00:00:00 2001 From: StyxX65 <150797939+StyxX65@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:14:33 +0200 Subject: [PATCH] Use page origin for share links except when browsing at localhost The LAN-IP rewrite in _getShareBaseUrl() exists to fix unusable 127.0.0.1 links; applying it to every origin meant links copied behind a reverse proxy pointed at http://:5100, bypassing TLS. HTTPS and non-localhost origins are now used as-is. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 4 ++++ static/js/CLAUDE.md | 2 +- static/js/viewer.js | 11 ++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87a69d9..118cc90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ Version numbers follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html ## [Unreleased] +### Fixed + +- **Share links now respect a reverse proxy** — `_getShareBaseUrl()` rewrote every copied share link to `http://:5100` (via `/api/local_ip`), which would bypass TLS when the scanner sits behind a reverse proxy (Zoraxy, Caddy, nginx, …): a DPO opening the link would silently fall back to plain HTTP. The LAN-IP rewrite now only applies in the case it was built for — browsing the app at `localhost` over HTTP, where `window.location.origin` would produce links unusable from other machines. Any HTTPS or non-localhost origin is used as-is. + --- ## [1.7.2] — 2026-06-10 diff --git a/static/js/CLAUDE.md b/static/js/CLAUDE.md index 61802b2..2a84ba5 100644 --- a/static/js/CLAUDE.md +++ b/static/js/CLAUDE.md @@ -65,7 +65,7 @@ Never revert to `!!window._googleConnected` / `_fileSources.length > 0` — thos - **`window.VIEWER_MODE`** — injected by Jinja2. `auth.js` adds `viewer-mode` class to ``; all hide rules are CSS (`body.viewer-mode …`) except `delBtn` which is also guarded in JS. - **`window.VIEWER_SCOPE`** — injected alongside `VIEWER_MODE`. If `VIEWER_SCOPE.role` is set, `auth.js` pre-sets `#filterRole` and hides the dropdown. - **Token onclick attributes** — Copy/Revoke buttons pass the token as a single-quoted JS string literal, never via `JSON.stringify` (which produces double-quoted strings that break `onclick="…"` attributes). -- **Share link base URL** — `_getShareBaseUrl()` fetches `/api/local_ip` (LAN IP via UDP probe to `8.8.8.8`) so copied links are routable from other machines. Both `createShareLink` and `copyTokenLink` are `async`. Do not revert to `window.location.origin` — that produces `127.0.0.1` links. +- **Share link base URL** — `_getShareBaseUrl()` uses `window.location.origin` whenever the page is served over HTTPS or from a non-localhost host (a reverse-proxied hostname or LAN IP is already routable, and rewriting it to `http://` would bypass the proxy's TLS). Only when browsing at `localhost`/`127.0.0.1` over HTTP does it fetch `/api/local_ip` (LAN IP via UDP probe to `8.8.8.8`) so copied links work from other machines. The result is cached in `_shareBaseUrl` so Copy buttons stay within the click gesture. Both `createShareLink` and `copyTokenLink` are `async`. Do not make it return bare `window.location.origin` unconditionally — that reintroduces unusable `127.0.0.1` links. - **Settings Security pane** — Admin PIN and Viewer PIN groups live in `stPaneSecurity`. `switchSettingsTab('security')` triggers both `stLoadPinStatus()` and `stLoadViewerPinStatus()`. ## Gotchas diff --git a/static/js/viewer.js b/static/js/viewer.js index 7de5ecf..1cd9f9e 100644 --- a/static/js/viewer.js +++ b/static/js/viewer.js @@ -5,8 +5,17 @@ import { S } from './state.js'; let _shareBaseUrl = null; // cached so Copy buttons can build the URL synchronously async function _getShareBaseUrl() { - // Use the machine's LAN IP so links work for remote users, not just localhost. if (_shareBaseUrl) return _shareBaseUrl; + // The LAN-IP probe exists only to fix links when the operator browses the + // app at localhost — those would be unusable for remote users. Any other + // origin (LAN IP, or a reverse-proxied HTTPS hostname) is already routable, + // and rewriting it to http:// would bypass the proxy's TLS. + const host = window.location.hostname; + if (window.location.protocol === 'https:' || + (host !== 'localhost' && host !== '127.0.0.1' && host !== '[::1]')) { + _shareBaseUrl = window.location.origin; + return _shareBaseUrl; + } try { const r = await fetch('/api/local_ip'); if (r.ok) {