Terrain 16 — Sécurité & qualité
⏱️ TL;DR — 20 mini-jeux
⭐⭐⭐⭐sur les réflexes de sécurité et de qualité d’un plugin Moodle :require_login, capabilities, sesskey,PARAM_*, DML paramétré, échappement de sortie, Privacy API, coding style, tests. Ce sont les règles que la review moodle.org traque — et qui séparent un plugin pro d’une passoire.
⚠️ La devise : « Vérifier l’accès (login + capability). Assainir l’entrée (PARAM). Échapper la sortie (s/format). Protéger les actions (sesskey). »
🕹️ Mini-jeu 1 — require_login ⭐⭐⭐⭐ 💻
But : exiger une session. Mission : protège une page de plugin. Étapes :
require_login($course, true, $cm);✅ Solution
require_login() garantit un utilisateur connecté et l’accès au cours/activité (visibilité, inscription, restrictions). Première ligne de toute page. Sans elle, l’accès est ouvert. Variantes : require_login() seul (contexte système).
🕹️ Mini-jeu 2 — La capability ⭐⭐⭐⭐ 💻
But : l’autorisation fine. Mission : exige une capability dans le bon contexte. Étapes :
require_capability('mod/simplecheck:grade', $context);✅ Solution
require_capability($cap, $context) bloque si le droit manque dans ce contexte (terrain 11). require_login dit « connecté » ; require_capability dit « autorisé ». Les deux sont nécessaires. has_capability() renvoie un booléen pour un affichage conditionnel.
🕹️ Mini-jeu 3 — Le sesskey ⭐⭐⭐⭐ 💻
But : anti-CSRF. Mission : protège une action destructive. Étapes :
require_sesskey(); // ou confirm_sesskey()✅ Solution
Le sesskey prouve que la requête vient de ton interface (pas d’un site tiers = CSRF). Toute action qui modifie un état via GET/POST hors Form API exige require_sesskey(), et le lien doit porter sesskey(). La Form API le gère automatiquement.
🕹️ Mini-jeu 4 — required_param + PARAM_* ⭐⭐⭐⭐ 💻
But : lire l’entrée en sécurité.
Mission : lis un id et une action.
Étapes :
$id = required_param('id', PARAM_INT);
$act = optional_param('action', 'view', PARAM_ALPHA);✅ Solution
required_param/optional_param + un type PARAM (INT, ALPHA, TEXT, ALPHANUMEXT, RAW…) lisent et assainissent. On ne touche jamais $_GET/$_POST bruts. Le type est la première défense. RAW = non filtré (à éviter/échapper à la sortie).
🕹️ Mini-jeu 5 — DML paramétré ⭐⭐⭐⭐⭐ 💻
But : anti-injection SQL. Mission : interroge la base avec un paramètre utilisateur. Étapes :
$record = $DB->get_record('simplecheck', ['id' => $id, 'course' => $courseid], '*', MUST_EXIST);✅ Solution
Le DML paramètre les conditions → pas d’injection. Pour du SQL custom : $DB->get_records_sql($sql, $params) avec des placeholders (:id), jamais de concaténation. Privilégie l’API haut niveau (get_record(s), get_in_or_equal).
🕹️ Mini-jeu 6 — Échapper le texte simple ⭐⭐⭐⭐ 💻
But : anti-XSS à la sortie. Mission : affiche une donnée utilisateur en sécurité. Étapes :
echo s($user->firstname); // texte brut échappé
echo format_string($course->fullname); // noms (filtres appliqués)✅ Solution
s() échappe un texte pour du HTML ; format_string() échappe et applique les filtres (pour les noms/titres). On échappe au moment de la sortie. Les templates Mustache échappent déjà {{var}} automatiquement (préfère-les).
🕹️ Mini-jeu 7 — Formater du contenu riche ⭐⭐⭐⭐ 💻
But : afficher du HTML sûr. Mission : affiche une description (HTML) en sécurité. Étapes :
echo format_text($instance->intro, $instance->introformat, ['context' => $context]);✅ Solution
format_text() nettoie le HTML (anti-XSS), applique filtres et gère les fichiers embarqués (via le contexte). Pour du contenu riche (intro, forum). ⚠️ Ne jamais afficher du HTML utilisateur sans format_text (ou s() pour du texte simple).
🕹️ Mini-jeu 8 — clean_param ⭐⭐⭐ 💻
But : assainir une valeur en code. Mission : assainis une valeur reçue autrement que par param. Étapes :
$slug = clean_param($raw, PARAM_ALPHANUMEXT);✅ Solution
clean_param($value, PARAM_*) applique le filtrage d’un type PARAM à une valeur déjà en main (issue d’un calcul, d’un webservice). Complète required_param. Toujours typer les données douteuses.
🕹️ Mini-jeu 9 — Démo XSS ⭐⭐⭐⭐ 💻
But : voir la faille pour la comprendre.
Mission : affiche une saisie sans échapper (en local), constate, puis corrige avec s().
✅ Solution
echo $data->name avec <script>alert(1)</script> exécute le script (XSS). Correction : echo s($data->name). Fais-le en local : voir la faille tirer ancre le réflexe d’échappement bien mieux que la théorie.
🕹️ Mini-jeu 10 — Démo CSRF ⭐⭐⭐⭐ 💻
But : comprendre le sesskey par l’absence. Mission : une action de suppression sans sesskey — explique l’attaque.
✅ Solution
Sans sesskey, un site tiers peut déclencher une suppression au nom d’un enseignant connecté (CSRF). require_sesskey() casse l’attaque (l’attaquant ne peut pas deviner le jeton). D’où : jamais d’action d’état sans sesskey (ou via Form API).
🕹️ Mini-jeu 11 — Sécurité de la File API ⭐⭐⭐⭐⭐ 💻
But : servir des fichiers en sécurité. Mission : repère comment un plugin sert un fichier (contexte + capability).
✅ Solution
Servir un fichier passe par un callback *_pluginfile() qui vérifie login + capability + contexte avant de délivrer le fichier (via send_stored_file). Sinon, fuite de documents privés. La File API sécurise l’accès aux fichiers stockés (Partie 5).
🕹️ Mini-jeu 12 — Le Privacy API ⭐⭐⭐⭐⭐ 💻
But : conformité RGPD (obligatoire).
Mission : implémente le provider (metadata + export + delete).
✅ Solution
Tout plugin stockant des données perso doit un classes/privacy/provider.php : déclarer les données (get_metadata), les exporter (export_user_data) et les supprimer (delete_data_for_user). Sans lui, install signalée non conforme → refus wp… pardon, moodle.org (Partie 11).
🕹️ Mini-jeu 13 — Zéro chaîne en dur ⭐⭐⭐ 💻
But : i18n = qualité.
Mission : remplace un texte en dur par get_string.
Étapes :
echo get_string('mytasks', 'local_todolist');✅ Solution
Aucune chaîne visible en dur : tout via get_string() + lang/en/. C’est de l’i18n ET une exigence de qualité (le codechecker le vérifie). Un texte non traduisible = un plugin non publiable. (Côté JS : core/str, terrain 14.)
🕹️ Mini-jeu 14 — L’en-tête GPL & PHPDoc ⭐⭐⭐ 💻
But : conformité de forme.
Mission : ajoute l’en-tête GPL et un @package en tête de fichier.
✅ Solution
Chaque fichier PHP porte l’en-tête GPL standard + PHPDoc (@package, @copyright, @license). Le générateur de squelette (terrain 12) les met. Exigé par le coding style et la review. Forme = signal de sérieux.
🕹️ Mini-jeu 15 — Le coding style ⭐⭐⭐⭐ 💻
But : passer le linter.
Mission : lance moodle-cs et corrige les écarts.
✅ Solution
moodle-cs (PHP_CodeSniffer) vérifie indentation, nommage, PHPDoc, fonctions interdites, patterns de sécurité. Vise zéro erreur. À lancer en continu (VS Code/CI), pas à la fin. C’est le juge de forme de moodle.org.
🕹️ Mini-jeu 16 — Un test PHPUnit ⭐⭐⭐⭐⭐ 💻
But : prouver que ça marche.
Mission : écris un test de ..._add_instance dans tests/.
✅ Solution
Un test étend advanced_testcase, utilise $this->getDataGenerator() pour créer cours/users/instances, et assert le comportement. $this->resetAfterTest(). Lancé avec vendor/bin/phpunit. Filet contre les régressions (terrain 12, jeu 20).
🕹️ Mini-jeu 17 — Un scénario Behat ⭐⭐⭐⭐⭐ 💻
But : tester le parcours utilisateur.
Mission : écris un scénario tests/behat/ (créer et cocher une checklist).
✅ Solution
Behat décrit le parcours en langage Given/When/Then (Gherkin), joué dans un navigateur. Vérifie l’UX de bout en bout (l’étudiant coche → la note apparaît). Lourd mais précieux pour un mod. Lancé via l’environnement Behat (terrain 12).
🕹️ Mini-jeu 18 — Frankenstyle sans collision ⭐⭐⭐ 💻
But : éviter les conflits. Mission : vérifie que toutes tes fonctions/tables/strings sont préfixées.
✅ Solution
Le frankenstyle (local_todolist_, mod_simplecheck_) évite les collisions avec le cœur et les autres plugins. Fonctions globales, tables, options, capabilities, événements : tout préfixé. Une fonction save() non préfixée casserait potentiellement le site.
🕹️ Mini-jeu 19 — Ne jamais hacker le cœur ⭐⭐⭐ 💻
But : la règle absolue. Mission : un besoin semble impossible sans modifier le cœur — que fais-tu ?
✅ Solution
Jamais de core hack (chaque MAJ l’écraserait, et c’est un risque de sécurité). On passe par hooks/callbacks, un plugin du bon type, ou une surcharge propre. Si vraiment impossible, on propose un patch au cœur (Partie 11). Tout est faisable par plugin.
🕹️ Mini-jeu 20 — Check-list sécurité/qualité ⭐⭐⭐⭐ 💻
But : capstone du terrain.
Mission : audite local_todolist/mod_simplecheck : login + capability sur chaque page, sesskey sur chaque action, PARAM sur chaque entrée, DML paramétré, sortie échappée, privacy provider, i18n complète, coding style vert, quelques tests.
✅ Solution
Si tu coches tout, tes plugins sont sûrs et publiables. Ces réflexes sont exactement ce que traque la review moodle.org (Partie 11). La sécurité/qualité n’est pas une étape finale : c’est une façon d’écrire chaque ligne. Cap sur l’ouverture vers l’extérieur : les web services (terrain 17).
Terrain suivant : 17 — Web services.