// 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, ''));