Cours 2 – Compléments avancés DOM & Évènements

Quelques points supplémentaires

Pêle-mêle :

Vulnérabilité XSS au TD2

Lors du TD2, vous avez pu être amenés à écrire le code suivant pour l’affichage du tableau de score du championnat de football

class Equipe {
  toHTML() {
    return `<tr><td>${this.classement}</td><td>${this.nom}</td></tr>`
  }
}

// championnat.js
class Championnat {
  afficherClassement() {
    document.querySelectorAll("#bloc-classement tbody").insertAdjacentHTML(
      'beforeend', 
      this.tabEquipes[i].toHTML()
    )
  } 
}

Or, ce code a une vulnérabilité XSS. Le nom de l’équipe est saisi un utilisateur, qui pourrait provoquer un comportement non souhaité du navigateur.

Solution 1

On utilise une variante personnalisée des template string : `string text ${expression} string text`. Ces variantes récupèrent les morceaux de chaines de caractères du template string, ainsi que les valeurs des expressions. Puis, il peut les traiter comme il le souhaite. Dans notre cas, nous allons échapper à la main les valeurs des expressions.

// library.js
function escapeHtml(text) {
  return String(text)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
}

function safeTag(strings, ...values) {
  let out = ""
  for (let i = 0; i < strings.length; i++) {
    out += strings[i]
    if (i < values.length) {
      safeValue = escapeHtml(values[i])
      out += safeValue
    }
  }
  return out        
}

// equipe.js
class Equipe {
  toHTML() {
    return safeTag`<tr><td>${this.classement}</td><td>${this.nom}</td></tr>`
  }
}

Source sur MDN pour les tagged templates tagFunction`string text ${expression} string text`

Solution 2

L’utilisation de la balise HTML <template> peut faciliter la mise en place d’une solution. Le <template> permet de cloner facilement un morceau de page Web. On peut alors cibler certaines balises, par exemple grâce à leur identifiant, pour modifier leur textContent.

<!-- championnat.html -->
<template id="ligne">
  <tr>
    <td id="classement"></td>
    <td id="nom"></td>
  </tr>
</template>
// equipe.js
class Equipe {
  toHTMLElement() {
    let template = document.getElementById("ligne");
    let ligne = template.content.cloneNode(true);
    ligne.getElementById("classement").textContent = this.classement;
    ligne.getElementById("nom").textContent = this.nom;
    return ligne;
  }
}

// championnat.js
class Championnat {
  afficherClassement() {
    document.querySelectorAll("#bloc-classement tbody").insertAdjacentElement(
      'beforeend', 
      this.tabEquipes[i].toHTMLElement()
    )
  } 
}

Source de l’ANSSI