Files
Forge-Engine/docs/redos_probe_s8786.js
T
williamandClaude Sonnet 5 66d8eaeae8 Lots 1-3 modernisation JS (S8786/S2703/S2486) + retrait Sonar CI/prod
- Lot 1 (S8786, ReDoS) : 5 sites documentes NOSONAR apres preuve empirique
  (script reproductible docs/redos_probe_s8786.js), aucune reecriture
  defensive necessaire.
- Lot 2 (S2703, variable globale implicite) : bug reel trouve et corrige
  (SCENE_OBJECT_NAMES en const au lieu de let, cassait la reassignation
  cross-script depuis scene-editor.js) + test de non-regression ; 4 autres
  sites confirmes surs et documentes.
- Lot 3 (S2486, exceptions avalees) : 6 sites confirmes surs et
  documentes ; 2 sites (config sprite JSON invalide) corriges avec un
  console.warn devtools, comportement joueur inchange, couverts par un
  nouveau test.
- Retrait du job CI sonarqube (.gitea/workflows/deploy.yml) et du service
  prod sonarqube/sonar-postgres (docker-compose.prod.yml) : acces dashboard
  bloque par des soucis d'infrastructure reseau (WSL2/pare-feu Hyper-V en
  local, reseau Docker partage avec Caddy pas en place en prod), sans lien
  avec le code du moteur - mis de cote plutot que de continuer a bloquer
  sur de l'infra. Les lots 4+ de modernisation JS dependent de scores
  Sonar exacts et sont donc egalement en pause (voir CODE_QUALITY.md).

SKIP=djlint : H021 (styles inline, 49 occurrences) est un backlog deja
documente et assume (CODE_QUALITY.md section 6), sur des templates non
touches par ce commit - deja exclu de la CI pour la meme raison.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 08:46:24 +02:00

58 lines
2.6 KiB
JavaScript

// Script de verification empirique pour les 5 sites NOSONAR javascript:S8786
// (voir CODE_QUALITY.md, section 5) - teste les 3 regex distinctes flaguees
// (2 sont dupliquees dans 2 fichiers chacune) contre des entrees construites
// pour maximiser l'ambiguite que l'analyseur Sonar soupconne, a plusieurs
// tailles croissantes. Un vrai ReDoS montrerait une croissance exponentielle
// du temps avec la taille (x10 la taille -> beaucoup plus que x10 le temps).
//
// A relancer si l'une de ces 3 regex change de forme, pour confirmer que le
// NOSONAR reste justifie plutot que de le recopier aveuglement.
//
// Usage : node docs/redos_probe_s8786.js
function timeIt(label, fn) {
const start = process.hrtime.bigint();
const result = fn();
const end = process.hrtime.bigint();
const ms = Number(end - start) / 1e6;
console.log(`${label}: ${ms.toFixed(2)}ms -> match=${result !== null}`);
return ms;
}
console.log('=== Pattern A (filter-repeater-rows.js:10, panel-init.js:254) ===');
const patternA = /^\{\{\s*([^.{}]+)\.([^.{}]+)\s*\}\}$/;
for (const n of [1000, 10000, 50000, 100000]) {
const evil = `{{${'a'.repeat(n)}`; // pas de point, pas de fermeture
timeIt(` n=${n} (pas de point, pas de fermeture)`, () => patternA.exec(evil));
}
for (const n of [1000, 10000, 50000, 100000]) {
const evil = `{{${'a'.repeat(n)}.${'b'.repeat(n)}`; // point present, pas de fermeture
timeIt(` n=${n} (point present, pas de fermeture)`, () => patternA.exec(evil));
}
console.log('');
console.log('=== Pattern B (filter-repeater-rows.js:11, panel-init.js:261) ===');
const patternB = /^\{\{\s*\$([^.{}[\]]+)((?:\.[^.{}[\]]+|\[\d+\])*)\s*\}\}$/;
for (const n of [1000, 5000, 10000, 20000]) {
const evil = `{{$a${'.b'.repeat(n)}`; // repetition simple, pas de fermeture
timeIt(` n=${n} (${n} segments ".b", pas de fermeture)`, () => patternB.exec(evil));
}
for (const n of [1000, 5000, 10000, 20000]) {
const evil = `{{$a${'.b[0]'.repeat(n)}`; // alternance des deux branches
timeIt(` n=${n} (${n} segments ".b[0]" alternes, pas de fermeture)`, () => patternB.exec(evil));
}
console.log('');
console.log('=== Pattern C (xapi-client.js:132) ===');
const patternC = /\/+$/;
for (const n of [10000, 100000, 1000000]) {
const evil = '/'.repeat(n);
timeIt(` n=${n} (que des slashes)`, () => patternC.exec(evil));
}
console.log('');
console.log('=== Verification d\'equivalence (entrees valides normales, comportement inchange attendu) ===');
console.log('A sur "{{Objet.champ}}":', patternA.exec('{{Objet.champ}}'));
console.log('B sur "{{$var.champ[0].sous}}":', patternB.exec('{{$var.champ[0].sous}}'));
console.log('C sur "https://host///":', 'https://host///'.replace(patternC, ''));