Repository navigation
Securite et RGPD
Statut : 🟡 Brouillon · Màj : 2026-10-14 · Tickets : #306 #397
Cette page fixe les exigences produit. Les consignes de travail qui en découlent (niveaux de risque des changements, revue OWASP du diff, interventions en production) sont dans CLAUDE.md du dépôt ; SECURITY.md ne porte que la procédure de signalement d'une vulnérabilité.
Widoo est une application grand public avec des comptes, du contenu créé par la communauté et des données de sortie.
Actifs : la base, dont les comptes utilisateurs (identité, e-mail, favoris, programmations, historique de sorties, position implicite) ; le contenu créé et les médias (parcours, photos) ; l'intégrité du catalogue de lieux (une fermeture non détectée casse la promesse) ; la disponibilité de la recherche ; les clés des services tiers facturables.
Sont non fiables : les entrées de l'app, les URL, les fichiers envoyés et les textes libres.
Menaces réalistes : vol de compte, contournement des rôles (publier sans modération, éditer le lieu d'un autre), spam de création ou de signalements, injection via textes libres, fuite de secret, abus des API tierces payantes (Mapbox) ou de l'envoi d'e-mails (Brevo : quota, boîtes de tiers inondées) via l'app, upload de contenu illicite, scraping massif du catalogue.
- Jeton Firebase vérifié à chaque requête authentifiée ; rôle relu en base, jamais déduit du jeton.
- Contrôle d'accès par ressource : un utilisateur n'écrit que ses parcours, lieux et programmations ; les statuts de modération ne sont modifiables que par
moderatoretadmin. - Validation Zod de toute entrée et de toute sortie de l'API, refus par défaut en cas d'échec ; textes libres nettoyés (longueur, pas de HTML), URL de réservation limitées à
httpset à une liste de domaines connus ou marquées « lien externe non vérifié ». - Requêtes paramétrées via Drizzle ; aucune concaténation SQL.
- Limitation de débit : recherche 60/min par IP, création de lieu 20/jour par compte, signaux 30/jour par compte, upload 50/jour, lien de connexion un envoi par adresse toutes les 30 s et 5/heure, 30/heure par IP (D-090).
- Clés tierces : les clés publiques embarquées (Mapbox, Firebase) sont restreintes aux bundle ids et aux URL ; Directions et Geocoding passent par l'API avec limitation de débit, jamais depuis l'app pour les usages facturables ; Geocoding sans cache serveur au MVP (résultats temporaires de Mapbox, qu'il est interdit de conserver, D-081).
- Upload : URL signées, type et taille vérifiés (10 Mo max), redimensionnement serveur qui réencode l'image (supprime les métadonnées EXIF, dont la position GPS).
- Secrets dans Secret Manager. Aucun secret, jeton, cookie ni donnée personnelle réelle (e-mail, nom, photo…) dans Git, le code de l'app, les tests, les logs, les prompts, rapports et transcriptions d'agents ou les sorties de commande ; les recettes utilisent des données fictives.
- Staging et production séparés (projets ou instances distincts), données fictives en staging ; voir Architecture-Technique › Environnements.
- Logs structurés sans donnée personnelle ; identifiant utilisateur opaque uniquement.
- Migrations Drizzle additives, versionnées et testées.
- Sauvegardes Cloud SQL quotidiennes, PITR 7 jours, restauration testée avant lancement.
- Dépendances : Dependabot, audit en CI. Une alerte bloque si elle est critique ou élevée, ou moyenne avec un impact plausible sur l'accès, les secrets ou les données ; une alerte faible devient une dette suivie en issue.
| Donnée | Finalité | Base légale | Durée |
|---|---|---|---|
| E-mail, prénom, avatar | compte, notifications | contrat | vie du compte + 30 jours |
| Identifiant Firebase | authentification | contrat | idem |
| Favoris, programmations, historique de parcours | service | contrat | idem ; historique purgeable par l'utilisateur |
| Position de l'appareil | centrage de la carte, tri par distance | consentement (permission OS) | jamais stockée côté serveur |
| Jeton push | rappels | consentement | jusqu'à révocation |
| Signaux de vérification | qualité du contenu | intérêt légitime | 24 mois, puis anonymisés |
| Événements analytics | amélioration produit | consentement (bannière au premier lancement, refus possible) | 12 mois, PostHog UE |
| Crash reports | fiabilité | intérêt légitime | 90 jours |
| Langue, préférences de notification (D-062) | service | contrat | vie du compte + 30 jours |
| Changement d'e-mail en attente (D-062) | confirmer la nouvelle adresse | contrat | jusqu'à confirmation, annulation ou expiration du lien |
| Messages au support (D-063) | répondre | intérêt légitime, contrat | à fixer (3 ans après le dernier échange proposés) |
- Aucune position n'est envoyée au serveur : la recherche reçoit une zone de carte, pas la position exacte ; le tri par distance est calculé côté client.
- Photos : l'utilisateur atteste des droits ; pas de reconnaissance faciale ; suppression immédiate à la demande.
- Droits : accès, rectification (profil), effacement (suppression de compte dans l'app), portabilité (export JSON dans Réglages › Confidentialité, E-19, D-063).
- Mineurs : service réservé aux 16 ans et plus (CGU) ; pas de vérification d'âge au MVP.
- Sous-traitants : Google Cloud (UE), Firebase, Mapbox, PostHog (UE), Sentry (UE), Expo. Registre des traitements à tenir ; politique de confidentialité et conditions d'utilisation rédigées en brouillon le 2026-09-28 (Politique-de-confidentialite, Conditions-d-utilisation, D-063), à relire par un juriste avant la soumission aux stores. À ajouter à la liste : RevenueCat (abonnements), le fournisseur de traduction automatique des parcours et le prestataire d'envoi d'e-mails.
- Réglages obligatoires des sous-traitants, à vérifier sur chaque nouvelle organisation ou nouveau projet :
- PostHog : organisation et projets sur PostHog Cloud UE (
eu.posthog.com), « Discard client IP data » activé ; l'assistant PostHog AI n'est pas utilisé. - Sentry : organisation en région UE (DSN en
.de.sentry.io) ; « Require Data Scrubber », « Require Using Default Scrubbers » et « Prevent Storing of IP Addresses » activés au niveau de l'organisation ; au niveau de l'organisation également, règles avancées « Remove » surcontexts.app.device_app_hash(empreinte d'appareil ajoutée par le SDK natif iOS) et$user.geo.**(ville et pays que Sentry déduit de l'IP de l'émetteur avant de la jeter), qui s'appliquent d'office à tout nouveau projet. Vérifié le 2026-09-24 surwidoo-mobile(#108) etwidoo-api(#109).
- PostHog : organisation et projets sur PostHog Cloud UE (
- Toute nouvelle donnée personnelle, table, export ou sous-traitant revient au cadrage avant tout code : finalité, durée, information de la personne, transfert éventuel ; le tableau ci-dessus est mis à jour en conséquence.
Charte dans Creation-et-Moderation. Signalement d'un parcours pour contenu inapproprié disponible depuis la fiche (motif, traité dans A-02). Journal immuable des actions de modération.
Un risque résiduel qu'Ilan accepte lors d'une PR de niveau 2 ou 3 est consigné dans cette section, et le Design-Changelog l'annonce. Une entrée par risque, au format suivant :
### <Risque, en une ligne>
- **Contexte :** …
- **Raison de l'acceptation :** …
- **PR :** [#N](https://github.com/Inprogress-Agency/widoo-app/pull/N)
- **Accepté par :** …
- **Date :** AAAA-MM-JJ
- **Date de révision :** AAAA-MM-JJ-
Contexte : l'API vérifie la signature, l'audience, l'émetteur et l'expiration de chaque jeton, mais pas sa révocation (
verifyIdTokensanscheckRevoked). Un compte désactivé ou déconnecté depuis la console Firebase garde l'accès jusqu'à l'expiration de son jeton, une heure au plus. -
Raison de l'acceptation : vérifier la révocation coûte un appel à Firebase à chaque requête ; un compte supprimé par l'utilisateur est refusé immédiatement par la base (
users.deleted_at). - PR : #95
- Accepté par : Paul, par délégation d'Ilan
- Date : 2026-09-24
- Date de révision : avant la soumission aux stores
-
Contexte : en staging, l'API et le job de migration utilisent le même utilisateur Postgres intégré (
cloudsqlsuperuser), qui peut modifier le schéma à l'exécution. - Raison de l'acceptation : staging seulement, données fictives ; un rôle de migration et un rôle d'exécution séparés sont à créer pour la production.
- PR : #111
- Accepté par : Paul, par délégation d'Ilan
- Date : 2026-09-24
- Date de révision : avant la mise en place de la production
-
Contexte : l'URL de la base utilise
sslmode=require: la connexion est chiffrée (l'instance refuse le clair) mais le client ne vérifie pas le certificat du serveur ; elle ne passe que par le réseau privé du projet. - Raison de l'acceptation : instance sans IP publique, joignable seulement depuis le VPC ; vérification du certificat (CA de l'instance) à ajouter pour la production.
- PR : #111
- Accepté par : Paul, par délégation d'Ilan
- Date : 2026-09-24
- Date de révision : avant la mise en place de la production
-
Contexte : le ticket #24 prévoyait un connecteur VPC ; Cloud Run utilise la sortie VPC directe vers le sous-réseau
widoo-run. - Raison de l'acceptation : même accès privé à Cloud SQL, sans les 10 € par mois du connecteur.
- PR : #111
- Accepté par : Paul, par délégation d'Ilan
- Date : 2026-09-24
- Date de révision : avant la mise en place de la production
-
Contexte : exception nommée à la règle des alertes de Propriétés obligatoires, qui reste en place. Depuis le 2026-10-05,
pnpm audit --audit-level=highéchoue sur toutes les PR du dépôt à cause de deux alertes élevées sans version corrigée en amont :-
node-forge1.4.0, GHSA-86w9-cpqp-85rv (CVE-2026-85393, CVSS 7.5) : la vérification de signature RSA PKCS#1 v1.5 accepte des éléments DigestAlgorithm imbriqués en trop, ce qui permet de forger une signature sur une clé à exposant public faible (e=3). N'entre que parapps/mobile > expo > @expo/cli. -
braces3.0.3, GHSA-vfj7-8cjw-p6xm (CVE-2026-93687, CVSS 7.5) : déni de service par épuisement de pile sur un motif très imbriqué. Entre partailwindcss > chokidaret parmicromatch(Metro, Tailwind, ESLint).
Aucun des deux n'atteint l'app livrée (absents des source maps des bundles iOS et Android exportés) ni l'image de l'API (absents de la sortie copiée dans l'image au rejeu de la construction). Exposition : postes de développement, CI, build EAS.
-
-
Raison de l'acceptation : au pire, le plantage d'un outil de développement.
@expo/cline vérifie avecnode-forgeaucune signature venue de l'extérieur : il signe et vérifie ses propres manifestes, avec des clés en e=65537, et le projet n'utilise niexpo-updatesni signature de code. Les motifs donnés àbracesviennent de la configuration du dépôt. Aucun contournement :node-forge1.4.0 etbraces3.0.3 sont les dernières versions publiées,@expo/cli(57.0.27 comme 58.1.2) et Tailwind 3 en dépendent toujours, et les correctifs proposés en amont ne sont pas fusionnés (digitalbazaar/forge#1152, micromatch/braces#75).-
Portée : ces deux identifiants seulement (
auditConfig.ignoreGhsasdanspnpm-workspace.yaml) ; l'audit continue de bloquer toute autre alerte élevée ou critique. - Limites : pnpm ne sait ni restreindre l'exception à un chemin de dépendance ni lui donner une date d'expiration. La révision se tient donc à la main, par le ticket #280. Un identifiant ignoré l'est aussi si le paquet arrive par un autre chemin, par exemple par le site web en cours de développement.
-
Écarté : attendre les correctifs en bloquant toutes les fusions, sans date ;
--ignore-unfixable, qui ignorerait toute alerte sans correctif, présente ou future ; baisser le seuil de l'audit.
-
Portée : ces deux identifiants seulement (
-
PR : #279 (ticket de l'exception ; PR à venir, à compléter à la fusion)
-
Accepté par : Paul Thiberville, par délégation d'Ilan
-
Date : 2026-10-05
-
Date de révision : 2026-11-05 (#280), ou plus tôt dès la publication d'une version de
node-forge> 1.4.0 ou debraces> 3.0.3. Condition de retrait, alerte par alerte : la version corrigée est publiée et atteignable par les dépendances du dépôt (au besoin par une version d'@expo/cliou de Tailwind qui l'accepte, ou par unoverridesde pnpm) ; l'identifiant sort alors deignoreGhsaset l'audit repasse sans lui.
Manques connus, qui ne sont pas des risques acceptés, à combler avant la soumission aux stores :
- la réinscription après suppression est permise (décision d'Ilan, D-027), mais le code refuse encore la même identité jusqu'à la purge : #128 corrige ;
- la purge des comptes supprimés à 30 jours et la suppression du compte Firebase ne sont pas implémentées (durées du tableau Données personnelles) ;
- le consentement à la mesure d'audience ne peut pas encore être retiré dans l'app : le retrait est dessiné (interrupteur « Mesure d'audience » des Réglages, E-09, avec ou sans compte, D-060) mais pas implémenté (#107, #188) ;
- la politique de confidentialité reste à rédiger et à publier : la sheet de consentement du premier lancement (E-18, D-060) et les Réglages y renvoient (#107).
Des risques résiduels sont encore déclarés dans les PR de l'epic #13, en attente de décision : #91 (fusionnée), #96 et #97 (gardes d'authentification déclarées route par route), #107 à #109, #112.
Revue de sécurité sur commit figé, test de restauration, politique de confidentialité et CGU publiées, formulaire de contact RGPD, vérification des permissions demandées par l'app (position « quand l'app est utilisée » seulement, notifications, photos).
Produit
- Vision-Produit
- Perimetre-MVP
- Personas-et-Parcours-Utilisateurs
- Ecrans
- Filtres-et-Recherche
- Orchestration
- Creation-et-Moderation
- Admin-Web
- Compte-et-Monetisation
- Direction-Artistique
Technique
- Modele-de-Donnees
- Architecture-Technique
- Site-Web
- API
- Securite-et-RGPD
- Conditions-d-utilisation
- Politique-de-confidentialite
- Analytics
Pilotage