Harden match detail scoreboard link
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
</a>
|
||||
`;
|
||||
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 "";
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
|
||||
Reference in New Issue
Block a user