Commit 93c5e8c2 authored by Kourser's avatar Kourser
Browse files

Documentation : le bac à sable public n'était décrit nulle part

SECURITY.md ne disait rien de la surface la plus exposée du produit : une
route publique, non authentifiée, qui crée des organisations et des
exercices. Le code le disait — « son activation est un acte
d'exploitation qui demande une revue de sécurité » — mais le document
que l'exploitant lit AVANT de l'ouvrir restait muet.

Une section décrit maintenant ce qu'elle expose et ce qui la retient :
organisation éphémère purgée indépendamment du visiteur, plafond global,
liste blanche de kits en dur, aucune donnée personnelle, plafonds par IP
route par route. Les chiffres ont été relus contre le code.

Ce que partager une séance implique y est dit en clair, puisque c'est
nouveau : l'adresse porte 128 bits d'aléa et vaut autorisation, mais
aucun lien de jeu n'est distribué par cette page — les jetons ne sont
émis qu'à la prise d'un rôle, et montrés à ce seul visiteur. Partager
l'adresse d'une séance ne distribue pas les accès de tout le monde.

Les deux gestes d'administration y figurent avec leur raison d'être,
dont celle qui n'est pas devinable : prolonger repousse aussi les jetons
des joueurs, faute de quoi la page resterait ouverte pendant qu'ils
seraient refusés à la porte.

DEUX CHOSES PORTENT LE NOM DE « BAC À SABLE » dans ce document — le
confinement de la simulation, et cette route publique. Un renvoi le dit
en tête : l'une est une propriété d'étanchéité, l'autre une surface
d'exposition, et les confondre serait fâcheux.

Le README n'en parlait qu'incidemment, dans une note sur le plafonnement.
Il a désormais sa section, et son index de documentation cite le cahier
de test et la recette, qui n'y figuraient pas.

Restait deux affirmations devenues fausses : la recette compte 160 cas et
non 130, et elle ne laisse plus d'organisation éphémère derrière elle
depuis que la console sait en fermer une. Le compte rendu de campagne
garde en revanche son bilan d'origine — 104 cas sur la version testée :
c'est un relevé daté, pas un état courant, et le réécrire le falsifierait.

Co-Authored-By: default avatarClaude Opus 5 <noreply@anthropic.com>
parent 203365c3
Loading
Loading
Loading
Loading
+18 −1
Changes for README.md: 18 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@ Plateforme **open-source** d'organisation et d'animation d'**exercices de gestio
- [Découpage du MVP](docs/mvp-decoupage.md)
- [Spécification technique (lots 0–3)](docs/spec-technique-lots-0-3.md)
- [Dépendances & licences](DEPENDENCIES.md)
- [Cahier de test](docs/cahier-de-test.md) — écrans, boutons, cas métier attendus
- [Recette automatisée](scripts/README.md) — `pnpm recette`, 160 cas contre une instance en marche
- Maquette cliquable : [docs/maquettes/prototype.html](docs/maquettes/prototype.html)

## Prérequis
@@ -216,9 +218,24 @@ cat sauvegarde.sql | docker compose exec -T db psql -U enkadenn enkadenn
Les documents/pièces jointes sont stockés en base (texte) au stade actuel ; aucun volume de
fichiers séparé n'est requis.

## Découverte sans compte (bac à sable public)

**Fermé par défaut.** Ouvert depuis la console d'instance, il expose une route publique et non
authentifiée qui crée une organisation éphémère et un exercice jouable, sans qu'aucun compte ni
aucune information ne soit demandé. Le visiteur reçoit l'adresse d'une **séance** qu'il transmet
à ses collègues : chacun l'ouvre et prend un rôle libre de la cellule.

Deux scénarios sont proposés — une PME logistique frappée par un rançongiciel, une commune
menacée par une crue. Tout est supprimé à l'échéance de la séance.

