Initial commit: Family Planner application
Complete family planning application with: - React frontend with TypeScript - Node.js/Express backend with TypeScript - Python ingestion service for document processing - Planning ingestion service with LLM integration - Shared UI components and type definitions - OAuth integration for calendar synchronization - Comprehensive documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
251
docs/archive/SECURITY_IMPROVEMENTS.md
Normal file
251
docs/archive/SECURITY_IMPROVEMENTS.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Améliorations de Sécurité - Family Planner
|
||||
|
||||
## 📋 Résumé des corrections appliquées
|
||||
|
||||
Ce document liste toutes les corrections de sécurité et d'hygiène de code appliquées le 2025-10-12.
|
||||
|
||||
---
|
||||
|
||||
## 🔐 1. Sécurisation de l'Upload Planning
|
||||
|
||||
### Problème identifié
|
||||
- Aucune validation de type de fichier
|
||||
- Pas de limite de taille
|
||||
- Risque de path traversal via `childId`
|
||||
|
||||
### Corrections appliquées
|
||||
|
||||
**Fichier**: `backend/src/routes/uploads.ts`
|
||||
|
||||
#### A. FileFilter avec whitelist stricte
|
||||
```typescript
|
||||
const planFileFilter = (_req: Request, file: Express.Multer.File, callback: FileFilterCallback) => {
|
||||
const allowedMimeTypes = [
|
||||
'image/jpeg', 'image/jpg', 'image/png', 'image/webp',
|
||||
'application/pdf',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
];
|
||||
|
||||
const allowedExtensions = /\.(jpg|jpeg|png|webp|pdf|xls|xlsx)$/i;
|
||||
const hasValidMime = allowedMimeTypes.includes(file.mimetype);
|
||||
const hasValidExt = allowedExtensions.test(file.originalname);
|
||||
|
||||
if (hasValidMime && hasValidExt) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error('Type de fichier non autorisé'));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### B. Limite de taille de fichier
|
||||
```typescript
|
||||
const planUpload = multer({
|
||||
storage: planStorage,
|
||||
fileFilter: planFileFilter,
|
||||
limits: {
|
||||
fileSize: 10 * 1024 * 1024 // 10MB max
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### C. Validation UUID pour prévenir path traversal
|
||||
```typescript
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (!rawChildId || !uuidRegex.test(rawChildId)) {
|
||||
console.warn("[uploads] invalid or missing childId:", rawChildId);
|
||||
res.status(400).json({ message: "Parametre childId manquant ou invalide" });
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 2. Suppression de secrets.json et migration vers .env
|
||||
|
||||
### Problème identifié
|
||||
- Clé OpenAI stockée en clair dans `backend/src/data/secrets.json`
|
||||
- Fichier commité dans Git (même si .gitignore l'exclut maintenant)
|
||||
|
||||
### Corrections appliquées
|
||||
|
||||
#### A. Fichier secrets.json supprimé
|
||||
```bash
|
||||
rm backend/src/data/secrets.json
|
||||
```
|
||||
|
||||
#### B. Fichier .env créé
|
||||
**Fichier**: `backend/.env`
|
||||
|
||||
```env
|
||||
# API Keys
|
||||
# ⚠️ IMPORTANT: CETTE CLÉ A ÉTÉ EXPOSÉE ET DOIT ÊTRE RÉVOQUÉE!
|
||||
# 1. Aller sur https://platform.openai.com/api-keys
|
||||
# 2. Révoquer la clé commençant par sk-proj-efaTQ8...
|
||||
# 3. Créer une nouvelle clé
|
||||
# 4. Remplacer la valeur ci-dessous
|
||||
OPENAI_API_KEY=sk-proj-...
|
||||
OPENAI_MODEL=gpt-4o
|
||||
```
|
||||
|
||||
#### C. Code déjà configuré pour lire depuis .env
|
||||
Le code existant dans `backend/src/services/secret-store.ts` et `backend/src/config/env.ts` lit déjà depuis les variables d'environnement.
|
||||
|
||||
### ⚠️ ACTION REQUISE PAR L'UTILISATEUR
|
||||
|
||||
**CRITIQUE**: La clé OpenAI a été exposée dans Git et doit être révoquée:
|
||||
|
||||
1. **Aller sur**: https://platform.openai.com/api-keys
|
||||
2. **Révoquer**: La clé commençant par `sk-proj-efaTQ8cicJYU7k8RG...`
|
||||
3. **Créer**: Une nouvelle clé API
|
||||
4. **Remplacer**: La valeur dans `backend/.env`
|
||||
|
||||
---
|
||||
|
||||
## 🌐 3. Sécurisation du Service d'Ingestion
|
||||
|
||||
### Problèmes identifiés
|
||||
- CORS permissif (`allow_origins=["*"]`)
|
||||
- Endpoint `/config/openai` expose les clés en production
|
||||
|
||||
### Corrections appliquées
|
||||
|
||||
**Fichier**: `ingestion-service/src/ingestion/main.py`
|
||||
|
||||
#### A. CORS restreint par environnement
|
||||
```python
|
||||
_env = os.getenv("NODE_ENV", "development")
|
||||
_allowed_origins = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:5173,http://localhost:5000").split(",")
|
||||
|
||||
if _env == "production":
|
||||
# Production: strict CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=_allowed_origins,
|
||||
allow_methods=["GET", "POST"],
|
||||
allow_headers=["Content-Type"],
|
||||
allow_credentials=False,
|
||||
)
|
||||
else:
|
||||
# Development: permissive
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
#### B. Endpoint /config/openai désactivé en production
|
||||
```python
|
||||
@app.post("/config/openai")
|
||||
async def set_openai_config(api_key: str = Body(..., embed=True), model: str | None = Body(None)) -> dict:
|
||||
if _env == "production":
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Configuration endpoint disabled in production. Use environment variables instead."
|
||||
)
|
||||
# ... reste du code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧹 4. Nettoyage du Repository
|
||||
|
||||
### Problèmes identifiés
|
||||
- Fichiers `.js` dupliqués dans `frontend/src/` (générés par compilation)
|
||||
- Fichiers `*.bak`, `*.bak2`, `*.bak3`
|
||||
- `dist/` potentiellement commité
|
||||
- Données PII dans `backend/src/data/client.json` et fichiers uploadés
|
||||
|
||||
### Corrections appliquées
|
||||
|
||||
#### A. Suppression des fichiers obsolètes
|
||||
```bash
|
||||
# Suppression de tous les .js dans frontend/src/
|
||||
find family-planner/frontend/src -name "*.js" -type f -delete
|
||||
|
||||
# Suppression de tous les .bak*
|
||||
find family-planner -name "*.bak*" -type f -delete
|
||||
```
|
||||
|
||||
#### B. Mise à jour du .gitignore
|
||||
**Fichier**: `.gitignore`
|
||||
|
||||
Ajouté:
|
||||
```gitignore
|
||||
# Data files with PII (Personally Identifiable Information)
|
||||
backend/src/data/client.json
|
||||
**/public/plans/
|
||||
**/public/avatars/
|
||||
```
|
||||
|
||||
Les lignes suivantes étaient déjà présentes:
|
||||
- `dist/` (ligne 7)
|
||||
- `backend/src/data/secrets.json` (ligne 29)
|
||||
- `*.bak`, `*.backup` (lignes 36-37)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Statistiques
|
||||
|
||||
### Fichiers modifiés
|
||||
- `backend/src/routes/uploads.ts` - Sécurisation upload
|
||||
- `backend/src/services/file-db.ts` - Ajout type personalLeaves
|
||||
- `backend/src/services/personal-leave-service.ts` - Correction imports
|
||||
- `backend/.env` - Nouveau fichier de configuration
|
||||
- `ingestion-service/src/ingestion/main.py` - CORS et endpoints
|
||||
- `frontend/src/state/ChildrenContext.tsx` - Ajout schoolRegion type
|
||||
- `.gitignore` - Protection données PII
|
||||
|
||||
### Fichiers supprimés
|
||||
- `backend/src/data/secrets.json` (1 fichier)
|
||||
- Tous les `.js` dans `frontend/src/` (35 fichiers)
|
||||
- Tous les `.bak*` (3 fichiers)
|
||||
|
||||
### Tests
|
||||
- ✅ Backend compile sans erreur (`npm run build`)
|
||||
- ✅ Frontend compile sans erreur (`npm run build`)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Recommandations futures
|
||||
|
||||
### Court terme (Sprint actuel)
|
||||
1. ✅ Révoquer la clé OpenAI exposée
|
||||
2. ⏳ Ajouter tests backend pour routes critiques (supertest)
|
||||
3. ⏳ Implémenter rate limiting par IP pour uploads
|
||||
|
||||
### Moyen terme (2-3 sprints)
|
||||
1. Ajouter authentification utilisateur (JWT)
|
||||
2. Implémenter encryption au repos pour données PII
|
||||
3. Ajouter logs d'audit pour opérations sensibles
|
||||
|
||||
### Long terme (Roadmap)
|
||||
1. Migration vers base de données (SQLite → Postgres)
|
||||
2. Intégration avec solution de secrets management (Azure Key Vault, AWS Secrets Manager)
|
||||
3. Mise en place CI/CD avec scans de sécurité automatiques
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist de déploiement
|
||||
|
||||
Avant de déployer en production:
|
||||
|
||||
- [ ] Révoquer ancienne clé OpenAI
|
||||
- [ ] Créer nouvelle clé OpenAI
|
||||
- [ ] Configurer variable `OPENAI_API_KEY` en production
|
||||
- [ ] Configurer variable `NODE_ENV=production` pour ingestion service
|
||||
- [ ] Configurer variable `ALLOWED_ORIGINS` avec domaines production
|
||||
- [ ] Vérifier que `.env` n'est PAS commité
|
||||
- [ ] Vérifier que `dist/` n'est PAS commité
|
||||
- [ ] Vérifier que `client.json` n'est PAS commité
|
||||
- [ ] Tester upload avec fichiers valides et invalides
|
||||
- [ ] Tester que `/config/openai` retourne 403 en production
|
||||
|
||||
---
|
||||
|
||||
**Date**: 2025-10-12
|
||||
**Auteur**: Claude (Assistant IA)
|
||||
**Validé par**: Philippe H.
|
||||
Reference in New Issue
Block a user