fix(web): SRI sur les dépendances CDN et timeout des appels API
Les cinq bibliothèques chargées depuis unpkg et jsDelivr (Leaflet, MarkerCluster, leaflet.heat, noUiSlider, flag-icons) n'avaient aucun contrôle d'intégrité : un CDN compromis exécutait du code arbitraire sur le site public. Les neuf hashes ont été recalculés depuis les URL versionnées. GoatCounter reste sans SRI : son script n'est pas versionné. apiGet() reçoit un timeout : sans lui, une API qui ne répond jamais laissait l'interface et le rechargement du viewport bloqués indéfiniment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
da24d2a8a1
commit
1fe5b4a618
2 changed files with 31 additions and 14 deletions
|
|
@ -173,11 +173,19 @@ const MACRO_GENRES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- API ---
|
// --- API ---
|
||||||
async function apiGet(path) {
|
async function apiGet(path, timeoutMs = 15000) {
|
||||||
const url = `${API_BASE}${path}`;
|
const url = `${API_BASE}${path}`;
|
||||||
const r = await fetch(url, { mode: "cors" });
|
// Timeout explicite : sans lui, une API qui ne répond jamais laisse l'UI (et
|
||||||
|
// le rechargement du viewport de la carte) bloquée indéfiniment.
|
||||||
|
const ctrl = new AbortController();
|
||||||
|
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
||||||
|
try {
|
||||||
|
const r = await fetch(url, { mode: "cors", signal: ctrl.signal });
|
||||||
if (!r.ok) throw new Error(`API ${path} failed: ${r.status}`);
|
if (!r.ok) throw new Error(`API ${path} failed: ${r.status}`);
|
||||||
return await r.json();
|
return await r.json();
|
||||||
|
} finally {
|
||||||
|
clearTimeout(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadStats() {
|
async function loadStats() {
|
||||||
|
|
|
||||||
|
|
@ -11,26 +11,35 @@
|
||||||
<link href="https://fonts.googleapis.com/css2?family=UnifrakturCook:wght@700&family=Inter:wght@300;400;600;700;900&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=UnifrakturCook:wght@700&family=Inter:wght@300;400;600;700;900&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Leaflet -->
|
<!-- Leaflet -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
integrity="sha384-sHL9NAb7lN7rfvG5lfHpm643Xkcjzp4jFvuavGOndn6pjVqS6ny56CAt3nsEVT4H" crossorigin="anonymous" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||||
|
integrity="sha384-cxOPjt7s7Iz04uaHJceBmS+qpjv2JkIHNVcuOrM+YHwZOmJGBXI00mdUXEq65HTH" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- MarkerCluster -->
|
<!-- MarkerCluster -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css"/>
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css"
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css"/>
|
integrity="sha384-pmjIAcz2bAn0xukfxADbZIb3t8oRT9Sv0rvO+BR5Csr6Dhqq+nZs59P0pPKQJkEV" crossorigin="anonymous"/>
|
||||||
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
|
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css"
|
||||||
|
integrity="sha384-wgw+aLYNQ7dlhK47ZPK7FRACiq7ROZwgFNg0m04avm4CaXS+Z9Y7nMu8yNjBKYC+" crossorigin="anonymous"/>
|
||||||
|
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"
|
||||||
|
integrity="sha384-eXVCORTRlv4FUUgS/xmOyr66XBVraen8ATNLMESp92FKXLAMiKkerixTiBvXriZr" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- Heatmap -->
|
<!-- Heatmap -->
|
||||||
<script src="https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js"></script>
|
<script src="https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js"
|
||||||
|
integrity="sha384-mFKkGiGvT5vo1fEyGCD3hshDdKmW3wzXW/x+fWriYJArD0R3gawT6lMvLboM22c0" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- noUiSlider (double slider timeline) -->
|
<!-- noUiSlider (double slider timeline) -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/nouislider@15.7.1/dist/nouislider.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/nouislider@15.7.1/dist/nouislider.min.css"
|
||||||
<script src="https://unpkg.com/nouislider@15.7.1/dist/nouislider.min.js"></script>
|
integrity="sha384-PSZaVsyG9jDu8hFaSJev5s/9poIJlX7cuxSGdqCgXRHpo2DzIaZAyCd2rG/DJJmV" crossorigin="anonymous">
|
||||||
|
<script src="https://unpkg.com/nouislider@15.7.1/dist/nouislider.min.js"
|
||||||
|
integrity="sha384-/gBUOLHADjY2rp6bHB0IyW9AC28q4OsnirJScje4l1crgYW7Qarx3dH8zcqcUgmy" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
<!-- GoatCounter -->
|
<!-- GoatCounter (script d'analytics non versionné → pas de SRI possible) -->
|
||||||
<script data-goatcounter="https://metalfromeurope.goatcounter.com/count"
|
<script data-goatcounter="https://metalfromeurope.goatcounter.com/count"
|
||||||
async src="//gc.zgo.at/count.js"></script>
|
async src="//gc.zgo.at/count.js"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flag-icons@7.2.3/css/flag-icons.min.css"
|
||||||
|
integrity="sha384-aQuvIWWIbpu/mSqULLDiUveyYiPoJzPKAWjUmGJ+Elm+N/LJhzfZqsutsfw870JS" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="./styles.css" />
|
<link rel="stylesheet" href="./styles.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue