xpeditis2.0/docs/security/check-secu/coverage.json
2026-09-14 11:19:29 +02:00

150 lines
6.9 KiB
JSON

{
"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"
}
]
}