From 5836be465c2a5b0022599c42373cf3e2b6e602f3 Mon Sep 17 00:00:00 2001 From: OpenAI Date: Fri, 17 Apr 2026 14:17:56 +0000 Subject: [PATCH] Initial SOC2 cockpit repository --- .gitignore | 4 + README.md | 101 +++ .../soc2/registry/auditor-pack-index.json | 44 + .../soc2/registry/evidence-manifest.json | 150 ++++ compliance/soc2/registry/soc2-registry.json | 787 ++++++++++++++++++ index.html | 16 + package.json | 21 + src/App.jsx | 333 ++++++++ src/components/DataTab.jsx | 70 ++ src/components/EvidenceEditor.jsx | 88 ++ src/components/EvidenceLibrary.jsx | 57 ++ src/components/MilestoneCard.jsx | 67 ++ src/components/PillarOverviewCard.jsx | 31 + src/components/ui/primitives.jsx | 53 ++ src/features/hooks/useSoc2Registry.js | 191 +++++ src/lib/selectors.js | 150 ++++ src/lib/utils.js | 47 ++ src/lib/validation.js | 163 ++++ src/lib/view.js | 9 + src/main.jsx | 10 + src/model/schema.js | 70 ++ src/model/seed.js | 642 ++++++++++++++ src/model/stages.js | 28 + src/styles.css | 119 +++ 24 files changed, 3251 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 compliance/soc2/registry/auditor-pack-index.json create mode 100644 compliance/soc2/registry/evidence-manifest.json create mode 100644 compliance/soc2/registry/soc2-registry.json create mode 100644 index.html create mode 100644 package.json create mode 100644 src/App.jsx create mode 100644 src/components/DataTab.jsx create mode 100644 src/components/EvidenceEditor.jsx create mode 100644 src/components/EvidenceLibrary.jsx create mode 100644 src/components/MilestoneCard.jsx create mode 100644 src/components/PillarOverviewCard.jsx create mode 100644 src/components/ui/primitives.jsx create mode 100644 src/features/hooks/useSoc2Registry.js create mode 100644 src/lib/selectors.js create mode 100644 src/lib/utils.js create mode 100644 src/lib/validation.js create mode 100644 src/lib/view.js create mode 100644 src/main.jsx create mode 100644 src/model/schema.js create mode 100644 src/model/seed.js create mode 100644 src/model/stages.js create mode 100644 src/styles.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9451024 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.DS_Store +*.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e5612d --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# Chezlepro SOC 2 Type II Cockpit + +Cockpit web React orienté **conformité opérationnelle SOC 2 Type II** pour Chezlepro Inc. + +L’application fournit : + +- un **radar des 5 piliers** SOC 2 ; +- un suivi **pondéré des jalons** ; +- une **bibliothèque de preuves** éditable ; +- une **salle auditeur** avec paquets de revue ; +- une **source de vérité JSON** persistée localement ; +- des **exports/manifests** pour préparer un dépôt Git versionné. + +## Architecture + +```text +src/ + App.jsx + styles.css + model/ + schema.js + seed.js + stages.js + lib/ + utils.js + validation.js + selectors.js + view.js + features/ + hooks/ + useSoc2Registry.js + components/ + PillarOverviewCard.jsx + MilestoneCard.jsx + EvidenceLibrary.jsx + EvidenceEditor.jsx + DataTab.jsx + ui/ + primitives.jsx +``` + +## Principes de conception + +- **Modèle** : constantes, schéma, seed data, états des contrôles. +- **Lib métier** : normalisation, validation, calculs, sélecteurs. +- **Hook de registre** : persistance locale, import/export, mutations synchronisées. +- **Composants** : interface séparée du moteur métier. + +Le cockpit est prêt à évoluer vers une vraie source de vérité Git/Forgejo : + +- chemin recommandé pour le registre : `compliance/soc2/registry/soc2-registry.json` +- pièces jointes recommandées : `compliance/soc2/evidence/` + +## Lancer le projet + +```bash +npm install +npm run dev +``` + +Build de production : + +```bash +npm run build +npm run preview +``` + +## Dépendances + +- React +- Vite +- Recharts +- Framer Motion +- Lucide React + +## Source de vérité + +Le cockpit persiste son état dans `localStorage` pour le prototype. + +La prochaine étape naturelle est de remplacer cette persistance par : + +1. un fichier JSON versionné dans un dépôt Forgejo ; +2. une API légère de lecture/écriture ; +3. éventuellement une ingestion automatique depuis Icinga, Keycloak et les registres internes. + +## Limites actuelles + +- pas d’authentification applicative ; +- pas de backend ; +- pas de gestion de fichiers binaires de preuves ; +- pas encore de synchronisation distante. + +## Recommandation d’évolution + +Séquence recommandée : + +1. brancher le registre JSON versionné ; +2. ajouter gestion de pièces jointes ; +3. ajouter règles de validation plus strictes ; +4. ajouter API backend ; +5. brancher les collecteurs automatiques de preuves. diff --git a/compliance/soc2/registry/auditor-pack-index.json b/compliance/soc2/registry/auditor-pack-index.json new file mode 100644 index 0000000..dc04a95 --- /dev/null +++ b/compliance/soc2/registry/auditor-pack-index.json @@ -0,0 +1,44 @@ +[ + { + "id": "pack-1", + "title": "System Description", + "status": "ready", + "requiredEvidence": [ + "ev-001", + "ev-011" + ] + }, + { + "id": "pack-2", + "title": "Management Assertion", + "status": "draft", + "requiredEvidence": [ + "ev-012" + ] + }, + { + "id": "pack-3", + "title": "Controls ↔ Evidence Matrix", + "status": "ready", + "requiredEvidence": [ + "ev-012" + ] + }, + { + "id": "pack-4", + "title": "Exceptions & Remediation Register", + "status": "draft", + "requiredEvidence": [ + "ev-009", + "ev-010" + ] + }, + { + "id": "pack-5", + "title": "Access & Privileged Activity Pack", + "status": "draft", + "requiredEvidence": [ + "ev-002" + ] + } +] \ No newline at end of file diff --git a/compliance/soc2/registry/evidence-manifest.json b/compliance/soc2/registry/evidence-manifest.json new file mode 100644 index 0000000..8de4be3 --- /dev/null +++ b/compliance/soc2/registry/evidence-manifest.json @@ -0,0 +1,150 @@ +[ + { + "id": "ev-001", + "title": "Politique de sécurité de l’information v1.4", + "pillar": "security", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "sec-governance" + ], + "updatedAt": "2026-04-09" + }, + { + "id": "ev-002", + "title": "Export Keycloak – MFA et rôles administrateurs", + "pillar": "security", + "auditorReady": false, + "visibility": "internal", + "linkedMilestones": [ + "sec-iam", + "con-access" + ], + "updatedAt": "2026-03-29" + }, + { + "id": "ev-003", + "title": "Journal des changements Forgejo / Pull Requests", + "pillar": "security", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "sec-change", + "pi-inputs" + ], + "updatedAt": "2026-04-15" + }, + { + "id": "ev-004", + "title": "Rapport Icinga – disponibilité des services", + "pillar": "availability", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "ava-monitoring", + "ava-incidents", + "sec-logging" + ], + "updatedAt": "2026-04-16" + }, + { + "id": "ev-005", + "title": "Procédure de sauvegarde et test de restauration", + "pillar": "availability", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "ava-backup", + "ava-resilience" + ], + "updatedAt": "2026-04-12" + }, + { + "id": "ev-006", + "title": "Cartographie des flux critiques de traitement", + "pillar": "processing-integrity", + "auditorReady": false, + "visibility": "internal", + "linkedMilestones": [ + "pi-flows", + "pi-audit" + ], + "updatedAt": "2026-04-04" + }, + { + "id": "ev-007", + "title": "Registre de classification des données", + "pillar": "confidentiality", + "auditorReady": false, + "visibility": "internal", + "linkedMilestones": [ + "con-classification", + "con-lifecycle" + ], + "updatedAt": "2026-03-18" + }, + { + "id": "ev-008", + "title": "Registre des renseignements personnels", + "pillar": "privacy", + "auditorReady": false, + "visibility": "internal", + "linkedMilestones": [ + "pri-mapping", + "pri-rules", + "pri-retention" + ], + "updatedAt": "2026-04-02" + }, + { + "id": "ev-009", + "title": "Journal des incidents et post-mortems", + "pillar": "security", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "sec-ir", + "ava-incidents" + ], + "updatedAt": "2026-04-10" + }, + { + "id": "ev-010", + "title": "Registre des fournisseurs critiques", + "pillar": "confidentiality", + "auditorReady": false, + "visibility": "internal", + "linkedMilestones": [ + "con-thirdparty", + "pri-incidents" + ], + "updatedAt": "2026-03-27" + }, + { + "id": "ev-011", + "title": "Inventaire des services in-scope", + "pillar": "availability", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "ava-inventory", + "sec-governance" + ], + "updatedAt": "2026-04-11" + }, + { + "id": "ev-012", + "title": "Matrice contrôles ↔ preuves", + "pillar": "security", + "auditorReady": true, + "visibility": "auditor", + "linkedMilestones": [ + "sec-governance", + "sec-change", + "con-review", + "pri-review", + "pi-audit" + ], + "updatedAt": "2026-04-16" + } +] \ No newline at end of file diff --git a/compliance/soc2/registry/soc2-registry.json b/compliance/soc2/registry/soc2-registry.json new file mode 100644 index 0000000..0920f91 --- /dev/null +++ b/compliance/soc2/registry/soc2-registry.json @@ -0,0 +1,787 @@ +{ + "schemaVersion": "1.0.0", + "appContext": { + "orgName": "Chezlepro Inc.", + "programName": "SOC 2 Type II Operational Cockpit", + "observationWindow": { + "start": "2026-01-01", + "end": "2026-06-30", + "targetReadiness": 85 + } + }, + "evidence": [ + { + "id": "ev-001", + "title": "Politique de sécurité de l’information v1.4", + "category": "Policy", + "pillar": "security", + "owner": "Direction", + "freshness": "Valide", + "updatedAt": "2026-04-09", + "auditorReady": true, + "visibility": "auditor", + "source": "Wiki / Gouvernance", + "tags": [ + "policy", + "security", + "governance" + ], + "linkedMilestones": [ + "sec-governance" + ], + "summary": "Politique cadre de sécurité approuvée et versionnée." + }, + { + "id": "ev-002", + "title": "Export Keycloak – MFA et rôles administrateurs", + "category": "Access", + "pillar": "security", + "owner": "Ops", + "freshness": "À revoir", + "updatedAt": "2026-03-29", + "auditorReady": false, + "visibility": "internal", + "source": "Keycloak / Export CSV", + "tags": [ + "iam", + "mfa", + "admin" + ], + "linkedMilestones": [ + "sec-iam", + "con-access" + ], + "summary": "État courant des administrateurs, rôles et MFA; manque la revue signée." + }, + { + "id": "ev-003", + "title": "Journal des changements Forgejo / Pull Requests", + "category": "Change", + "pillar": "security", + "owner": "Ops", + "freshness": "Valide", + "updatedAt": "2026-04-15", + "auditorReady": true, + "visibility": "auditor", + "source": "Forgejo / Repositories", + "tags": [ + "change", + "git", + "approval" + ], + "linkedMilestones": [ + "sec-change", + "pi-inputs" + ], + "summary": "Historique des changements, approbations et validations post-déploiement." + }, + { + "id": "ev-004", + "title": "Rapport Icinga – disponibilité des services", + "category": "Monitoring", + "pillar": "availability", + "owner": "Ops", + "freshness": "Valide", + "updatedAt": "2026-04-16", + "auditorReady": true, + "visibility": "auditor", + "source": "Icinga2 / Monitoring", + "tags": [ + "availability", + "monitoring", + "uptime" + ], + "linkedMilestones": [ + "ava-monitoring", + "ava-incidents", + "sec-logging" + ], + "summary": "Disponibilité, incidents et tendances de capacité sur la fenêtre d’observation." + }, + { + "id": "ev-005", + "title": "Procédure de sauvegarde et test de restauration", + "category": "Backup", + "pillar": "availability", + "owner": "Ops", + "freshness": "Valide", + "updatedAt": "2026-04-12", + "auditorReady": true, + "visibility": "auditor", + "source": "Runbook / Restore test", + "tags": [ + "backup", + "restore", + "dr" + ], + "linkedMilestones": [ + "ava-backup", + "ava-resilience" + ], + "summary": "Procédure documentée et test de restauration horodaté." + }, + { + "id": "ev-006", + "title": "Cartographie des flux critiques de traitement", + "category": "Process", + "pillar": "processing-integrity", + "owner": "Architecture", + "freshness": "Brouillon", + "updatedAt": "2026-04-04", + "auditorReady": false, + "visibility": "internal", + "source": "Architecture / Flux", + "tags": [ + "flows", + "processing", + "integrity" + ], + "linkedMilestones": [ + "pi-flows", + "pi-audit" + ], + "summary": "Cartographie initiale des flux critiques; consolidation encore en cours." + }, + { + "id": "ev-007", + "title": "Registre de classification des données", + "category": "Data", + "pillar": "confidentiality", + "owner": "Architecture", + "freshness": "À revoir", + "updatedAt": "2026-03-18", + "auditorReady": false, + "visibility": "internal", + "source": "Registre data", + "tags": [ + "classification", + "data", + "confidentiality" + ], + "linkedMilestones": [ + "con-classification", + "con-lifecycle" + ], + "summary": "Inventaire de classification existant mais incomplet pour certaines charges hébergées." + }, + { + "id": "ev-008", + "title": "Registre des renseignements personnels", + "category": "Privacy", + "pillar": "privacy", + "owner": "Direction", + "freshness": "Brouillon", + "updatedAt": "2026-04-02", + "auditorReady": false, + "visibility": "internal", + "source": "Registre privacy", + "tags": [ + "privacy", + "pii", + "register" + ], + "linkedMilestones": [ + "pri-mapping", + "pri-rules", + "pri-retention" + ], + "summary": "Première version du registre des renseignements personnels et de leurs finalités." + }, + { + "id": "ev-009", + "title": "Journal des incidents et post-mortems", + "category": "Incident", + "pillar": "security", + "owner": "Ops", + "freshness": "Valide", + "updatedAt": "2026-04-10", + "auditorReady": true, + "visibility": "auditor", + "source": "Incident register", + "tags": [ + "incident", + "rca", + "security" + ], + "linkedMilestones": [ + "sec-ir", + "ava-incidents" + ], + "summary": "Historique des incidents, causes racines et actions correctives." + }, + { + "id": "ev-010", + "title": "Registre des fournisseurs critiques", + "category": "Vendor", + "pillar": "confidentiality", + "owner": "Direction", + "freshness": "À revoir", + "updatedAt": "2026-03-27", + "auditorReady": false, + "visibility": "internal", + "source": "Vendor register", + "tags": [ + "vendors", + "third-party", + "contracts" + ], + "linkedMilestones": [ + "con-thirdparty", + "pri-incidents" + ], + "summary": "Liste des tiers, contrats et dépendances; classement critique à finaliser." + }, + { + "id": "ev-011", + "title": "Inventaire des services in-scope", + "category": "Inventory", + "pillar": "availability", + "owner": "Architecture", + "freshness": "Valide", + "updatedAt": "2026-04-11", + "auditorReady": true, + "visibility": "auditor", + "source": "CMDB / Architecture", + "tags": [ + "inventory", + "scope", + "services" + ], + "linkedMilestones": [ + "ava-inventory", + "sec-governance" + ], + "summary": "Liste des services critiques, dépendances et responsables d’exploitation." + }, + { + "id": "ev-012", + "title": "Matrice contrôles ↔ preuves", + "category": "Matrix", + "pillar": "security", + "owner": "Direction", + "freshness": "Valide", + "updatedAt": "2026-04-16", + "auditorReady": true, + "visibility": "auditor", + "source": "Compliance register", + "tags": [ + "matrix", + "controls", + "evidence" + ], + "linkedMilestones": [ + "sec-governance", + "sec-change", + "con-review", + "pri-review", + "pi-audit" + ], + "summary": "Matrice de rapprochement entre critères, contrôles, jalons et éléments probants." + } + ], + "pillars": [ + { + "id": "security", + "name": "Security", + "globalWeight": 32, + "objective": "Protéger les systèmes, les accès, les changements et la capacité de détection/réaction.", + "description": "Protection globale des systèmes, identités, journaux, changements et incidents.", + "milestones": [ + { + "id": "sec-governance", + "title": "Gouvernance sécurité de base", + "weight": 10, + "owner": "Direction", + "cadence": "Mensuelle", + "dueAt": "2026-04-25", + "stage": "proved", + "evidenceRefs": [ + "ev-001", + "ev-011", + "ev-012" + ], + "notes": "Périmètre et rôles définis; maintenir l’actualisation du registre des risques.", + "nextAction": "Ajouter la revue mensuelle du registre des risques à la fenêtre Type II." + }, + { + "id": "sec-iam", + "title": "Gestion des accès et des identités", + "weight": 20, + "owner": "Ops", + "cadence": "Mensuelle", + "dueAt": "2026-04-22", + "stage": "operating", + "evidenceRefs": [ + "ev-002" + ], + "notes": "MFA déployé mais la revue périodique signée et la révocation démontrée restent à consolider.", + "nextAction": "Capturer la revue d’accès du mois et la rattacher à ce jalon." + }, + { + "id": "sec-hardening", + "title": "Durcissement et gestion des vulnérabilités", + "weight": 20, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-21", + "stage": "operating", + "evidenceRefs": [], + "notes": "Le standard technique existe mais la chaîne de preuve automatisée doit être branchée.", + "nextAction": "Importer le dernier rapport de scan et lier la remédiation en cours." + }, + { + "id": "sec-logging", + "title": "Journalisation, surveillance et alertes", + "weight": 20, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-19", + "stage": "proved", + "evidenceRefs": [ + "ev-004" + ], + "notes": "Bonne maturité opérationnelle, surveillance déjà exploitable côté auditeur.", + "nextAction": "Ajouter un sommaire de rétention des journaux à la salle auditeur." + }, + { + "id": "sec-change", + "title": "Gestion des changements", + "weight": 15, + "owner": "Ops", + "cadence": "Continue", + "dueAt": "2026-04-20", + "stage": "operating", + "evidenceRefs": [ + "ev-003", + "ev-012" + ], + "notes": "Les PR existent; il reste à standardiser la preuve des validations post-changement.", + "nextAction": "Ajouter le champ “validation post-déploiement” dans le flux de changement." + }, + { + "id": "sec-ir", + "title": "Réponse aux incidents", + "weight": 15, + "owner": "Ops", + "cadence": "Trimestrielle", + "dueAt": "2026-05-05", + "stage": "operating", + "evidenceRefs": [ + "ev-009" + ], + "notes": "Registre en place; exercice formel tabletop à programmer.", + "nextAction": "Planifier un exercice et produire le compte rendu auditable." + } + ] + }, + { + "id": "availability", + "name": "Availability", + "globalWeight": 22, + "objective": "Maintenir la disponibilité promise, démontrer la résilience et la restaurabilité.", + "description": "Disponibilité des services, dépendances, capacité, sauvegardes et reprise.", + "milestones": [ + { + "id": "ava-inventory", + "title": "Inventaire des services critiques et dépendances", + "weight": 15, + "owner": "Architecture", + "cadence": "Mensuelle", + "dueAt": "2026-04-24", + "stage": "proved", + "evidenceRefs": [ + "ev-011" + ], + "notes": "Inventaire déjà formalisé, à garder vivant.", + "nextAction": "Ajouter les dépendances de monitoring externe dans l’inventaire." + }, + { + "id": "ava-slo", + "title": "Objectifs de service définis", + "weight": 10, + "owner": "Direction", + "cadence": "Trimestrielle", + "dueAt": "2026-05-10", + "stage": "designed", + "evidenceRefs": [], + "notes": "Le langage de promesse client existe, pas encore uniformisé en SLO/SLA.", + "nextAction": "Formaliser les cibles internes et externes par service in-scope." + }, + { + "id": "ava-monitoring", + "title": "Monitoring et capacité", + "weight": 25, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-19", + "stage": "proved", + "evidenceRefs": [ + "ev-004" + ], + "notes": "Surveillance solide et historique disponible.", + "nextAction": "Ajouter un résumé tendance capacité par trimestre." + }, + { + "id": "ava-backup", + "title": "Sauvegardes et restauration testée", + "weight": 25, + "owner": "Ops", + "cadence": "Mensuelle", + "dueAt": "2026-04-23", + "stage": "operating", + "evidenceRefs": [ + "ev-005" + ], + "notes": "Test de restauration existant; cadence et couverture des jeux de données à élargir.", + "nextAction": "Ajouter un deuxième scénario de restauration ciblant la chaîne complète applicative." + }, + { + "id": "ava-resilience", + "title": "Résilience opérationnelle", + "weight": 15, + "owner": "Architecture", + "cadence": "Trimestrielle", + "dueAt": "2026-05-12", + "stage": "operating", + "evidenceRefs": [ + "ev-005" + ], + "notes": "Les mécanismes existent mais le récit auditeur n’est pas encore assez structuré.", + "nextAction": "Rédiger une fiche de reprise courte et exposable." + }, + { + "id": "ava-incidents", + "title": "Gestion des incidents de disponibilité", + "weight": 10, + "owner": "Ops", + "cadence": "Continue", + "dueAt": "2026-04-20", + "stage": "proved", + "evidenceRefs": [ + "ev-004", + "ev-009" + ], + "notes": "Bonne base; il faut lier plus clairement l’incident à l’action corrective.", + "nextAction": "Relier chaque incident clos à une décision corrective explicite." + } + ] + }, + { + "id": "processing-integrity", + "name": "Processing Integrity", + "globalWeight": 16, + "objective": "Démontrer que les traitements critiques sont exacts, complets, traçables et revus.", + "description": "Intégrité des traitements, qualité des flux, détection d’erreurs et validation.", + "milestones": [ + { + "id": "pi-flows", + "title": "Flux critiques identifiés", + "weight": 15, + "owner": "Architecture", + "cadence": "Mensuelle", + "dueAt": "2026-04-28", + "stage": "operating", + "evidenceRefs": [ + "ev-006" + ], + "notes": "La cartographie existe mais pas encore sur tout le périmètre Type II.", + "nextAction": "Terminer la cartographie des flux administratifs et de preuve." + }, + { + "id": "pi-inputs", + "title": "Contrôles d’entrée et de changement", + "weight": 20, + "owner": "Ops", + "cadence": "Continue", + "dueAt": "2026-04-22", + "stage": "operating", + "evidenceRefs": [ + "ev-003" + ], + "notes": "Le lien entre changements techniques et impact métier doit être explicité.", + "nextAction": "Ajouter un champ “impact traitement” dans les changements sensibles." + }, + { + "id": "pi-audit", + "title": "Traçabilité et auditabilité des traitements", + "weight": 20, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-20", + "stage": "proved", + "evidenceRefs": [ + "ev-006", + "ev-012" + ], + "notes": "Base structurante en place.", + "nextAction": "Mieux exposer la traçabilité pour les auditeurs non techniques." + }, + { + "id": "pi-errors", + "title": "Détection et gestion des erreurs", + "weight": 20, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-21", + "stage": "operating", + "evidenceRefs": [], + "notes": "Le processus existe de façon implicite; il faut le rendre explicite et prouvable.", + "nextAction": "Créer un registre simple d’anomalies de traitement." + }, + { + "id": "pi-validation", + "title": "Validation / tests / réconciliation", + "weight": 15, + "owner": "Ops", + "cadence": "Mensuelle", + "dueAt": "2026-04-26", + "stage": "operating", + "evidenceRefs": [], + "notes": "Présent dans la pratique, mais pas encore uniformisé dans la preuve.", + "nextAction": "Définir trois contrôles de réconciliation standard." + }, + { + "id": "pi-review", + "title": "Revue périodique des écarts", + "weight": 10, + "owner": "Direction", + "cadence": "Mensuelle", + "dueAt": "2026-04-30", + "stage": "absent", + "evidenceRefs": [], + "notes": "C’est un vrai trou de gouvernance pour ce pilier.", + "nextAction": "Instaurer une revue mensuelle d’écarts avec décision consignée." + } + ] + }, + { + "id": "confidentiality", + "name": "Confidentiality", + "globalWeight": 18, + "objective": "Encadrer l’accès, les secrets, la classification, les tiers et le cycle de vie des données confidentielles.", + "description": "Protection des données confidentielles, accès, secrets, conservation et tiers.", + "milestones": [ + { + "id": "con-classification", + "title": "Classification des données", + "weight": 15, + "owner": "Architecture", + "cadence": "Trimestrielle", + "dueAt": "2026-05-15", + "stage": "operating", + "evidenceRefs": [ + "ev-007" + ], + "notes": "Une base existe, mais elle n’est pas encore assez normalisée pour l’audit.", + "nextAction": "Uniformiser les niveaux de classification et leurs exigences associées." + }, + { + "id": "con-access", + "title": "Restriction d’accès", + "weight": 25, + "owner": "Ops", + "cadence": "Mensuelle", + "dueAt": "2026-04-22", + "stage": "proved", + "evidenceRefs": [ + "ev-002" + ], + "notes": "Contrôle robuste, sous réserve d’une meilleure preuve de revue formelle.", + "nextAction": "Ajouter l’attestation mensuelle du responsable sur les accès sensibles." + }, + { + "id": "con-tech", + "title": "Protection technique", + "weight": 20, + "owner": "Ops", + "cadence": "Hebdomadaire", + "dueAt": "2026-04-20", + "stage": "proved", + "evidenceRefs": [], + "notes": "Bonne maîtrise technique, chaîne de preuve encore dispersée.", + "nextAction": "Centraliser les preuves chiffrement/TLS/secrets dans un seul dossier." + }, + { + "id": "con-lifecycle", + "title": "Cycle de vie des données", + "weight": 15, + "owner": "Architecture", + "cadence": "Trimestrielle", + "dueAt": "2026-05-08", + "stage": "operating", + "evidenceRefs": [ + "ev-007" + ], + "notes": "Règles de conservation à préciser selon type de données.", + "nextAction": "Créer une matrice conservation / archivage / destruction." + }, + { + "id": "con-thirdparty", + "title": "Tiers et obligations contractuelles", + "weight": 10, + "owner": "Direction", + "cadence": "Trimestrielle", + "dueAt": "2026-05-02", + "stage": "operating", + "evidenceRefs": [ + "ev-010" + ], + "notes": "Le registre existe, la criticité et le suivi restent à renforcer.", + "nextAction": "Classer les fournisseurs par criticité et lier les clauses sécurité." + }, + { + "id": "con-review", + "title": "Preuves de revue et exceptions", + "weight": 15, + "owner": "Direction", + "cadence": "Mensuelle", + "dueAt": "2026-04-30", + "stage": "operating", + "evidenceRefs": [ + "ev-012" + ], + "notes": "Bonne structure, manque encore des revues exécutées sur la durée.", + "nextAction": "Ajouter la dernière revue mensuelle avec décisions d’exception." + } + ] + }, + { + "id": "privacy", + "name": "Privacy", + "globalWeight": 12, + "objective": "Encadrer les renseignements personnels, leurs finalités, leurs droits et leur suppression.", + "description": "Renseignements personnels, finalités, droits, conservation et incidents.", + "milestones": [ + { + "id": "pri-mapping", + "title": "Cartographie des renseignements personnels", + "weight": 20, + "owner": "Direction", + "cadence": "Trimestrielle", + "dueAt": "2026-05-16", + "stage": "operating", + "evidenceRefs": [ + "ev-008" + ], + "notes": "Démarré, mais encore incomplet sur certains fournisseurs et flux secondaires.", + "nextAction": "Finaliser la source, la finalité et la localisation pour chaque catégorie de RP." + }, + { + "id": "pri-rules", + "title": "Règles de collecte et d’usage", + "weight": 20, + "owner": "Direction", + "cadence": "Trimestrielle", + "dueAt": "2026-05-18", + "stage": "operating", + "evidenceRefs": [ + "ev-008" + ], + "notes": "La doctrine existe, la démonstration opérationnelle est incomplète.", + "nextAction": "Documenter explicitement les finalités et la minimisation." + }, + { + "id": "pri-rights", + "title": "Droits et demandes des personnes", + "weight": 20, + "owner": "Direction", + "cadence": "Mensuelle", + "dueAt": "2026-04-29", + "stage": "absent", + "evidenceRefs": [], + "notes": "Aucun mécanisme simple et démontrable n’est encore en place.", + "nextAction": "Créer un registre de demandes et une procédure courte de traitement." + }, + { + "id": "pri-retention", + "title": "Conservation et suppression", + "weight": 20, + "owner": "Architecture", + "cadence": "Trimestrielle", + "dueAt": "2026-05-11", + "stage": "operating", + "evidenceRefs": [ + "ev-008" + ], + "notes": "Les règles existent partiellement par système, pas encore au niveau programme.", + "nextAction": "Formaliser la matrice de rétention et les méthodes de suppression." + }, + { + "id": "pri-incidents", + "title": "Incidents vie privée et tiers", + "weight": 10, + "owner": "Direction", + "cadence": "Trimestrielle", + "dueAt": "2026-05-09", + "stage": "operating", + "evidenceRefs": [ + "ev-010" + ], + "notes": "La zone tiers est présente, mais l’angle privacy est sous-documenté.", + "nextAction": "Ajouter le scénario “incident touchant des RP” au registre d’incidents." + }, + { + "id": "pri-review", + "title": "Revue périodique de conformité vie privée", + "weight": 10, + "owner": "Direction", + "cadence": "Mensuelle", + "dueAt": "2026-04-30", + "stage": "absent", + "evidenceRefs": [], + "notes": "Pas encore de cycle récurrent de revue privacy.", + "nextAction": "Instaurer une revue mensuelle compacte de conformité privacy." + } + ] + } + ], + "auditorPacks": [ + { + "id": "pack-1", + "title": "System Description", + "status": "ready", + "requiredEvidence": [ + "ev-001", + "ev-011" + ], + "description": "Périmètre, architecture, dépendances, flux de données, rôles et responsabilités." + }, + { + "id": "pack-2", + "title": "Management Assertion", + "status": "draft", + "requiredEvidence": [ + "ev-012" + ], + "description": "Déclaration de la direction sur la fidélité de la description et l’efficacité opérationnelle." + }, + { + "id": "pack-3", + "title": "Controls ↔ Evidence Matrix", + "status": "ready", + "requiredEvidence": [ + "ev-012" + ], + "description": "Matrice de rapprochement entre critères, jalons, contrôles, propriétaires et preuves." + }, + { + "id": "pack-4", + "title": "Exceptions & Remediation Register", + "status": "draft", + "requiredEvidence": [ + "ev-009", + "ev-010" + ], + "description": "Écarts connus, compensating controls, décisions et plan de remédiation." + }, + { + "id": "pack-5", + "title": "Access & Privileged Activity Pack", + "status": "draft", + "requiredEvidence": [ + "ev-002" + ], + "description": "Revue des accès, privilèges, MFA et preuves de révocation." + } + ] +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..7dc7869 --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + Chezlepro SOC 2 Cockpit + + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..f1d1a90 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "chezlepro-soc2-cockpit", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "framer-motion": "^12.23.12", + "lucide-react": "^0.542.0", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "recharts": "^3.1.2" + }, + "devDependencies": { + "vite": "^7.1.3" + } +} diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..692996d --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,333 @@ +import React, { useMemo, useState } from "react"; +import { motion } from "framer-motion"; +import { + Bar, + BarChart, + CartesianGrid, + PolarAngleAxis, + PolarGrid, + PolarRadiusAxis, + Radar, + RadarChart, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; +import { + CalendarDays, + ClipboardCheck, + Clock3, + Eye, + FileText, + Gauge, + KanbanSquare, + Layers3, + Plus, + ShieldAlert, + Sparkles, + Target, +} from "lucide-react"; +import { useSoc2Registry } from "./features/hooks/useSoc2Registry.js"; +import { OFFICIAL_SCHEMA } from "./model/schema.js"; +import { INITIAL_DB } from "./model/seed.js"; +import { buildRegistryManifests, validateRegistry } from "./lib/validation.js"; +import { + buildBarData, + buildBlockers, + buildDataHealth, + buildEvidenceMap, + buildPackReadiness, + buildRadarData, + buildScoredPillars, + computeGlobalScore, + filterEvidence, + getOverallStatus, + getPackTone, + getOwners, +} from "./lib/selectors.js"; +import { daysUntil } from "./lib/utils.js"; +import { PILLAR_ICONS } from "./lib/view.js"; +import PillarOverviewCard from "./components/PillarOverviewCard.jsx"; +import MilestoneCard from "./components/MilestoneCard.jsx"; +import EvidenceLibrary from "./components/EvidenceLibrary.jsx"; +import EvidenceEditor from "./components/EvidenceEditor.jsx"; +import DataTab from "./components/DataTab.jsx"; +import { ProgressBar, ScorePill, SectionTitle, Tag } from "./components/ui/primitives.jsx"; + +function DashboardPanel({ scoredPillars, targetReadiness, overall, selectedPillar, setSelectedPillar }) { + const radarData = buildRadarData(scoredPillars, targetReadiness); + + return ( + <> +
+
+ +
+ + + + + + + + + + +
+
+
+ +
+
Logique de score
Absent = 0, Conçu = 0,35, En opération = 0,7, Prouvé = 1. Les preuves bonifient la crédibilité du score.
+
Règle de plancher
Tant que Security reste sous 60%, l’état global doit être considéré fragile.
+
État actuel
{overall.label}
+
+
+
+
+ {scoredPillars.map((pillar) => ( + + ))} +
+ + ); +} + +export default function App() { + const registry = useSoc2Registry(); + const { db, lastSavedAt, importError, updateMilestoneStage, updateEvidence, toggleEvidenceMilestoneLink, createEvidence, deleteEvidence, exportDb, copyDb, importDb, resetDemo } = registry; + + const [activeTab, setActiveTab] = useState("dashboard"); + const [selectedPillar, setSelectedPillar] = useState("security"); + const [selectedEvidenceId, setSelectedEvidenceId] = useState(db.evidence[0]?.id || ""); + const [search, setSearch] = useState(""); + const [evidenceFilter, setEvidenceFilter] = useState("all"); + const [ownerFilter, setOwnerFilter] = useState("all"); + const [auditorMode, setAuditorMode] = useState(false); + const [importText, setImportText] = useState(""); + + const evidenceMap = useMemo(() => buildEvidenceMap(db), [db]); + const scoredPillars = useMemo(() => buildScoredPillars(db, evidenceMap), [db, evidenceMap]); + const selected = scoredPillars.find((pillar) => pillar.id === selectedPillar) || scoredPillars[0]; + const selectedEvidence = db.evidence.find((item) => item.id === selectedEvidenceId) || db.evidence[0] || null; + const owners = useMemo(() => getOwners(db), [db]); + const globalScore = useMemo(() => computeGlobalScore(db.pillars, evidenceMap), [db, evidenceMap]); + const overall = getOverallStatus(globalScore); + const auditWindowDays = daysUntil(db.appContext.observationWindow.end); + const blockers = useMemo(() => buildBlockers(db, evidenceMap), [db, evidenceMap]); + const packReadiness = useMemo(() => buildPackReadiness(db), [db]); + const dataHealth = useMemo(() => buildDataHealth(db), [db]); + const validation = useMemo(() => validateRegistry(db), [db]); + const manifests = useMemo(() => buildRegistryManifests(db), [db]); + + const filteredEvidence = useMemo( + () => filterEvidence(db, { query: search, pillarId: evidenceFilter, owner: ownerFilter, auditorMode }), + [db, search, evidenceFilter, ownerFilter, auditorMode] + ); + + const barData = useMemo(() => buildBarData(selected, evidenceMap), [selected, evidenceMap]); + const readyEvidenceCount = db.evidence.filter((item) => item.auditorReady).length; + const readinessRate = db.evidence.length ? Math.round((readyEvidenceCount / db.evidence.length) * 1000) / 10 : 0; + const blocker = scoredPillars.find((pillar) => pillar.id === "security" && pillar.score < 60); + + const handleCreateEvidence = () => { + const id = createEvidence(selectedPillar); + setSelectedEvidenceId(id); + setActiveTab("evidence"); + }; + + const handleDeleteEvidence = (evidenceId) => { + deleteEvidence(evidenceId); + const fallback = db.evidence.find((item) => item.id !== evidenceId); + setSelectedEvidenceId(fallback?.id || ""); + }; + + const handleImport = () => { + const result = importDb(importText); + if (result.ok) { + setSelectedPillar(result.data.pillars[0]?.id || "security"); + setSelectedEvidenceId(result.data.evidence[0]?.id || ""); + setImportText(""); + } + }; + + const handleReset = () => { + resetDemo(); + setSelectedPillar("security"); + setSelectedEvidenceId(INITIAL_DB.evidence[0]?.id || ""); + }; + + return ( +
+
+ +
+
+
+ {db.appContext.orgName} • {db.appContext.programName} + {overall.label} + {blocker ? Blocage: Security < 60 : null} +
+

Assistant web de conformité SOC 2 orienté preuve, remédiation et exposition auditeur

+

Architecture modulaire, source de vérité versionnable, radar des 5 piliers, bibliothèque de preuves et salle auditeur.

+
+ + + +
+
+
+ + + + +
+
+
+ +
+ {["dashboard", "controls", "execution", "evidence", "auditor", "data"].map((tab) => ( + + ))} +
+ + {activeTab === "dashboard" && ( + + )} + + {activeTab === "controls" && ( +
+
+ +
+ + + +
+
+ + + + + + + + + +
+
+
+ +
+
+ {selected.milestones.map((milestone) => ( + milestone.evidenceRefs.includes(item.id))} onStageChange={updateMilestoneStage} /> + ))} +
+
+
+
+ )} + + {activeTab === "execution" && ( +
+
+ +
+ {blockers.map((item) => ( +
+
+
+
{item.pillarName}
+
{item.title}
+
+ {item.dueInDays < 0 ? `Retard ${Math.abs(item.dueInDays)} j` : `${item.dueInDays} j`} +
+
{item.nextAction}
+
+ ))} +
+
+
+ +
+ {db.pillars.flatMap((pillar) => + pillar.milestones.map((milestone) => ( +
+
{pillar.name}{milestone.stage}
+
{milestone.title}
+
{milestone.owner} • {milestone.cadence}
+
Échéance: {milestone.dueAt}
+
+ )) + )} +
+
+
+ )} + + {activeTab === "evidence" && ( +
+ + +
+ )} + + {activeTab === "auditor" && ( +
+
+ +
+ {packReadiness.map((pack) => ( +
+
{pack.title}
{pack.description}
{pack.status}
+
Degré de préparation{pack.readiness}%
+ +
{pack.linked.map((item) => {item.title})}
+
+ ))} +
Mode {auditorMode ? "auditeur" : "interne"}
{auditorMode ? "Affichage réduit aux preuves auditables." : "Affichage complet incluant brouillons et remédiation."}
+
+
+
+ +
+
Description du système
Service in-scope, architecture, dépendances, responsabilités, flux de données, outils de surveillance, changements et posture de reprise.
+
Chaîne de preuve
Chaque jalon significatif doit être relié à des éléments probants datés, intelligibles et contextualisés, avec propriétaire et fraîcheur connue.
+
Écarts et remédiation
Les trous de conformité ne sont pas cachés. Ils sont nommés, qualifiés, suivis et accompagnés d’une action corrective explicite.
+
Narratif recommandé
Présenter d’abord le périmètre, puis les contrôles communs, ensuite les preuves les plus parlantes, et finir par les exceptions connues avec leurs remédiations.
+
+
+
+ )} + + {activeTab === "data" && ( + + )} +
+
+ ); +} diff --git a/src/components/DataTab.jsx b/src/components/DataTab.jsx new file mode 100644 index 0000000..30b03ab --- /dev/null +++ b/src/components/DataTab.jsx @@ -0,0 +1,70 @@ +import React from "react"; +import { Database, Download, RefreshCcw, Save, Upload } from "lucide-react"; +import { MiniKpi, SectionTitle } from "./ui/primitives.jsx"; + +export default function DataTab({ db, owners, dataHealth, importText, setImportText, importError, onImport, onExport, onCopy, onReset, validation, manifests, schema }) { + return ( +
+
+ +
+ + pillar.milestones).length)} /> + + +
+
+ + + + +
+
+ + + +
+
+ Structure persistée : schemaVersion, appContext, pillars, evidence, auditorPacks. +
+
+
Validation du registre
+
{validation.isValid ? "Le registre respecte le contrat structurel courant." : "Le registre contient des erreurs structurelles à corriger avant versionnement."}
+
+
+
Stratégie dépôt
+
Chemin recommandé : {schema.repositoryStrategy.recommendedPath}
+
+
+ +
+
+ +