C'est la surface la plus exposée du produit : l'ouvrir mérite d'être décidé, et les garde-fous
comme leurs limites sont décrits dans [SECURITY.md](SECURITY.md#bac-à-sable-public-découverte-sans-compte).
La console liste les séances en cours et permet de les prolonger ou de les fermer.

## Sécurité & conformité

Confinement, cloisonnement multi-tenant, RGPD et journal d'audit : voir [SECURITY.md](SECURITY.md).
Confinement, cloisonnement multi-tenant, bac à sable public, RGPD et journal d'audit : voir
[SECURITY.md](SECURITY.md).

## Licence

+62 −0
Changes for SECURITY.md: 62 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -2,6 +2,11 @@

## Confinement (« bac à sable »)

> Deux choses portent ce nom dans ce document. **Ici** : le confinement de la simulation,
> c'est-à-dire le fait que rien n'en sort. Plus bas, [le bac à sable
> public](#bac-à-sable-public-découverte-sans-compte) désigne la route anonyme de découverte,
> qui est une surface d'exposition et non une propriété d'étanchéité.

Enkadenn est un outil d'**entraînement**. L'environnement de simulation est **hermétique** :

- Aucun email, SMS, message ou publication n'est réellement envoyé à l'extérieur. « Envoyer »
@@ -114,6 +119,63 @@ Le mot de passe n'est **jamais renvoyé** à la console, même chiffré : elle n
savoir s'il est posé (`hasPassword`). Il est également exclu du journal d'audit, qui ne
consigne que les *noms* des champs modifiés.

## Bac à sable public (découverte sans compte)

À ne pas confondre avec le confinement décrit en tête de ce document. Il s'agit ici d'une
**route publique, non authentifiée, qui crée des organisations et des exercices** : c'est la
surface la plus exposée du produit. Elle est **fermée par défaut**, et l'ouvrir est un acte
d'exploitation qui mérite d'être décidé, pas un réglage anodin.

- **Une organisation éphémère par session**, isolée par le même mécanisme que les
  organisations clientes. Elle porte une date de fin, n'apparaît dans aucune liste
  d'organisations — ce n'est pas une cliente — et est supprimée en cascade à son échéance par
  un service dédié. La purge **ne dépend pas du visiteur** : il peut fermer son onglet,
  oublier, couper son réseau, l'échéance tombe quand même.
- **Plafond global d'organisations vivantes**, réglable. Au-delà, refus explicite : le
  visiteur doit comprendre qu'il peut revenir, pas croire que le service est cassé. Sans ce
  plafond, la route serait une invitation à remplir la base.
- **Liste blanche de kits, en dur dans le code** (`kits/catalogue.ts`), et volontairement pas
  un drapeau en base : la route publique ne peut désigner qu'un kit **fourni**, jamais un kit
  privé d'une organisation, même par erreur de requête. Toute ouverture passe par une
  modification de code, donc par une revue.
- **Aucune donnée personnelle** : ni nom, ni adresse, ni organisation ne sont demandés. Le
  journal d'instance enregistre la création **sans adresse IP** — la limitation de débit en
  garde une empreinte le temps de sa fenêtre, ce journal-ci est conservé.
- **Routes plafonnées par IP** : 3 créations/h, 60 lectures d'état/10 min, 120 lectures d'une
  page de séance/10 min, 20 prises de rôle/10 min. La lecture est large à dessein — c'est la
  page qu'une cellule entière ouvre en même temps ; le geste coûteux est la création.

### Partager une séance

L'adresse d'une séance (`/decouverte/<identifiant>`) est faite pour être transmise : une crise
se joue à plusieurs. Ce que cela implique, en clair :

- **L'identifiant porte 128 bits d'aléa** et vaut autorisation, comme un lien joueur. Quiconque
  l'obtient peut prendre un rôle libre sur cette séance. Il meurt avec elle.
- **Aucun lien de jeu n'est distribué par cette page.** Les jetons d'accès ne sont émis qu'à la
  **prise** d'un rôle, un par un, et ne sont montrés qu'à celui qui le prend — seule leur
  empreinte est stockée, comme pour tout lien joueur. Partager l'adresse d'une séance ne
  distribue donc pas les accès de tout le monde.
- **Un rôle pris ne peut pas être repris** : le reprendre reviendrait à remettre un jeton neuf
  sur le personnage, donc à mettre dehors celui qui l'occupe.

### Administration des séances

La console d'instance liste les séances vivantes et offre **deux gestes, tracés tous les deux**
au journal d'instance avec leur auteur :

- **Prolonger** (5 à 240 min, jamais au-delà de 24 h au total). Les jetons des joueurs sont
  repoussés **avec** l'échéance : ils portent leur propre date d'expiration, et prolonger la
  seule organisation laisserait la page de séance ouverte pendant que les joueurs se feraient
  refuser à la porte. La borne haute empêche de transformer par la console une organisation
  éphémère en organisation permanente.
- **Fermer** sur-le-champ, avec confirmation : des joueurs sont peut-être en train de jouer.
  C'est le geste de la purge, avancé dans le temps.

Ces routes **ne peuvent atteindre que des organisations éphémères**. L'identifiant d'une
organisation cliente y reçoit un 404 : une route de fermeture capable de supprimer un client
serait une porte dérobée dans la console.

## Appartenances et portées d'autorité

Un compte est une **identité globale** ; c'est le lien `Membership` qui le rattache à une
+10 −5
Changes for docs/recette-resultats.md: 10 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -112,11 +112,16 @@ Les sondes de cette campagne sont versées au dépôt et exécutables d'une comm
pnpm recette
```

Elles couvrent 130 cas — les 104 de la campagne manuelle, plus ceux que
l'écriture du harnais a fait apparaître : refus d'une vérification des accès sans
participant, survie d'un écran de salle à la révocation, distinction entre lecture
et écriture en session d'observation. Le détail d'usage est dans
[scripts/README.md](../scripts/README.md).
Le harnais partait de 130 cas — les 104 de cette campagne, plus ceux que son
écriture a fait apparaître : refus d'une vérification des accès sans participant,
survie d'un écran de salle à la révocation, distinction entre lecture et écriture
en session d'observation.

**Il en compte 160 aujourd'hui**, et ce nombre bouge avec le produit : ce compte
rendu fige une campagne, pas la couverture. Les trente cas ajoutés depuis
couvrent le partage d'une séance de découverte, l'administration de ces séances
depuis la console, et ce que le joueur reçoit en arrivant. Le détail d'usage est
dans [scripts/README.md](../scripts/README.md).

## 6. Remise en état

+7 −5
Changes for scripts/README.md: 7 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -24,10 +24,10 @@ docker compose up -d && docker compose exec app node_modules/.bin/ts-node apps/a
| `02-exercices` | Cycle de vie complet, duplication, exports, rapport |
| `03-joueur` | Espace joueur, import d'annuaire, invitations |
| `04-kits` | Éditeur de kit et imports CSV, vérification des accès, écran de salle |
| `05-instance` | Console d'instance, inscription, bac à sable, suspension, observation |
| `05-instance` | Console d'instance, inscription, bac à sable public et administration de ses séances, suspension, observation |

**Ce que la recette laisse derrière elle** — rien, sauf deux choses qui ne se
défont pas depuis l'API :
**Ce que la recette laisse derrière elle** — rien, sauf une chose qui ne se
défait pas depuis l'API :

- le **second facteur** de l'administrateur d'instance, que le lot 05 enrôle
  faute d'en connaître un. Le remettre à zéro :
@@ -38,8 +38,10 @@ défont pas depuis l'API :

  Pour l'éviter, fournir un secret existant : `RECETTE_TOTP=… node scripts/recette/executer.mjs`.

- l'**organisation éphémère** née de la session de découverte, que le produit
  purge de lui-même à son échéance et que la console ne liste pas.
La séance de découverte ouverte par le lot 05 est refermée par lui, avec le
geste que la console offre désormais : il ne reste plus d'organisation éphémère
en attente de purge.


**Réglages** — `RECETTE_BASE` (défaut `http://localhost:3000/api`),
`RECETTE_MOT_DE_PASSE`, `RECETTE_ROOT`, `RECETTE_TOTP`.