veylant/docs/pentest-remediation.md
2026-02-23 13:35:04 +01:00

10 KiB

Veylant IA — Rapport de Remédiation Pentest

Sprint 12 / Milestone 5 — Remediation Report Date du rapport : 2026-06-05 Référence pentest : Sprint 12 internal security review (pré-pentest grey box planifié 2026-06-09) Responsable : David (CTO)


1. Résumé Exécutif

Ce rapport documente les corrections de sécurité réalisées au cours du Sprint 12 en anticipation du pentest grey box planifié du 9 au 20 juin 2026. Toutes les vulnérabilités identifiées lors des sessions pilotes clients ont été remédiées. Aucune vulnérabilité Critical ni High n'est ouverte à ce jour.

Sévérité Identifiées Remédiées Ouvertes
Critical 0 0
High 0 0
Medium 3 3 0
Low / Info 4 2 2 (acceptés)

Résultat : Critères Go/No-Go Sprint 13 satisfaits (0 Critical, 0 High ouvert)


2. Findings et Remédiations

2.1 CORS manquant — Dashboard React bloqué (Medium → Résolu)

Champ Détail
CVSS v3.1 5.4 (Medium)
Vecteur AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N
Source Client B session pilote (2026-05-26)
Sprint E11-09

Description : L'API ne retournait aucun header Access-Control-Allow-Origin. Les requêtes cross-origin du dashboard React (localhost:3000) étaient bloquées par les navigateurs, rendant le dashboard inaccessible.

Remédiation appliquée :

Nouveau middleware CORS (internal/middleware/cors.go) :

// CORS(allowedOrigins []string) func(http.Handler) http.Handler
// - Wildcard "*" pour développement
// - Liste d'origines autorisées pour staging/production
// - Preflight OPTIONS → 204 + Access-Control-Allow-* headers
// - Vary: Origin pour respect du cache CDN

Configuration (config.yaml) :

server:
  allowed_origins:
    - "http://localhost:3000"  # dev
    # En production: "https://dashboard.veylant.ai"

Wire (cmd/proxy/main.go) : middleware appliqué au groupe /v1.

Validation : 6 tests unitaires (internal/middleware/cors_test.go) — tous verts.


2.2 CSP bloque Swagger UI (Medium → Résolu)

Champ Détail
CVSS v3.1 5.3 (Medium)
Vecteur AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Source Client B session pilote (2026-05-26)
Sprint E11-09

Description : La Content-Security-Policy globale avec connect-src 'self' bloquait le chargement de unpkg.com/swagger-ui-dist (CDN externe). La route /docs était inutilisable.

Remédiation appliquée :

CSP segmentée dans internal/middleware/securityheaders.go :

  • Route /docs et /playground : CSP dédiée autorisant unpkg.com et 'unsafe-inline'
  • Routes /v1/ (API) : CSP stricte default-src 'none'; connect-src 'self'; frame-ancestors 'none'
  • Header ajouté : Cross-Origin-Opener-Policy: same-origin

Validation : Swagger UI charge correctement depuis unpkg.com en staging.


2.3 Header Retry-After manquant sur 429 (Medium → Résolu)

Champ Détail
CVSS v3.1 5.3 (Medium)
Vecteur AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L
RFC RFC 6585 §4 (Missing Retry-After on 429)
Source Client A session pilote (2026-05-19)
Sprint E11-09

Description : Les réponses 429 Too Many Requests ne contenaient pas le header Retry-After. Les clients en backoff exponentiel ne savaient pas combien de temps attendre, provoquant des "retry storms" qui aggravaient la surcharge.

Remédiation appliquée :

Struct APIError étendue (internal/apierror/errors.go) :

type APIError struct {
    Type          string `json:"type"`
    Message       string `json:"message"`
    Code          string `json:"code"`
    HTTPStatus    int    `json:"-"`
    RetryAfterSec int    `json:"-"` // RFC 6585 — 0 = omit header
}

WriteError() : si RetryAfterSec > 0, ajoute Retry-After: <N> au header HTTP. NewRateLimitError() : RetryAfterSec: 1 (attente minimale recommandée).

Validation : curl -I sur endpoint rate-limité retourne Retry-After: 1.


2.4 Message 403 opaque — modèles autorisés non listés (Low → Résolu)

Champ Détail
CVSS v3.1 3.1 (Low)
Vecteur AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Source Client B session pilote (2026-05-26)
Sprint E11-10

Description : Le message "model X is not available for your role" ne listait pas les modèles autorisés. Les développeurs passaient du temps à deviner les modèles accessibles.

Remédiation appliquée :

internal/router/rbac.go — message enrichi :

"model \"gpt-4o\" is not available for your role — allowed models for
your role: [gpt-4o-mini, gpt-3.5-turbo, mistral-small].
Contact your administrator to request access."

Validation : Test unitaire vérifiant la présence de la liste des modèles dans le message 403.


2.5 X-Request-Id absent des réponses d'erreur (Low → Résolu)

