227 lines
54 KiB
JavaScript
227 lines
54 KiB
JavaScript
// Cours complets · lot 10 : algorithmique et structures, développement web back-end, JavaScript et TypeScript, langage C
|
||
(function () {
|
||
var reg = function (c) {
|
||
(window.CONTENUS_PRO = window.CONTENUS_PRO || []).unshift(c.cours);
|
||
(window.CONTENUS_PLUS = window.CONTENUS_PLUS || {})[c.cours.id] = c.plus;
|
||
(window.CONTENUS_EXEMPLES = window.CONTENUS_EXEMPLES || {})[c.cours.id] = c.exemples;
|
||
(window.CONTENUS_SCHEMAS = window.CONTENUS_SCHEMAS || {})[c.cours.id] = { termes: c.termes, figs: [] };
|
||
};
|
||
|
||
reg({
|
||
cours: { id: 'algo', match: /Tris|dichotomique|complexité|Complexité et notation O|Récursivité|Piles, files|listes chaînées|Graphes : parcours|Dijkstra|Listes, tuples|Fonctions, portée|Tests : assert/i,
|
||
titre: 'Algorithmique : complexité, tris, récursivité, structures de données et graphes', duree: '',
|
||
niveau: 'NSI Première et Terminale · BTS SIO SLAM · BUT Informatique · classes préparatoires',
|
||
prerequis: ['Python : variables, boucles, fonctions'],
|
||
competences: ['NSI · Algorithmique et structures de données', 'BTS SIO SLAM · Concevoir une solution applicative', 'BUT Info · Compétence « Optimiser »'],
|
||
intro: 'Deux programmes corrects peuvent avoir des comportements radicalement différents : l’un trie un million de lignes en une seconde, l’autre en plusieurs heures. L’algorithmique donne les outils pour choisir la bonne méthode et la bonne structure de données, et pour prévoir comment un programme se comportera quand les données grandiront. Ce module couvre la complexité, les tris et la recherche, la récursivité, les structures linéaires et les graphes, avec leurs applications concrètes en réseau et en informatique de gestion.',
|
||
cm: [
|
||
{ t: 'Mesurer un algorithme : la complexité', d: '', html: 'La <b>complexité en temps</b> compte le nombre d’opérations élémentaires en fonction de la taille n des données, dans le pire cas. On ne garde que l’ordre de grandeur, noté <b>O</b> : une boucle simple est en <b>O(n)</b>, deux boucles imbriquées en <b>O(n²)</b>, une recherche qui divise l’espace par deux à chaque étape en <b>O(log n)</b>. Pour n = 1 000 000, O(n log n) représente environ 20 millions d’opérations, O(n²) mille milliards : la différence entre une seconde et plusieurs jours. La <b>complexité en espace</b> mesure la mémoire utilisée.', table: [['Complexité', 'Exemple', 'n = 10⁶'], ['O(1)', 'accès à un élément d’un tableau, d’un dictionnaire', '1'], ['O(log n)', 'recherche dichotomique', '≈ 20'], ['O(n)', 'parcours, recherche linéaire', '10⁶'], ['O(n log n)', 'tri fusion, tri de Python', '≈ 2 × 10⁷'], ['O(n²)', 'tri par sélection, double boucle', '10¹²'], ['O(2ⁿ)', 'tester tous les sous-ensembles', 'inenvisageable']] },
|
||
{ t: 'Recherche et tris', d: '', html: 'La <b>recherche dichotomique</b> dans un tableau <b>trié</b> compare la valeur cherchée au milieu, puis continue dans la moitié qui convient : O(log n). Les tris simples, par <b>sélection</b> et par <b>insertion</b>, sont en O(n²) ; le tri par insertion est très efficace sur des données presque triées. Le <b>tri fusion</b> découpe le tableau en deux, trie chaque moitié récursivement et fusionne : O(n log n) garanti. Le <b>tri rapide</b> est en moyenne en O(n log n). En pratique, on utilise le tri de la bibliothèque (<code>sorted</code>, Timsort), mais comprendre ces algorithmes permet de raisonner sur les coûts et de réussir les épreuves.', exemple: 'def dichotomie(t, x):\n g, d = 0, len(t) - 1\n while g <= d:\n m = (g + d) // 2\n if t[m] == x:\n return m\n if t[m] < x:\n g = m + 1\n else:\n d = m - 1\n return -1' },
|
||
{ t: 'Récursivité', d: '', html: 'Une fonction <b>récursive</b> s’appelle elle-même sur un problème plus petit, jusqu’à un <b>cas de base</b> qui s’arrête. Elle exprime naturellement les problèmes à structure récursive : parcours d’arborescence de fichiers, tri fusion, calculs sur des arbres. Chaque appel occupe une place sur la <b>pile d’appels</b> : sans cas de base atteint, on obtient un dépassement (RecursionError en Python, limite par défaut de 1 000 appels). Certaines récursions recalculent plusieurs fois les mêmes valeurs (Fibonacci naïf en O(2ⁿ)) : la <b>mémoïsation</b> (<code>functools.cache</code>) ou une version itérative corrige le problème.' },
|
||
{ t: 'Structures de données linéaires', d: '', html: 'Une <b>pile</b> (LIFO, dernier entré premier sorti) sert à l’historique d’annulation, à la pile d’appels, à la vérification des parenthèses. Une <b>file</b> (FIFO) sert aux files d’attente : impressions, requêtes, mains levées dans l’ordre d’arrivée. En Python, une liste fait une bonne pile (<code>append</code>, <code>pop</code>), mais une mauvaise file (<code>pop(0)</code> en O(n)) : on utilise <code>collections.deque</code>. Une <b>liste chaînée</b> relie des maillons par des références : insertion en tête en O(1), accès au k-ième élément en O(k). Le <b>dictionnaire</b> (table de hachage) donne un accès moyen en O(1) par clé : c’est souvent la structure qui transforme un O(n²) en O(n).' },
|
||
{ t: 'Graphes et parcours', d: '', html: 'Un <b>graphe</b> est un ensemble de sommets reliés par des arêtes, éventuellement orientées et pondérées : routeurs et liens, pages web, dépendances entre tâches. On le représente par une <b>matrice d’adjacence</b> ou, plus économiquement, par des <b>listes d’adjacence</b> (un dictionnaire de voisins). Le <b>parcours en largeur</b> (BFS, avec une file) visite par distance croissante et trouve le plus court chemin en nombre d’arêtes ; le <b>parcours en profondeur</b> (DFS, avec une pile ou la récursivité) sert à détecter des cycles ou des composantes connexes.' },
|
||
{ t: 'Plus courts chemins : Dijkstra', d: '', html: 'L’algorithme de <b>Dijkstra</b> calcule les plus courts chemins depuis un sommet dans un graphe à poids positifs : il sélectionne à chaque étape le sommet non traité le plus proche et met à jour les distances de ses voisins. Avec une file de priorité (<code>heapq</code>), il s’exécute en O((V + E) log V). C’est exactement ce que fait chaque routeur <b>OSPF</b> pour construire sa table de routage à partir de la carte du réseau, avec un coût par lien inversement proportionnel au débit.', exemple: 'import heapq\ndef dijkstra(g, s):\n dist = {s: 0}\n file = [(0, s)]\n while file:\n d, u = heapq.heappop(file)\n if d > dist[u]:\n continue\n for v, w in g[u].items():\n if d + w < dist.get(v, float("inf")):\n dist[v] = d + w\n heapq.heappush(file, (d + w, v))\n return dist' }
|
||
],
|
||
synthese: ['Complexité O : O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ)', 'Dichotomie sur tableau trié en O(log n) ; tri fusion en O(n log n)', 'Récursivité : cas de base, pile d’appels, mémoïsation', 'Pile LIFO, file FIFO avec deque, dictionnaire en O(1) moyen', 'Graphes : listes d’adjacence ; BFS et DFS', 'Dijkstra et OSPF'],
|
||
qcm: [
|
||
['Complexité de la recherche dichotomique ?', ['O(1)', 'O(log n)', 'O(n)', 'O(n²)'], 1, 'On divise par deux à chaque étape.'],
|
||
['Condition pour utiliser la dichotomie ?', ['Tableau trié', 'Tableau court', 'Nombres entiers', 'Aucune'], 0, 'Sinon on ne sait pas quelle moitié choisir.'],
|
||
['Quelle structure pour une file d’attente en Python ?', ['list avec pop(0)', 'collections.deque', 'set', 'tuple'], 1, 'popleft en O(1).'],
|
||
['Que manque-t-il à une récursion qui provoque RecursionError ?', ['Un return', 'Un cas de base atteint', 'Une boucle', 'Un print'], 1, 'Elle ne s’arrête jamais.'],
|
||
['Quel parcours trouve le plus court chemin en nombre d’arêtes ?', ['DFS', 'BFS', 'Tri fusion', 'Dichotomie'], 1, 'Par distance croissante.'],
|
||
['Quel protocole de routage utilise Dijkstra ?', ['RIP', 'OSPF', 'DHCP', 'ARP'], 1, 'État de liens.'],
|
||
['Deux boucles imbriquées sur n éléments :', ['O(n)', 'O(n²)', 'O(log n)', 'O(2n)'], 1, 'n × n.']
|
||
],
|
||
td: [
|
||
{ n: 'Échauffement', c: 'Donner la complexité de : parcourir une liste ; vérifier si deux listes ont un élément commun avec deux boucles ; même chose en transformant la seconde en ensemble.', r: 'O(n) · O(n × m) · O(n + m)' },
|
||
{ n: 'Application', c: 'Écrire un tri par insertion, puis compter le nombre de comparaisons sur une liste déjà triée et sur une liste triée à l’envers de 1 000 éléments.', r: 'Déjà triée : environ n comparaisons (O(n)) · à l’envers : environ n²/2 = 500 000 (O(n²)).' },
|
||
{ n: 'Application', c: 'Vérifier le bon parenthésage d’une expression avec une pile.', r: 'Empiler chaque ouvrante ; pour chaque fermante, la pile ne doit pas être vide et son sommet doit correspondre ; à la fin, la pile doit être vide.' },
|
||
{ n: 'Approfondissement', c: 'Appliquer Dijkstra à la main sur un réseau de cinq routeurs fourni, puis en déduire la table de routage du routeur A.', r: 'Tableau des distances étape par étape ; la table associe à chaque destination le premier saut du plus court chemin.' }
|
||
],
|
||
tp: { type: 'IDE Python', url: 'apercu-ide.html', desc: 'Exercices avec tests et mesure du temps d’exécution.', etapes: ['Dichotomie et tests', 'Tri fusion', 'Comparaison des temps selon n', 'File des mains levées avec deque', 'BFS sur un plan de salles', 'Dijkstra sur un réseau de routeurs'] },
|
||
plusLoin: [['France-IOI · parcours algorithmique', 'https://www.france-ioi.org/'], ['HackerRank · Data Structures et Algorithms', 'https://www.hackerrank.com/domains/algorithms'], ['VisuAlgo (visualisation)', 'https://visualgo.net/fr']] },
|
||
plus: { cas: { titre: '', texte: '' },
|
||
resolu: { enonce: 'Le calcul des doublons dans l’export des 12 000 élèves de l’académie prend 40 minutes. Rendre le traitement quasi instantané.', etapes: [
|
||
['Lire le code', 'for a in eleves:\n for b in eleves:\n if a is not b and a["ine"] == b["ine"]: doublons.append(a)\n→ Deux boucles imbriquées : 144 millions de comparaisons, O(n²).'],
|
||
['Choisir la structure', '→ Un dictionnaire (ou un Counter) compte les occurrences de chaque identifiant en un seul parcours.'],
|
||
['Réécrire', 'from collections import Counter\nc = Counter(e["ine"] for e in eleves)\ndoublons = [e for e in eleves if c[e["ine"]] > 1]\n→ Deux parcours : O(n).'],
|
||
['Mesurer', 'Temps : 40 min → 0,02 s\n→ Même résultat vérifié par un test sur un petit jeu de données connu.'],
|
||
['Retenir', '→ Devant une double boucle de recherche, se demander quelle clé permettrait un accès direct par dictionnaire ou ensemble.']
|
||
] },
|
||
erreurs: ['Dichotomie sur une liste non triée', 'Oublier le cas de base', 'list.pop(0) pour une file', 'Double boucle là où un dictionnaire suffit', 'Recalculer sans mémoïsation', 'Dijkstra avec des poids négatifs'],
|
||
memo: [['Outil', 'Usage'], ['len, in sur set/dict', 'O(1) en moyenne'], ['sorted(t, key=…)', 'tri O(n log n)'], ['bisect', 'dichotomie de la bibliothèque'], ['collections.deque', 'file'], ['collections.Counter', 'compter'], ['heapq', 'file de priorité'], ['functools.cache', 'mémoïsation'], ['timeit', 'mesurer le temps'], ['assert f(x) == y', 'tests simples'], ['sys.setrecursionlimit', 'limite de récursion (avec prudence)']],
|
||
examen: { titre: 'Sujet type · 20 points', q: ['Déterminer la complexité de trois fonctions. (4 pts)', 'Écrire la recherche dichotomique et ses tests. (4 pts)', 'Écrire une fonction récursive et justifier sa terminaison. (4 pts)', 'Implémenter une file avec deque. (3 pts)', 'Appliquer Dijkstra à la main. (5 pts)'], r: ['O(n), O(n²), O(log n)', 'voir cours', 'cas de base, taille décroissante', 'append / popleft', 'tableau des distances'] },
|
||
prof: { deroule: [['Ouverture', 'Chronométrer une double boucle puis un dictionnaire'], ['Cours', 'Complexité'], ['Pratique', 'Tris et dichotomie'], ['Cours', 'Récursivité et structures'], ['Pratique', 'Graphes et Dijkstra'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : visualisations VisuAlgo. Élèves avancés : HackerRank Medium, A*.', eval: 'Tests IDE, sujet type.' } },
|
||
exemples: [
|
||
['Comparer deux complexités', 'Justifier un choix.', 'python3 -m timeit -s "l=list(range(10**5)); s=set(l)" "99999 in l"\npython3 -m timeit -s "l=list(range(10**5)); s=set(l)" "99999 in s"', '500 loops, best of 5: 612 usec per loop\n5000000 loops, best of 5: 42.1 nsec per loop', 'La recherche dans un ensemble est environ 15 000 fois plus rapide que dans une liste.'],
|
||
['Dichotomie de la bibliothèque', 'Retrouver un seuil de note.', 'import bisect; bisect.bisect_left([8, 10, 12, 14, 16], 13)', '3', 'Position d’insertion de 13 dans la liste triée, en O(log n).'],
|
||
['Limite de récursion', 'Fonction récursive sur une longue liste.', 'def somme(l): return 0 if not l else l[0] + somme(l[1:])\nsomme(list(range(5000)))', 'RecursionError: maximum recursion depth exceeded', 'Au-delà de 1 000 appels : on écrit une version itérative, plus adaptée ici.'],
|
||
['Mémoïsation', 'Fibonacci lent.', 'from functools import cache\n@cache\ndef fib(n): return n if n < 2 else fib(n-1) + fib(n-2)\nfib(200)', '280571172992510140037611932413038677189525', 'Instantané grâce au cache ; la version naïve ne terminerait pas.'],
|
||
['File des mains levées', 'Fonction Lever la main.', 'from collections import deque\nq = deque(["Nora", "Yanis"]); q.append("Léa"); q.popleft(), list(q)', "('Nora', ['Yanis', 'Léa'])", 'Premier arrivé, premier servi, en O(1).'],
|
||
['Plus courts chemins', 'Réseau de quatre routeurs.', 'g = {"A":{"B":10,"C":1},"B":{"D":1},"C":{"B":1,"D":20},"D":{}}\ndijkstra(g, "A")', "{'A': 0, 'C': 1, 'B': 2, 'D': 3}", 'Le chemin vers D passe par C puis B (coût 3) plutôt que par le lien direct C–D (21).']
|
||
],
|
||
termes: ['complexité', 'O(n)', 'O(log n)', 'O(n²)', 'O(n log n)', 'recherche dichotomique', 'tri par insertion', 'tri fusion', 'récursivité', 'cas de base', 'pile d’appels', 'mémoïsation', 'pile', 'file', 'deque', 'liste chaînée', 'dictionnaire', 'graphe', 'BFS', 'DFS', 'Dijkstra', 'heapq']
|
||
});
|
||
|
||
reg({
|
||
cours: { id: 'backend', match: /Laravel|MVC|API REST|OpenAPI|Authentification : sessions|OAuth|PHP : bases|Framework back|Déployer une application web|Sécurité web|CSP|CORS|Tests : unitaires, intégration/i,
|
||
titre: 'Développement web back-end : MVC avec Laravel, API REST, authentification et sécurité', duree: '',
|
||
niveau: 'BTS SIO SLAM · BUT Informatique · licence pro · Master',
|
||
prerequis: ['Le Web : HTTP et HTML', 'SQL', 'Git'],
|
||
competences: ['BTS SIO SLAM · Bloc 2 · Concevoir et développer une solution applicative', 'BTS SIO · Bloc 3 · Cybersécurité des services', 'BUT Info · Réaliser et déployer'],
|
||
intro: 'Une application web professionnelle se compose d’une interface, d’une logique métier, d’une base de données et d’une API, le tout sécurisé et testé. Les frameworks comme Laravel, sur lequel est construit Syllabus, apportent une organisation éprouvée et des protections intégrées. Ce module présente l’architecture MVC, le routage, l’ORM, la conception d’une API REST, l’authentification, les protections contre les failles courantes et le déploiement.',
|
||
cm: [
|
||
{ t: 'Architecture MVC', d: '', html: 'Le motif <b>Modèle-Vue-Contrôleur</b> sépare les responsabilités. Le <b>modèle</b> représente les données et les règles métier (un élève, une classe, un devoir). La <b>vue</b> présente les données (templates Blade). Le <b>contrôleur</b> reçoit la requête, appelle le modèle et choisit la vue ou la réponse. Le <b>routeur</b> associe chaque URL et méthode HTTP à une action. Cette séparation rend le code testable et permet à plusieurs développeurs de travailler en parallèle.', exemple: '// routes/web.php\nRoute::middleware("auth")->group(function () {\n Route::get("/classes/{classe}", [ClasseController::class, "show"])->name("classes.show");\n});\n\n// app/Http/Controllers/ClasseController.php\npublic function show(Classe $classe) {\n $this->authorize("view", $classe);\n return view("classes.show", [\n "classe" => $classe->load("eleves"),\n ]);\n}' },
|
||
{ t: 'Modèles, ORM et migrations', d: '', html: 'L’<b>ORM</b> Eloquent associe chaque table à une classe : <code>Eleve::where("classe_id", 3)->orderBy("nom")->get()</code> produit une requête SQL <b>préparée</b>. Les <b>relations</b> (hasMany, belongsTo, belongsToMany) décrivent les clés étrangères. Les <b>migrations</b> versionnent le schéma de la base avec le code : chaque modification est un fichier, appliqué dans le même ordre partout. Piège de performance classique : le problème <b>N+1</b>, une requête par élément d’une liste, évité par le chargement anticipé (<code>with("eleves")</code>).' },
|
||
{ t: 'Concevoir une API REST', d: '', html: 'Une API <b>REST</b> expose des <b>ressources</b> identifiées par des URL et manipulées par les méthodes HTTP : <code>GET /api/devoirs</code> liste, <code>POST /api/devoirs</code> crée, <code>GET /api/devoirs/12</code> lit, <code>PUT</code> ou <code>PATCH</code> modifie, <code>DELETE</code> supprime. Les réponses sont en <b>JSON</b> avec des codes cohérents : 200, 201 Created, 204 No Content, 400 et 422 pour une requête invalide, 401 non authentifié, 403 interdit, 404 introuvable. On <b>versionne</b> l’API (/api/v1), on pagine les listes, et on la documente avec <b>OpenAPI</b>, qui génère une documentation interactive et des clients.' },
|
||
{ t: 'Authentification et autorisation', d: '', html: 'L’<b>authentification</b> établit qui est l’utilisateur ; l’<b>autorisation</b> décide de ce qu’il peut faire. Pour une application web, une <b>session</b> côté serveur, identifiée par un cookie <code>HttpOnly</code>, <code>Secure</code> et <code>SameSite</code>, reste la solution la plus sûre. Pour une API consommée par d’autres applications, on utilise des <b>jetons</b> (tokens d’API, Laravel Sanctum) ou <b>OAuth 2</b> / <b>OpenID Connect</b> pour déléguer l’authentification à un fournisseur d’identité (ENT, EduConnect). Les mots de passe sont hachés avec bcrypt ou Argon2. L’autorisation se centralise dans des <b>politiques</b> (policies) : un professeur ne voit que ses classes.' },
|
||
{ t: 'Sécurité : les failles courantes', d: '', html: 'L’<b>OWASP Top 10</b> recense les failles les plus fréquentes. <b>Contrôle d’accès défaillant</b> : changer un identifiant dans l’URL donne accès aux données d’un autre (IDOR) ; on vérifie l’autorisation à chaque action. <b>Injection</b> : requêtes préparées systématiques. <b>XSS</b> : Blade échappe automatiquement <code>{{ }}</code> ; <code>{!! !!}</code> ne s’utilise que sur du contenu maîtrisé. <b>CSRF</b> : un jeton dans chaque formulaire (<code>@csrf</code>). <b>Assignation de masse</b> : déclarer les champs modifiables (<code>$fillable</code>). À quoi s’ajoutent les <b>en-têtes</b> : Content-Security-Policy contre l’injection de scripts, CORS limité aux origines autorisées, HSTS.', attention: 'Le mode débogage (APP_DEBUG=true) en production affiche les traces d’erreur, les variables d’environnement et parfois les mots de passe de la base. Il doit être désactivé, et le fichier .env ne doit jamais être accessible par le web ni versionné.' },
|
||
{ t: 'Tester et déployer', d: '', html: 'Les <b>tests unitaires</b> vérifient une classe isolée, les <b>tests de fonctionnalité</b> envoient de vraies requêtes à l’application (<code>$this->actingAs($prof)->get("/classes/3")->assertOk()</code>), les tests de <b>bout en bout</b> pilotent un navigateur. Le déploiement suit toujours les mêmes étapes, automatisées par la CI : récupération du code, <code>composer install --no-dev</code>, migrations, mise en cache de la configuration et des routes, redémarrage des workers de file d’attente, derrière Nginx et PHP-FPM, avec un retour arrière possible.' }
|
||
],
|
||
synthese: ['MVC : modèle, vue, contrôleur, routeur', 'ORM et migrations ; éviter le N+1 avec with()', 'REST : ressources, méthodes, codes HTTP, JSON, versionnement, OpenAPI', 'Session sécurisée ou jetons ; OAuth 2 / OIDC ; policies pour l’autorisation', 'OWASP : contrôle d’accès, injection, XSS, CSRF, assignation de masse ; en-têtes', 'APP_DEBUG=false, .env protégé ; tests et déploiement automatisés'],
|
||
qcm: [
|
||
['Dans MVC, qui reçoit la requête et choisit la réponse ?', ['Le modèle', 'Le contrôleur', 'La vue', 'La base'], 1, 'Il orchestre.'],
|
||
['Quelle méthode pour créer une ressource en REST ?', ['GET', 'POST', 'DELETE', 'HEAD'], 1, 'Réponse 201.'],
|
||
['Code HTTP pour « authentifié mais pas autorisé » ?', ['401', '403', '404', '500'], 1, '401 = non authentifié.'],
|
||
['Qu’est-ce que le problème N+1 ?', ['Une faille', 'Une requête SQL par élément d’une liste', 'Un code HTTP', 'Une erreur de migration'], 1, 'Chargement anticipé.'],
|
||
['Protection contre le CSRF ?', ['Un jeton dans chaque formulaire', 'HTTPS seul', 'Un captcha', 'Le hachage'], 0, '@csrf.'],
|
||
['Pourquoi désactiver APP_DEBUG en production ?', ['Performance', 'Il expose des informations sensibles', 'Obligation légale', 'Pour le cache'], 1, 'Traces, variables, secrets.']
|
||
],
|
||
td: [
|
||
{ n: 'Application', c: 'Concevoir les routes REST de la ressource « devoirs » d’une classe, avec les codes de réponse attendus.', r: 'GET /api/v1/classes/{c}/devoirs 200 · POST 201 · GET /devoirs/{d} 200/404 · PATCH 200/422 · DELETE 204 · 401 sans jeton, 403 si pas professeur de la classe.' },
|
||
{ n: 'Application', c: 'Corriger la faille : « public function show($id) { return Eleve::find($id); } ».', r: 'Liaison de modèle + autorisation : show(Eleve $eleve) { $this->authorize("view", $eleve); return $eleve; } avec une policy vérifiant que l’élève appartient à une classe du professeur.' },
|
||
{ n: 'Approfondissement', c: 'Écrire un test de fonctionnalité qui vérifie qu’un professeur ne peut pas voir la fiche d’un élève d’une autre classe.', r: '$prof = User::factory()->prof()->create(); $eleve = Eleve::factory()->create(); $this->actingAs($prof)->get(route("eleves.show", $eleve))->assertForbidden();' }
|
||
],
|
||
tp: { type: 'IDE', url: 'apercu-ide.html', desc: 'Mini-application Laravel de gestion de devoirs.', etapes: ['Migration et modèle', 'Contrôleur et vues', 'API REST', 'Policy d’autorisation', 'Tests de fonctionnalité', 'Déploiement par CI'] },
|
||
plusLoin: [['Documentation Laravel', 'https://laravel.com/docs'], ['OWASP Top 10', 'https://owasp.org/Top10/fr/'], ['OpenAPI Specification', 'https://spec.openapis.org/oas/latest.html'], ['ANSSI · Recommandations pour la mise en œuvre d’un site web', 'https://cyber.gouv.fr/']] },
|
||
plus: { cas: { titre: '', texte: '' },
|
||
resolu: { enonce: 'Un élève signale qu’en modifiant le numéro dans l’adresse de sa page de résultats, il voit les notes d’un camarade.', etapes: [
|
||
['Reproduire', 'Connecté comme élève 1204 : GET /resultats/1205 → 200 OK, notes de l’élève 1205.\n→ Faille de contrôle d’accès (IDOR).'],
|
||
['Lire le code', 'public function show($id) { return view("resultats", ["eleve" => Eleve::findOrFail($id)]); }\n→ Aucune vérification que l’élève connecté est celui demandé.'],
|
||
['Corriger', 'Policy EleveResultatsPolicy : un élève voit ses résultats, un professeur ceux de ses classes, la vie scolaire tout l’établissement.\npublic function show(Eleve $eleve) { $this->authorize("voirResultats", $eleve); … }\n→ Réponse 403 pour un autre élève.'],
|
||
['Tester et vérifier les autres routes', '→ Test automatisé ajouté ; revue de toutes les routes prenant un identifiant : deux autres corrigées (exports, devoirs rendus).'],
|
||
['Traiter les conséquences', '→ Analyse des journaux d’accès : consultations anormales par deux comptes. Information du DPO, qui évalue la notification à la CNIL et l’information des familles concernées.']
|
||
] },
|
||
erreurs: ['Contrôler l’accès seulement dans l’interface', 'Requêtes SQL construites par concaténation', 'Utiliser {!! !!} sur une saisie utilisateur', 'APP_DEBUG=true en production', 'Pas de tests d’autorisation', 'Codes HTTP incohérents dans l’API'],
|
||
memo: [['Commande / élément', 'Usage'], ['php artisan route:list', 'lister les routes'], ['php artisan make:model -mcr Devoir', 'modèle, migration, contrôleur'], ['php artisan migrate', 'appliquer les migrations'], ['php artisan make:policy', 'politique d’autorisation'], ['$this->authorize()', 'vérifier un droit'], ['with("relation")', 'éviter le N+1'], ['@csrf', 'jeton de formulaire'], ['php artisan test', 'lancer les tests'], ['php artisan config:cache route:cache', 'optimiser en production'], ['curl -H "Authorization: Bearer …" /api/v1/…', 'tester une API']],
|
||
examen: { titre: 'Sujet type · 20 points', q: ['Expliquer MVC sur un exemple. (3 pts)', 'Concevoir une API REST. (5 pts)', 'Écrire un contrôleur avec autorisation. (5 pts)', 'Identifier et corriger trois failles dans un code fourni. (5 pts)', 'Décrire un déploiement. (2 pts)'], r: ['voir cours', 'ressources et codes', 'authorize + policy', 'IDOR, XSS, injection', 'composer, migrate, cache'] },
|
||
prof: { deroule: [['Ouverture', 'Démonstration d’une faille IDOR sur une application de lab'], ['Cours', 'MVC et routage'], ['Pratique', 'Modèle et contrôleur'], ['Cours', 'API et sécurité'], ['Pratique', 'Policies et tests'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : squelette fourni. Élèves avancés : OAuth 2, files d’attente, OpenAPI.', eval: 'Application testée, sujet type.' } },
|
||
exemples: [
|
||
['Lister les routes', 'Audit d’une application.', 'php artisan route:list --path=api', 'GET|HEAD api/v1/devoirs ...... devoirs.index › DevoirController@index\nPOST api/v1/devoirs ...... devoirs.store › DevoirController@store\nDELETE api/v1/devoirs/{devoir} devoirs.destroy', 'On vérifie que chaque route sensible a un middleware d’authentification.'],
|
||
['Détecter un N+1', 'Page lente.', 'DB::enableQueryLog(); $c = Classe::all(); foreach ($c as $x) $x->eleves->count(); count(DB::getQueryLog())', '31', '31 requêtes pour 30 classes. Avec Classe::withCount("eleves")->get(), une seule.'],
|
||
['Tester une API', 'Création d’un devoir.', 'curl -s -X POST https://syllabus.local/api/v1/devoirs -H "Authorization: Bearer $T" -H "Content-Type: application/json" -d \'{"titre":"DM 3"}\'', '{"message":"The date limite field is required.","errors":{"date_limite":["…"]}} (HTTP 422)', 'La validation renvoie 422 avec le détail des champs manquants.'],
|
||
['Test d’autorisation', 'Non-régression.', 'php artisan test --filter=ResultatsTest', 'PASS Tests\\Feature\\ResultatsTest\n✓ un élève voit ses résultats\n✓ un élève ne voit pas ceux d’un camarade', 'Le test échouerait si la faille réapparaissait.'],
|
||
['Vérifier les en-têtes', 'Audit de sécurité.', 'curl -sI https://syllabus.local | grep -iE "content-security|set-cookie"', 'content-security-policy: default-src \'self\'; frame-ancestors \'none\'\nset-cookie: syllabus_session=…; secure; httponly; samesite=lax', 'CSP restrictive et cookie de session protégé.'],
|
||
['Déploiement', 'Mise en production.', 'composer install --no-dev -o && php artisan migrate --force && php artisan optimize && php artisan queue:restart', 'INFO Running migrations. 2026_10_01_create_rendus_table ....... DONE\nINFO Caching framework bootstrap, configuration, and metadata.', 'Chaque étape est dans le pipeline CI, avec retour arrière en cas d’échec.']
|
||
],
|
||
termes: ['MVC', 'modèle', 'vue', 'contrôleur', 'routeur', 'Laravel', 'Blade', 'Eloquent', 'ORM', 'migration', 'N+1', 'API REST', 'JSON', 'OpenAPI', 'session', 'jeton', 'OAuth 2', 'OpenID Connect', 'policy', 'OWASP', 'IDOR', 'XSS', 'CSRF', 'CSP', 'CORS', 'APP_DEBUG']
|
||
});
|
||
|
||
reg({
|
||
cours: { id: 'js', match: /JavaScript|TypeScript|DOM|fetch|Accessibilité web|RGAA|WCAG|Performance web/i,
|
||
titre: 'JavaScript et TypeScript côté navigateur : DOM, asynchrone, fetch, accessibilité', duree: '',
|
||
niveau: 'NSI Première · BTS SIO SLAM · BUT Informatique · Titre pro DWWM',
|
||
prerequis: ['Le Web : HTML et CSS', 'Python : bases de la programmation'],
|
||
competences: ['NSI · Interactions dans le navigateur', 'BTS SIO SLAM · Développer une interface', 'DWWM · Développer la partie front-end'],
|
||
intro: 'Tout ce qui réagit dans une page web, du bouton « Lever la main » au terminal xterm.js des labs, est écrit en JavaScript. Le langage s’exécute dans le navigateur, manipule la page, dialogue avec le serveur sans la recharger et doit rester utilisable par tous, y compris au clavier et avec un lecteur d’écran. Ce module présente le langage moderne, le DOM, la programmation asynchrone avec fetch, TypeScript et les exigences d’accessibilité et de performance.',
|
||
cm: [
|
||
{ t: 'Le langage moderne', d: '', html: 'On déclare avec <code>const</code> (par défaut) et <code>let</code> ; <code>var</code> est à éviter. Les types de base : nombres, chaînes, booléens, <code>null</code>, <code>undefined</code>, objets et tableaux. Les <b>fonctions fléchées</b> (<code>x => x * 2</code>), la <b>déstructuration</b> (<code>const { nom, classe } = eleve</code>), les <b>gabarits</b> de chaînes (<code>`Bonjour ${nom}`</code>) et les méthodes de tableaux (<code>map</code>, <code>filter</code>, <code>reduce</code>, <code>find</code>) forment le socle quotidien. On compare avec <code>===</code>, qui ne convertit pas les types (<code>"1" == 1</code> est vrai, <code>"1" === 1</code> est faux). Les <b>modules</b> (<code>import</code> / <code>export</code>) organisent le code.' },
|
||
{ t: 'Le DOM et les événements', d: '', html: 'Le <b>DOM</b> est la représentation de la page sous forme d’arbre d’objets. On sélectionne un élément (<code>document.querySelector("#liste")</code>), on modifie son contenu (<code>textContent</code>), ses classes (<code>classList.add</code>) ou ses attributs, on en crée (<code>createElement</code>). On réagit aux actions de l’utilisateur avec <code>addEventListener("click", …)</code>. La <b>délégation d’événements</b> place un seul écouteur sur un parent pour gérer de nombreux enfants, y compris ceux ajoutés plus tard.', attention: 'innerHTML avec une donnée saisie par un utilisateur ouvre une faille XSS : le texte peut contenir un script. On utilise textContent pour du texte, ou on échappe explicitement.' },
|
||
{ t: 'Asynchrone : promesses et async/await', d: '', html: 'Le navigateur n’exécute qu’un seul fil de JavaScript ; une opération longue (requête réseau, minuterie) ne doit pas le bloquer. Elle renvoie une <b>promesse</b>, résolue plus tard. La syntaxe <code>async</code> / <code>await</code> permet d’écrire ce code de façon lisible, et <code>try</code> / <code>catch</code> gère les erreurs. <code>Promise.all</code> lance plusieurs requêtes en parallèle. La <b>boucle d’événements</b> exécute les rappels quand le fil principal est libre.' },
|
||
{ t: 'Dialoguer avec le serveur : fetch', d: '', html: '<code>fetch</code> envoie une requête HTTP et renvoie une promesse de réponse. Il ne rejette la promesse qu’en cas d’erreur réseau : une réponse 404 ou 500 est une réponse « réussie », il faut tester <code>response.ok</code>. On envoie du JSON avec l’en-tête Content-Type et on joint le jeton CSRF ou d’authentification. Pour des mises à jour en temps réel (mains levées, présences du quiz), on utilise les <b>WebSockets</b> ou les Server-Sent Events plutôt qu’interroger le serveur toutes les secondes.', exemple: 'async function leverLaMain(labId) {\n const r = await fetch(`/api/v1/labs/${labId}/mains`, {\n method: "POST",\n headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": jeton },\n body: JSON.stringify({ etape: 4 }),\n });\n if (!r.ok) throw new Error(`Erreur ${r.status}`);\n const { position } = await r.json();\n document.querySelector("#position").textContent = `Vous êtes ${position}e dans la file.`;\n}' },
|
||
{ t: 'TypeScript', d: '', html: '<b>TypeScript</b> ajoute un typage statique à JavaScript : on déclare la forme des données (<code>interface Eleve { id: number; nom: string; classe?: string }</code>) et le compilateur signale les erreurs avant l’exécution : propriété mal orthographiée, valeur possiblement undefined, mauvais type d’argument. Le code est compilé en JavaScript standard. Sur un projet de plusieurs milliers de lignes, il réduit fortement les régressions et rend l’éditeur beaucoup plus utile (complétion, renommage sûr).' },
|
||
{ t: 'Accessibilité et performance', d: '', html: 'Un site public en France doit respecter le <b>RGAA</b>, fondé sur les normes <b>WCAG</b>. Les règles de base : utiliser les bons éléments HTML (un <code><button></code> plutôt qu’un <code><div></code> cliquable, qui ne se commande pas au clavier), des étiquettes de formulaire liées, des textes alternatifs, un contraste suffisant (4,5:1 pour le texte), un focus visible, et <code>aria-live</code> pour annoncer les mises à jour dynamiques. Côté <b>performance</b> : réduire le JavaScript chargé, différer ce qui n’est pas indispensable (<code>defer</code>, chargement à la demande), optimiser les images, mettre en cache. Les outils de développement du navigateur et Lighthouse mesurent les deux.' }
|
||
],
|
||
synthese: ['const/let, fonctions fléchées, déstructuration, ===, modules', 'DOM : querySelector, textContent, addEventListener, délégation', 'Jamais innerHTML avec une saisie utilisateur', 'Promesses, async/await, try/catch, Promise.all', 'fetch : tester response.ok ; WebSockets pour le temps réel', 'TypeScript : interfaces et vérification avant exécution', 'RGAA/WCAG : éléments natifs, clavier, contraste, aria-live ; performance'],
|
||
qcm: [
|
||
['Que vaut "1" === 1 ?', ['true', 'false', 'undefined', 'erreur'], 1, 'Pas de conversion de type.'],
|
||
['Quelle propriété insérer pour afficher un texte saisi par l’utilisateur ?', ['innerHTML', 'textContent', 'outerHTML', 'eval'], 1, 'Pas d’interprétation HTML.'],
|
||
['Quand fetch rejette-t-il sa promesse ?', ['Sur un 404', 'Sur un 500', 'En cas d’erreur réseau', 'Jamais'], 2, 'Tester response.ok.'],
|
||
['À quoi sert await ?', ['Bloquer le navigateur', 'Attendre le résultat d’une promesse dans une fonction async', 'Créer une boucle', 'Déclarer une variable'], 1, 'Sans bloquer le fil principal.'],
|
||
['Pourquoi préférer <button> à une <div> cliquable ?', ['Esthétique', 'Accessible au clavier et aux lecteurs d’écran', 'Plus rapide', 'Obligatoire en HTML5'], 1, 'Comportement natif.'],
|
||
['Quel est l’apport principal de TypeScript ?', ['Plus rapide à l’exécution', 'Détecter des erreurs de types avant l’exécution', 'Remplacer HTML', 'Chiffrer le code'], 1, 'Typage statique.']
|
||
],
|
||
td: [
|
||
{ n: 'Application', c: 'À partir d’un tableau d’élèves, afficher dans une liste ceux dont la moyenne est inférieure à 10, triés par moyenne, avec textContent.', r: 'eleves.filter(e => e.moyenne < 10).sort((a, b) => a.moyenne - b.moyenne).forEach(e => { const li = document.createElement("li"); li.textContent = `${e.nom} : ${e.moyenne}`; liste.append(li); });' },
|
||
{ n: 'Application', c: 'Charger en parallèle la liste des classes et celle des devoirs, et afficher un message d’erreur accessible en cas d’échec.', r: 'try { const [c, d] = await Promise.all([fetch("/api/v1/classes"), fetch("/api/v1/devoirs")]); if (!c.ok || !d.ok) throw … } catch (e) { zone.textContent = "Chargement impossible"; } avec zone en aria-live="polite".' },
|
||
{ n: 'Approfondissement', c: 'Auditer l’accessibilité d’un formulaire fourni et proposer cinq corrections.', r: 'label lié à chaque champ · message d’erreur associé (aria-describedby) · bouton réel · contraste · ordre de tabulation et focus visible.' }
|
||
],
|
||
tp: { type: 'IDE web', url: 'apercu-ide.html', desc: 'Page interactive avec API simulée.', etapes: ['Manipuler le DOM', 'Gérer les événements par délégation', 'Appeler une API avec fetch', 'Gérer les erreurs', 'Typer avec TypeScript', 'Audit Lighthouse accessibilité'] },
|
||
plusLoin: [['MDN · JavaScript (fr)', 'https://developer.mozilla.org/fr/docs/Web/JavaScript'], ['TypeScript Handbook', 'https://www.typescriptlang.org/docs/'], ['RGAA 4', 'https://accessibilite.numerique.gouv.fr/'], ['freeCodeCamp · JavaScript', 'https://www.freecodecamp.org/']] },
|
||
plus: { cas: { titre: '', texte: '' },
|
||
resolu: { enonce: 'Le bouton « Lever la main » affiche parfois « Vous êtes undefinede dans la file » et, pour un élève malvoyant, ne produit aucune annonce.', etapes: [
|
||
['Reproduire', 'Console : POST /api/v1/labs/7/mains → 419\n→ Le serveur refuse la requête (jeton CSRF expiré) mais le code lit quand même le JSON.'],
|
||
['Lire le code', 'const r = await fetch(…); const { position } = await r.json();\n→ Aucun test de r.ok : la réponse d’erreur n’a pas de champ position.'],
|
||
['Corriger la logique', 'if (!r.ok) { afficherErreur(r.status === 419 ? "Session expirée, rechargez la page." : "Envoi impossible."); return; }\n→ Message clair selon l’erreur.'],
|
||
['Corriger l’accessibilité', 'Zone de message : <p id="position" aria-live="polite"></p> et bouton réel avec aria-pressed.\n→ Le lecteur d’écran annonce la position et l’état du bouton.'],
|
||
['Tester', '→ Test automatisé du cas 419 et vérification avec NVDA. Ajout du typage TypeScript de la réponse (interface ReponseMain { position: number }).']
|
||
] },
|
||
erreurs: ['== au lieu de ===', 'innerHTML avec des données utilisateur', 'Ne pas tester response.ok', 'Oublier await', 'Div cliquable au lieu d’un bouton', 'Mises à jour non annoncées aux lecteurs d’écran', 'Charger tout le JavaScript au démarrage'],
|
||
memo: [['Élément', 'Usage'], ['document.querySelector(sel)', 'sélectionner'], ['el.textContent = …', 'afficher du texte sûr'], ['el.addEventListener("click", f)', 'réagir'], ['async / await / try…catch', 'asynchrone'], ['fetch(url, { method, headers, body })', 'requête HTTP'], ['response.ok · response.json()', 'lire la réponse'], ['Promise.all([...])', 'requêtes parallèles'], ['aria-live="polite"', 'annoncer une mise à jour'], ['<script type="module" defer>', 'charger sans bloquer'], ['tsc --noEmit', 'vérifier les types']],
|
||
examen: { titre: 'Sujet type · 20 points', q: ['Manipuler le DOM pour un besoin donné. (5 pts)', 'Écrire un appel fetch avec gestion d’erreur. (5 pts)', 'Expliquer la faille XSS liée à innerHTML. (3 pts)', 'Typer des données avec TypeScript. (3 pts)', 'Corriger quatre problèmes d’accessibilité. (4 pts)'], r: ['querySelector, createElement, textContent', 'async, ok, json, catch', 'interprétation du HTML', 'interface', 'label, bouton, contraste, aria-live'] },
|
||
prof: { deroule: [['Ouverture', 'Naviguer sur un site au clavier seul'], ['Cours', 'Langage et DOM'], ['Pratique', 'Liste dynamique'], ['Cours', 'Asynchrone et fetch'], ['Pratique', 'Appel d’API'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : exercices guidés MDN. Élèves avancés : TypeScript strict, WebSockets.', eval: 'Page rendue et auditée, sujet type.' } },
|
||
exemples: [
|
||
['Égalité stricte', 'Bug de comparaison.', 'console.log("0" == false, "0" === false)', 'true false', '== convertit les types de façon surprenante ; on utilise toujours ===.'],
|
||
['Filtrer et trier', 'Élèves en difficulté.', 'eleves.filter(e => e.moyenne < 10).map(e => e.nom)', "[ 'Martin', 'Petit' ]", 'Deux élèves sous la moyenne.'],
|
||
['Réponse d’erreur', 'Diagnostic dans la console.', 'const r = await fetch("/api/v1/devoirs/999"); console.log(r.ok, r.status)', 'false 404', 'La promesse est résolue même en cas de 404 : il faut tester ok.'],
|
||
['Requêtes parallèles', 'Accélérer le chargement.', 'console.time("p"); await Promise.all([fetch(a), fetch(b)]); console.timeEnd("p")', 'p: 212 ms (séquentiel : 405 ms)', 'Les deux requêtes partent en même temps.'],
|
||
['Vérifier les types', 'Avant la mise en production.', 'npx tsc --noEmit', "src/main.ts:14:23 - error TS2339: Property 'posiiton' does not exist on type 'ReponseMain'.", 'Faute de frappe détectée avant l’exécution.'],
|
||
['Audit d’accessibilité', 'Page de connexion.', 'npx lighthouse https://syllabus.local/connexion --only-categories=accessibility --quiet', 'Accessibility: 82\n- Form elements do not have associated labels\n- Background and foreground colors do not have a sufficient contrast ratio', 'Deux corrections prioritaires : étiquettes et contraste.']
|
||
],
|
||
termes: ['JavaScript', 'const', 'let', '===', 'fonction fléchée', 'DOM', 'querySelector', 'textContent', 'innerHTML', 'addEventListener', 'délégation', 'promesse', 'async', 'await', 'fetch', 'response.ok', 'JSON', 'WebSocket', 'TypeScript', 'interface', 'RGAA', 'WCAG', 'aria-live', 'Lighthouse']
|
||
});
|
||
|
||
reg({
|
||
cours: { id: 'langc', match: /C : bases|pointeurs|compilation|make|Java ou C#|Sockets réseau/i,
|
||
titre: 'Langage C : compilation, pointeurs, mémoire et sockets réseau', duree: '',
|
||
niveau: 'BTS CIEL IR · BUT R&T · BUT Informatique · école d’ingénieurs',
|
||
prerequis: ['Algorithmique de base', 'Binaire et hexadécimal', 'Architecture des ordinateurs'],
|
||
competences: ['BTS CIEL · C05 · Développer un logiciel embarqué ou réseau', 'BUT R&T · Programmer des applications réseau', 'BUT Info · Réaliser un développement proche du système'],
|
||
intro: 'Le noyau Linux, les pilotes, les micrologiciels des objets connectés et une grande partie des outils réseau sont écrits en C. C’est un langage proche de la machine : il donne un contrôle total sur la mémoire, au prix d’une responsabilité totale. Comprendre le C, c’est comprendre ce que font réellement les langages de plus haut niveau, et pourquoi tant de failles de sécurité viennent de débordements de mémoire. Ce module couvre la compilation, les types, les pointeurs, l’allocation, les outils de détection d’erreurs et la programmation réseau avec les sockets.',
|
||
cm: [
|
||
{ t: 'De la source à l’exécutable', d: '', html: 'Un programme C passe par plusieurs étapes. Le <b>préprocesseur</b> traite les directives (<code>#include</code>, <code>#define</code>). Le <b>compilateur</b> traduit chaque fichier .c en fichier objet .o (code machine). L’<b>éditeur de liens</b> assemble les objets et les bibliothèques en un exécutable. <code>gcc -Wall -Wextra -g -o prog main.c</code> compile avec les avertissements et les informations de débogage. Un <b>Makefile</b> décrit les dépendances pour ne recompiler que ce qui a changé.', exemple: '#include <stdio.h>\n\nint main(void) {\n int notes[] = {12, 15, 9, 18};\n int n = sizeof notes / sizeof notes[0];\n int total = 0;\n for (int i = 0; i < n; i++) total += notes[i];\n printf("Moyenne : %.2f\\n", (double) total / n);\n return 0;\n}' },
|
||
{ t: 'Types, mémoire et tableaux', d: '', html: 'Chaque type occupe une taille fixe (<code>char</code> 1 octet, <code>int</code> généralement 4, <code>double</code> 8), consultable avec <code>sizeof</code> ; <code>stdint.h</code> fournit des types de taille garantie (<code>uint8_t</code>, <code>int32_t</code>) indispensables pour les protocoles et l’embarqué. Un <b>tableau</b> est une zone contiguë ; le C ne vérifie <b>jamais</b> les bornes : écrire en dehors corrompt la mémoire voisine. Une <b>chaîne</b> est un tableau de char terminé par <code>\'\\0\'</code> : oublier la place de ce caractère est une erreur classique.' },
|
||
{ t: 'Pointeurs', d: '', html: 'Un <b>pointeur</b> contient une <b>adresse mémoire</b>. <code>&x</code> donne l’adresse de x, <code>*p</code> la valeur pointée. Les pointeurs permettent de passer une variable à une fonction pour qu’elle la modifie (passage par adresse), de parcourir un tableau (le nom d’un tableau se convertit en pointeur vers son premier élément) et de manipuler des structures allouées dynamiquement. Un pointeur non initialisé ou <code>NULL</code> déréférencé provoque une erreur de segmentation (segfault).', exemple: 'void echanger(int *a, int *b) {\n int tmp = *a;\n *a = *b;\n *b = tmp;\n}\n/* appel : echanger(&x, &y); */' },
|
||
{ t: 'Allocation dynamique', d: '', html: 'Les variables locales vivent sur la <b>pile</b> et disparaissent à la fin de la fonction. Pour une taille connue seulement à l’exécution, on alloue sur le <b>tas</b> avec <code>malloc</code> (en vérifiant le retour NULL) et on libère avec <code>free</code>. Trois familles d’erreurs dominent : la <b>fuite</b> (oublier free), l’<b>utilisation après libération</b> (use after free) et le <b>débordement de tampon</b> (buffer overflow), à l’origine d’une grande partie des failles historiques. <b>Valgrind</b> et les sanitizers (<code>-fsanitize=address</code>) les détectent à l’exécution.', attention: 'Les fonctions gets et strcpy n’ont aucune limite de taille : on utilise fgets et snprintf. Les agences de sécurité recommandent désormais les langages sûrs en mémoire (Rust, Go) pour les nouveaux développements critiques.' },
|
||
{ t: 'Structures et modularité', d: '', html: 'Une <b>struct</b> regroupe des champs de types différents : <code>struct eleve { char nom[40]; float moyenne; };</code>. On accède aux champs avec <code>.</code>, ou <code>-></code> à travers un pointeur. Un programme se découpe en modules : un fichier d’en-tête .h déclare les fonctions et les types, le fichier .c les implémente. Les tableaux de structures et les listes chaînées de structures sont la base de nombreux programmes système.' },
|
||
{ t: 'Programmation réseau : sockets', d: '', html: 'L’API des <b>sockets</b> BSD, disponible en C sur tous les systèmes, est celle qu’utilisent tous les serveurs réseau. Un serveur TCP enchaîne <code>socket</code>, <code>bind</code> (adresse et port), <code>listen</code>, <code>accept</code> (bloquant, renvoie une socket par client), puis <code>recv</code> et <code>send</code>. Un client fait <code>socket</code> puis <code>connect</code>. Les nombres transmis doivent être convertis dans l’<b>ordre réseau</b> (big-endian) avec <code>htons</code> et <code>htonl</code>. Pour UDP, <code>sendto</code> et <code>recvfrom</code> échangent des datagrammes sans connexion. On retrouve exactement les états TCP vus avec ss.', exemple: 'int s = socket(AF_INET, SOCK_STREAM, 0);\nstruct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons(5000), .sin_addr.s_addr = htonl(INADDR_ANY) };\nbind(s, (struct sockaddr *)&a, sizeof a);\nlisten(s, 16);\nint c = accept(s, NULL, NULL);\nchar buf[256];\nssize_t n = recv(c, buf, sizeof buf - 1, 0);\nif (n > 0) { buf[n] = \'\\0\'; send(c, "OK\\n", 3, 0); }\nclose(c); close(s);' }
|
||
],
|
||
synthese: ['Préprocesseur → compilateur → éditeur de liens ; gcc -Wall -Wextra -g ; make', 'Tailles de types, stdint.h ; aucune vérification des bornes ; chaînes terminées par \\0', 'Pointeurs : &, *, passage par adresse, NULL', 'malloc/free : fuites, use after free, débordements ; Valgrind et sanitizers', 'struct, en-têtes .h et modules', 'Sockets : socket, bind, listen, accept, recv, send ; htons ; UDP avec sendto/recvfrom'],
|
||
qcm: [
|
||
['Quelle étape assemble les fichiers objets en exécutable ?', ['Préprocesseur', 'Compilateur', 'Éditeur de liens', 'Makefile'], 2, 'Linker.'],
|
||
['Que contient un pointeur ?', ['Une valeur entière quelconque', 'Une adresse mémoire', 'Un fichier', 'Une chaîne'], 1, 'Adresse.'],
|
||
['Taille nécessaire pour stocker « SIO1 » ?', ['4', '5', '8', '1'], 1, '4 caractères + \\0.'],
|
||
['Que fait free ?', ['Alloue', 'Libère la mémoire allouée par malloc', 'Initialise', 'Copie'], 1, 'À appeler une seule fois.'],
|
||
['Pourquoi htons ?', ['Chiffrer', 'Convertir en ordre d’octets réseau', 'Compresser', 'Vérifier'], 1, 'Big-endian.'],
|
||
['Quel outil détecte les fuites mémoire ?', ['gcc', 'Valgrind', 'make', 'gdb uniquement'], 1, 'Et -fsanitize=address.']
|
||
],
|
||
td: [
|
||
{ n: 'Application', c: 'Écrire une fonction qui calcule la moyenne d’un tableau d’entiers et renvoie -1 si le tableau est vide.', r: 'double moyenne(const int *t, size_t n) { if (n == 0) return -1; long s = 0; for (size_t i = 0; i < n; i++) s += t[i]; return (double) s / n; }' },
|
||
{ n: 'Application', c: 'Allouer dynamiquement un tableau de n structures eleve lu au clavier, puis le libérer. Vérifier avec Valgrind.', r: 'struct eleve *t = malloc(n * sizeof *t); if (!t) return 1; … free(t); · valgrind --leak-check=full ./prog → 0 bytes lost.' },
|
||
{ n: 'Approfondissement', c: 'Écrire un client TCP qui se connecte au serveur du cours, envoie « PING » et affiche la réponse, puis observer la connexion avec ss et tcpdump.', r: 'socket, connect sur 127.0.0.1:5000, send, recv · ss -tn state established ; tcpdump -ni lo port 5000 montre SYN, SYN-ACK, ACK puis PSH.' }
|
||
],
|
||
tp: { type: 'Lab VM', url: 'apercu-labs.html', desc: 'Compilation et débogage dans une VM Debian.', etapes: ['Compiler avec gcc et make', 'Pointeurs et passage par adresse', 'Allocation dynamique et Valgrind', 'Provoquer puis corriger un débordement avec -fsanitize=address', 'Serveur et client TCP', 'Observer l’échange avec tcpdump'] },
|
||
plusLoin: [['Beej’s Guide to Network Programming', 'https://beej.us/guide/bgnet/'], ['Documentation GCC', 'https://gcc.gnu.org/onlinedocs/'], ['ANSSI · Règles de programmation pour le développement sécurisé en C', 'https://cyber.gouv.fr/publications/regles-de-programmation-pour-le-developpement-securise-de-logiciels-en-langage-c']] },
|
||
plus: { cas: { titre: '', texte: '' },
|
||
resolu: { enonce: 'Le petit serveur de collecte des capteurs de CO₂, écrit en C, plante de façon aléatoire après quelques heures.', etapes: [
|
||
['Reproduire', 'journalctl -u collecteur | tail -1\ncollecteur.service: Main process exited, code=killed, status=11/SEGV\n→ Erreur de segmentation.'],
|
||
['Compiler avec les sanitizers', 'gcc -g -fsanitize=address -o collecteur collecteur.c && ./collecteur\nERROR: AddressSanitizer: stack-buffer-overflow … WRITE of size 71 … in handle_message collecteur.c:48\n→ Écriture de 71 octets dans un tampon plus petit.'],
|
||
['Lire le code', 'char salle[32];\nstrcpy(salle, message + 5);\n→ Un nom de topic plus long que prévu déborde du tableau.'],
|
||
['Corriger', 'snprintf(salle, sizeof salle, "%s", message + 5);\n+ contrôle de la longueur et rejet des messages invalides.\n→ Plus aucun débordement, même avec des messages malformés.'],
|
||
['Prévenir', '→ Compilation systématique avec -Wall -Wextra -Werror en CI, tests avec -fsanitize=address et un jeu de messages malformés (fuzzing simple). Ce type de faille est exploitable à distance : correction déployée en priorité.']
|
||
] },
|
||
erreurs: ['Ignorer les avertissements du compilateur', 'Oublier la place du \\0', 'Utiliser gets ou strcpy', 'Ne pas vérifier le retour de malloc', 'Oublier free ou libérer deux fois', 'Oublier htons pour le port', 'Ne pas vérifier les retours de recv et send'],
|
||
memo: [['Commande / fonction', 'Usage'], ['gcc -Wall -Wextra -g -o p f.c', 'compiler'], ['make', 'recompiler selon les dépendances'], ['gdb ./p', 'déboguer'], ['valgrind --leak-check=full ./p', 'fuites et erreurs mémoire'], ['-fsanitize=address,undefined', 'détecter les débordements'], ['sizeof', 'taille d’un type ou d’un tableau'], ['malloc / free', 'allouer / libérer'], ['fgets · snprintf', 'lectures et copies bornées'], ['socket bind listen accept', 'serveur TCP'], ['htons · htonl · ntohs', 'ordre réseau']],
|
||
examen: { titre: 'Sujet type · 20 points', q: ['Décrire les étapes de compilation. (3 pts)', 'Écrire une fonction avec pointeurs. (4 pts)', 'Allouer et libérer une structure dynamique. (4 pts)', 'Identifier un débordement de tampon et le corriger. (4 pts)', 'Écrire les étapes d’un serveur TCP. (5 pts)'], r: ['préprocesseur, compilateur, éditeur de liens', 'passage par adresse', 'malloc, vérification, free', 'strcpy → snprintf', 'socket, bind, listen, accept, recv, send'] },
|
||
prof: { deroule: [['Ouverture', 'Même programme en Python et en C : temps et mémoire'], ['Cours', 'Compilation et types'], ['Pratique', 'Pointeurs'], ['Cours', 'Mémoire et sécurité'], ['Pratique', 'Sockets'], ['Clôture', 'Quiz flash']], diff: 'Élèves en difficulté : schémas de mémoire. Élèves avancés : serveur multi-clients avec poll, comparaison avec Rust.', eval: 'Programmes sans erreur Valgrind, sujet type.' } },
|
||
exemples: [
|
||
['Compiler avec avertissements', 'Premier programme.', 'gcc -Wall -Wextra -o moyenne moyenne.c', 'moyenne.c:6:9: warning: unused variable ‘i’ [-Wunused-variable]', 'Les avertissements signalent souvent de vraies erreurs ; on les corrige tous.'],
|
||
['Tailles des types', 'Protocole binaire.', 'printf("%zu %zu %zu\\n", sizeof(char), sizeof(int), sizeof(double));', '1 4 8', 'Pour un format de trame, on préfère uint8_t, uint16_t, uint32_t, de taille garantie.'],
|
||
['Erreur de segmentation', 'Programme qui plante.', 'gdb -q ./prog -ex run -ex bt', 'Program received signal SIGSEGV, Segmentation fault.\n#0 afficher (e=0x0) at eleves.c:21', 'Le pointeur e vaut NULL à la ligne 21 : un retour de recherche non vérifié.'],
|
||
['Détecter une fuite', 'Contrôle qualité.', 'valgrind --leak-check=full ./prog | tail -3', '==4012== LEAK SUMMARY:\n==4012== definitely lost: 1,280 bytes in 32 blocks', '32 blocs jamais libérés : un free manquant dans la boucle.'],
|
||
['Tester le serveur TCP', 'Après compilation.', './serveur & echo "PING" | nc 127.0.0.1 5000', 'OK', 'Le serveur répond ; ss -tln montre LISTEN sur 0.0.0.0:5000.'],
|
||
['Ordre des octets', 'Port mal configuré.', 'printf("%u\\n", (unsigned) htons(5000));', '34835', '5000 = 0x1388 devient 0x8813 en mémoire sur une machine little-endian : sans htons, le serveur écouterait sur le port 34835.']
|
||
],
|
||
termes: ['préprocesseur', 'compilateur', 'éditeur de liens', 'gcc', 'Makefile', 'sizeof', 'stdint.h', 'pointeur', 'adresse', 'NULL', 'segfault', 'malloc', 'free', 'pile', 'tas', 'fuite mémoire', 'use after free', 'débordement de tampon', 'Valgrind', 'AddressSanitizer', 'struct', 'socket', 'bind', 'listen', 'accept', 'htons']
|
||
});
|
||
})();
|