{ "completeness": "partial", "deferred": [ { "candidateId": "remaining-source", "id": "remaining-source", "reason": "Agents interrompus par limites d\u2019usage. Pages/composants frontend, migrations/scripts, adaptateurs transporteurs et portions CSV restent non lus int\u00e9gralement ; couverture non exhaustive." }, { "candidateId": "subscription-sync-binding", "id": "subscription-sync-binding", "reason": "syncFromStripe ne lie pas metadata.organizationId ; UNIQUE stripe_subscription_id bloque le sc\u00e9nario normal. Course avant webhook ou ancien abonnement non li\u00e9 non valid\u00e9s." }, { "candidate": { "evidence": "login/page.tsx:97 redirect query, auth-context.tsx:110 router.push without URL validation", "title": "Untrusted login redirect reaches router.push" }, "candidateId": "login-redirect", "id": "login-redirect", "reason": "Recovering interrupted investigator result for validation" }, { "candidate": { "evidence": "NotificationsGateway forwards notificationId without userId into repository update", "title": "WebSocket mark_as_read lacks ownership" }, "candidateId": "notification-owner", "id": "notification-owner", "reason": "Recovering interrupted baseline result" }, { "candidate": { "evidence": "CsvBookingService.toResponseDto exposes confirmationToken used by public accept/reject", "title": "Creator receives carrier confirmation token" }, "candidateId": "carrier-token", "id": "carrier-token", "reason": "Recovering interrupted baseline result" } ], "documentType": "codex-security.coverage", "excludePaths": [], "explicitExclusions": [ { "pattern": "**/.env*", "reason": "Restriction explicite de lecture." }, { "pattern": "**/.env*", "reason": "User prohibits .env and .env.* reads." } ], "includePaths": [ "." ], "inventoryStrategy": "repository", "mode": "repository", "openQuestions": [ { "question": "Compl\u00e9ter les fichiers non lus int\u00e9gralement avant de qualifier la couverture d\u2019exhaustive." }, { "question": "V\u00e9rifier rotation SMTP et plafond multipart au proxy sans r\u00e9utiliser le secret." }, { "question": "V\u00e9rifier liaison Stripe session/organisation et droits des abonnements UNPAID/PAUSED : plusieurs consommateurs lisent seulement plan." }, { "question": "Aligner DATABASE_SSL, validation TLS SMTP/SQL et buckets provisionn\u00e9s/ACL." } ], "scanId": "4c194468-0b5f-4f24-9005-5be211dc0e47", "schemaVersion": "1.0", "surfaces": [ { "disposition": "reported", "id": "surface_authentification-recuperation-et-websockets", "label": "Authentification, r\u00e9cup\u00e9ration et WebSockets", "notes": "JWT HTTP v\u00e9rifie le type access et le compte actif ; inscription li\u00e9e \u00e0 invitation v\u00e9rifi\u00e9e. Bypass WebSocket, sessions apr\u00e8s reset et secrets dans logs confirm\u00e9s.", "receiptRefs": [], "riskArea": "Sessions" }, { "disposition": "reported", "id": "surface_organisations-et-roles-csv", "label": "Organisations et r\u00f4les CSV", "notes": "Contr\u00f4le inter-organisations cass\u00e9 par casse du r\u00f4le ; liste CSV sans r\u00f4le et mutations VIEWER. Les mutations individuelles CSV v\u00e9rifient le propri\u00e9taire.", "receiptRefs": [], "riskArea": "Isolation et permissions" }, { "disposition": "reported", "id": "surface_liens-transporteurs-et-documents", "label": "Liens transporteurs et documents", "notes": "Jeton divulgu\u00e9 au client. T\u00e9l\u00e9chargements v\u00e9rifient ACCEPTED, mot de passe si configur\u00e9 et appartenance du document ; PDFKit rend du texte sans navigateur ni chargement HTML.", "receiptRefs": [], "riskArea": "Autorit\u00e9 et stockage" }, { "disposition": "reported", "id": "surface_souscriptions-stripe", "label": "Souscriptions Stripe", "notes": "Signatures v\u00e9rifi\u00e9es ; r\u00e9siliation bloqu\u00e9e par licences. Sync ne compare pas metadata.organizationId mais UNIQUE stripe_subscription_id bloque la r\u00e9association normale ; sc\u00e9nario de course non confirm\u00e9.", "receiptRefs": [], "riskArea": "Int\u00e9grit\u00e9 financi\u00e8re" }, { "disposition": "no_issue_found", "id": "surface_mcp-et-assistant-ia", "label": "MCP et assistant IA", "notes": "R\u00f4le/offre contr\u00f4l\u00e9s \u00e0 chaque invocation ; acteur li\u00e9 \u00e0 session, SQL des conversations param\u00e9tr\u00e9 avec user_id, quota atomique et tours IA born\u00e9s.", "receiptRefs": [], "riskArea": "Outils et donn\u00e9es" }, { "disposition": "reported", "id": "surface_frontend-et-exports", "label": "Frontend et exports", "notes": "Redirection brute v\u00e9rifi\u00e9e dans Next install\u00e9. Formules CSV non neutralis\u00e9es. Contexte actif avec cookies HttpOnly, distinct de l\u2019ancien client localStorage.", "receiptRefs": [], "riskArea": "XSS et CSV" }, { "disposition": "reported", "id": "surface_logs-et-deploiements", "label": "Logs et d\u00e9ploiements", "notes": "Cl\u00e9 SMTP litt\u00e9rale masqu\u00e9e, validit\u00e9 inconnue. Logs de production internes avec NetworkPolicy ; Compose dev expose 3100/3200 sans authentification, sans preuve d\u2019exposition Internet.", "receiptRefs": [], "riskArea": "Secrets et r\u00e9seau" }, { "disposition": "no_issue_found", "id": "surface_persistance-gdpr-et-configuration", "label": "Persistance, GDPR et configuration", "notes": "Requ\u00eates recherche/GDPR/conversations param\u00e9tr\u00e9es ; export GDPR exclut hash mot de passe, TOTP et hash de cl\u00e9. DATABASE_SSL ignor\u00e9 par runtime/startup et validation de certificat d\u00e9sactiv\u00e9e dans CLI ; buckets distincts, \u00e9tat r\u00e9el externe non test\u00e9. 95 fichiers suivis lus int\u00e9gralement ; lectures cibl\u00e9es suppl\u00e9mentaires non compt\u00e9es.", "receiptRefs": [], "riskArea": "Injection et donn\u00e9es" }, { "disposition": "rejected", "id": "surface_webhook-ssrf-a-l-enregistrement", "label": "Webhook SSRF \u00e0 l\u2019enregistrement", "notes": "WebhookService poste vers la destination enregistr\u00e9e sans filtre IP, mais les DTO CreateWebhookDto/UpdateWebhookDto n\u2019ont aucun d\u00e9corateur de validation ; la validation globale whitelist + forbidNonWhitelisted de main.ts rejette leurs champs. Aucune voie actuelle de cr\u00e9ation par un attaquant n\u2019a \u00e9t\u00e9 \u00e9tablie. Corriger les DTO doit imp\u00e9rativement ajouter aussi une politique de destination.", "receiptRefs": [], "riskArea": "Requ\u00eates sortantes" } ] }