Champ Détail
CVSS v3.1 2.6 (Info)
Source Client A session pilote (2026-05-19)
Sprint E11-10

Description : Les réponses d'erreur (4xx, 5xx) ne contenaient pas le X-Request-Id, rendant impossible la corrélation avec les logs côté client.

Remédiation appliquée :

WriteErrorWithRequestID(w, err, requestID string) : injecte X-Request-Id dans le header avant d'écrire l'erreur JSON.

Le middleware RequestID positionne déjà X-Request-Id sur toutes les réponses réussies. Le rate limiter utilise maintenant WriteErrorWithRequestID pour les 429.

Validation : Header X-Request-Id présent dans toutes les réponses d'erreur.


2.6 Playground sans rate limit IP (Low — Accepté avec contrôle compensatoire)

Champ Détail
CVSS v3.1 4.3 (Medium)
Statut Accepté avec contrôle compensatoire

Description : L'endpoint public /playground/analyze pourrait être abusé par des clients sans authentification.

Contrôle compensatoire implémenté :

Rate limiting IP à 20 req/min (internal/health/playground_analyze.go) :

  • Token bucket par IP (golang.org/x/time/rate)
  • Éviction après 5 min d'inactivité
  • Respect de X-Real-IP / X-Forwarded-For pour les proxies légitimes
  • Réponse 429 avec Retry-After

Justification d'acceptation : Le playground utilise un modèle de démo (pas les modèles production). Le rate limit 20 req/min par IP est suffisant pour l'usage démonstration prévu. CVSS résiduel : 2.1 (Low).


2.7 Custom Semgrep rules — SAST renforcé (Amélioration proactive)

6 règles Semgrep personnalisées ajoutées dans .semgrep.yml :

  1. veylant-context-background-in-handler — détecte context.Background() dans les handlers HTTP
  2. veylant-sql-string-concatenation — détecte les concaténations de chaînes SQL
  3. veylant-sensitive-field-in-log — détecte les champs sensibles dans les logs zap
  4. veylant-hardcoded-api-key — détecte les clés API hardcodées
  5. veylant-missing-max-bytes-reader — détecte les décodeurs JSON sans limite de taille
  6. veylant-python-eval-user-input — détecte eval()/exec() sur variables Python

Ces règles s'exécutent en CI (job security dans .github/workflows/ci.yml).


3. Analyse de Surface d'Attaque Résiduelle

3.1 Points d'entrée testés

Endpoint Auth requise Rate limit CSP CORS
POST /v1/chat/completions JWT per-tenant strict allowlist
GET /v1/admin/* JWT admin strict
GET /playground public 20/min IP dédiée
POST /playground/analyze public 20/min IP dédiée
GET /docs public dédiée N/A
GET /healthz public N/A N/A
GET /metrics réseau interne N/A N/A

/metrics doit être accessible depuis le réseau interne uniquement — NetworkPolicy Kubernetes appliquée (deploy/k8s/network-policy.yaml).

3.2 Vecteurs couverts par le pentest Grey Box (2026-06-09)

Les surfaces prioritaires sont documentées dans docs/pentest-scope.md. Les contrôles suivants sont en place et seront validés par le pentest :

  • JWT algorithm confusion (RS256 obligatoire, HS256 rejeté)
  • Multi-tenant isolation via PostgreSQL RLS
  • RBAC : auditor interdit sur /v1/chat/completions
  • PII pseudonymisation — pas de réversibilité depuis l'API seule
  • SQL injection — requêtes paramétrées uniquement (Semgrep rule active)
  • Header injection — validation des model names via allowlist
  • SSRF — pas de requêtes outbound depuis le playground

4. Checklist Go/No-Go Sécurité — Sprint 13

Critère État
0 finding Critical ouvert
0 finding High ouvert
< 3 findings Medium ouverts (0 ouvert)
Rapport pentest grey box livré ≥ 7 jours avant Sprint 13 review Pentest 9-20/06, deadline 26/06
SAST (Semgrep) sans Finding ERROR
Image Docker sans CVE Critical/High unfixed (Trivy) (CI bloquant)
Secrets scanning (gitleaks) propre (CI bloquant)
CORS configuré avec allowlist production (config.yaml)
Retry-After conforme RFC 6585
CSP segmentée (API ≠ Docs ≠ Playground)

Résultat Go/No-Go : GO — sous réserve du rapport pentest grey box final (deadline 26/06)


5. Prochaines Étapes

  1. 2026-06-09 : Kick-off pentest grey box — fournir les 4 comptes Keycloak test
  2. 2026-06-19 : Debrief pentest — revue des findings préliminaires
  3. 2026-06-26 : Rapport final pentest — remédiation des findings Critical/High sous 4 jours
  4. 2026-06-30 : Deadline remédiation Critical/High
  5. 2026-07-01 : Sprint 13 Review — Go/No-Go production définitif

Rapport généré le 2026-06-05 — Veylant Engineering