From f8aa10f91b4be19045cefe1d41da4c0e99c708a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=B0=BD=EC=9A=B1?= Date: Sat, 20 Jun 2026 18:44:20 +0900 Subject: [PATCH] fix: frontend URL scheme allowlist, fetch ok-check, image onerror Add safeUrl() to gate external search-result URLs into href/src (blocks javascript:/data:), parse the response body before the ok check in apiJson so non-JSON error bodies surface the real status, and hide broken evidence preview images via onerror. --- web/operator-gui/app.js | 52 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/web/operator-gui/app.js b/web/operator-gui/app.js index c835418..c4bef45 100644 --- a/web/operator-gui/app.js +++ b/web/operator-gui/app.js @@ -123,11 +123,29 @@ async function apiJson(path, options = {}) { }); - const payload = await response.json(); + const text = await response.text(); + + let payload = null; + + if (text) { + + try { + + payload = JSON.parse(text); + + } catch (error) { + + payload = null; + + } + + } if (!response.ok) { - throw new Error(payload.error || `API 요청 실패: ${response.status}`); + const message = (payload && payload.error) || `API 요청 실패: ${response.status}`; + + throw new Error(message); } @@ -523,6 +541,28 @@ function escapeHtml(value) { +function safeUrl(value) { + + // Defense-in-depth for URLs derived from external search results: only allow + + // absolute http(s) or same-origin paths into href/src (blocks javascript:, + + // data:, etc.). The server also normalizes these server-side. + + const url = String(value || "").trim(); + + if (/^https?:\/\//i.test(url) || url.startsWith("/")) { + + return url; + + } + + return "#"; + +} + + + function formatReason(reason) { const text = String(reason || ""); if (!text) return ""; @@ -1719,9 +1759,9 @@ function renderEvidencePreview(evidence) { return ` - + - + @@ -1741,7 +1781,7 @@ function renderEvidenceLink(evidence) { return ` - + ${escapeHtml(label)} @@ -1937,7 +1977,7 @@ function renderCollectionCandidates() { ${ candidate.sourceUrl - ? `출처 열기` + ? `출처 열기` : "" }