diff --git a/ai/tasks/pending/TASK-137-add-public-scoreboard-link-to-match-detail.md b/ai/tasks/done/TASK-137-add-public-scoreboard-link-to-match-detail.md similarity index 72% rename from ai/tasks/pending/TASK-137-add-public-scoreboard-link-to-match-detail.md rename to ai/tasks/done/TASK-137-add-public-scoreboard-link-to-match-detail.md index d267874..24194f4 100644 --- a/ai/tasks/pending/TASK-137-add-public-scoreboard-link-to-match-detail.md +++ b/ai/tasks/done/TASK-137-add-public-scoreboard-link-to-match-detail.md @@ -1,7 +1,7 @@ --- id: TASK-137 title: Add public scoreboard link to match detail -status: pending +status: done type: frontend team: Frontend Senior supporting_teams: @@ -132,11 +132,31 @@ Then hard refresh with `Ctrl+F5` and verify: ## Outcome -To be completed by AI Platform Run / Codex CLI. +- Updated the historical match detail action label to `Ver en Scoreboard`. +- Tightened the detail-page external action safety check to only show links from trusted public scoreboard origins: + - `https://scoreboard.comunidadhll.es` + - `https://scoreboard.comunidadhll.es:5443` +- Restricted accepted scoreboard paths to `/games` and `/games/...`. +- Left recent match card behavior unchanged. +- Updated the historical UI regression script to expect the new detail-page action label. +- No backend changes were needed because the existing RCON read model already exposes `match_url` when safe correlation exists. ## Validation Result -To be completed by AI Platform Run / Codex CLI. +- PASS: `node --check frontend/assets/js/historico-partida.js` +- PASS: `node --check frontend/assets/js/historico.js` +- PASS: `node --check frontend/assets/js/historico-recent-live.js` +- PASS: `python -m compileall backend/app` +- PASS after updating the expected label: `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1` +- PASS: `powershell -ExecutionPolicy Bypass -File scripts/run-rcon-data-pipeline-tests.ps1` + - Note: the script completed successfully but emitted existing `ResourceWarning` messages from backend unittest sqlite connections. +- PASS: `docker compose up -d --build backend frontend` +- PASS: `Invoke-WebRequest "http://localhost:8000/health" | Select-Object -ExpandProperty Content` +- PASS: `Invoke-WebRequest "http://localhost:8000/api/historical/recent-matches?server=all-servers&limit=10" | Select-Object -ExpandProperty Content` +- Additional endpoint check: the known Carentan match detail returns `match_url: null`, so no external action should render for that match. +- Manual/rendered verification: Browser plugin was listed, but the required Node runtime tool was not exposed in this session; used local Chrome headless fallback. + - Verified rendered DOM for the known Carentan match does not include `Ver en Scoreboard` or `historical-match-card__link` when `match_url` is absent. + - Verified rendered DOM still contains `3 : 2`, `Ganador: Aliados`, `Carentan`, `1 h 30 min`, `AntonioPruna` and `M1 GARAND`. ## Change Budget diff --git a/frontend/assets/js/historico-partida.js b/frontend/assets/js/historico-partida.js index 5022c5f..fa9aa21 100644 --- a/frontend/assets/js/historico-partida.js +++ b/frontend/assets/js/historico-partida.js @@ -234,7 +234,7 @@ function renderPlayerRow(player) { } function renderActions(item, actionsNode) { - const matchUrl = normalizeExternalMatchUrl(item.match_url); + const matchUrl = normalizeSafePublicScoreboardMatchUrl(item.match_url); if (!matchUrl) { actionsNode.innerHTML = ""; actionsNode.hidden = true; @@ -247,7 +247,7 @@ function renderActions(item, actionsNode) { target="_blank" rel="noopener noreferrer" > - Abrir en scoreboard + Ver en Scoreboard `; actionsNode.hidden = false; @@ -470,13 +470,18 @@ function formatMatchTimestamp(item, kind) { return "No disponible"; } -function normalizeExternalMatchUrl(value) { +function normalizeSafePublicScoreboardMatchUrl(value) { if (typeof value !== "string" || !value.trim()) { return ""; } try { const url = new URL(value.trim()); - return ["http:", "https:"].includes(url.protocol) ? url.href : ""; + const allowedOrigins = new Set([ + "https://scoreboard.comunidadhll.es", + "https://scoreboard.comunidadhll.es:5443", + ]); + const isAllowedPath = url.pathname === "/games" || url.pathname.startsWith("/games/"); + return allowedOrigins.has(url.origin) && isAllowedPath ? url.href : ""; } catch (error) { return ""; } diff --git a/scripts/run-historical-ui-regression-tests.ps1 b/scripts/run-historical-ui-regression-tests.ps1 index f4eb42a..da27132 100644 --- a/scripts/run-historical-ui-regression-tests.ps1 +++ b/scripts/run-historical-ui-regression-tests.ps1 @@ -59,7 +59,7 @@ Assert-Contains $historicoJs "Ver detalles" ` "Recent match cards no longer include the internal detail fallback label." Assert-NotContains $historicoJs "item.match_url || item.source_url" ` "Recent match cards must not trust legacy source_url fallback." -Assert-Contains $historicoPartidaJs "Abrir en scoreboard" ` +Assert-Contains $historicoPartidaJs "Ver en Scoreboard" ` "Match detail page no longer includes the external scoreboard action label." Assert-Contains $historicoPartidaJs 'rel="noopener noreferrer"' ` "External scoreboard links must keep rel noopener noreferrer."