fix(securite): lock down the two unauthenticated emergency endpoints

- POST /api/emergency-admin-reset accepted a fallback secret hardcoded in
  the repository, letting anyone who read the source reset the production
  admin account to admin/admin (EMERGENCY_SECRET is not set in the shipped
  docker-compose, so the fallback was live). The route now returns 404
  unless EMERGENCY_SECRET is explicitly configured, and invalid attempts
  are logged.
- POST /api/admin/emergency-migration ran database migrations with no
  authentication at all; it now requires an authenticated admin.

A sweep of the remaining API surface found no other unauthenticated
mutation or read routes beyond /api/health. .env.example documents
EMERGENCY_SECRET and ENCRYPTION_KEY.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHdE9nEh8zHhQNGpCgrtYe
This commit is contained in:
Claude committed 2026-08-14 16:21:40 +00:00
1 parent 37a8d2b40c
commit 0a816fa881
3 files changed
+28 -7

No files matched your search

+12 -5
View File
@@ -74,14 +74,21 @@ async function registerProductionRoutes(app: Express): Promise<void> {
});
});
// Emergency admin reset endpoint (production only)
// Emergency admin reset endpoint (production only)
// SÉCURITÉ : n'existe que si EMERGENCY_SECRET est défini dans l'environnement.
// L'ancien secret par défaut était codé en dur dans ce fichier, donc lisible
// par quiconque accède au dépôt — n'importe qui pouvait réinitialiser le
// compte admin en production.
app.post('/api/emergency-admin-reset', async (req: Request, res: Response) => {
try {
const emergencySecret = process.env.EMERGENCY_SECRET;
if (!emergencySecret) {
return res.status(404).json({ error: 'Not found' });
}
const { secret } = req.body;
// Require emergency secret
const emergencySecret = process.env.EMERGENCY_SECRET || 'logiflow-admin-reset-2025';
if (secret !== emergencySecret) {
if (!secret || secret !== emergencySecret) {
console.warn('🚨 Tentative de reset admin avec un secret invalide');
return res.status(403).json({ error: 'Invalid emergency secret' });
}
+7 -1
View File
@@ -5558,8 +5558,14 @@ RÉSUMÉ DU SCAN
});
// Emergency migration route for SAV priority column
app.post('/api/admin/emergency-migration', async (req, res) => {
// SÉCURITÉ : réservée aux administrateurs authentifiés (était accessible sans login)
app.post('/api/admin/emergency-migration', isAuthenticated, async (req: any, res) => {
try {
const user = await storage.getUser(req.user.claims ? req.user.claims.sub : req.user.id);
if (!user || user.role !== 'admin') {
return res.status(403).json({ message: "Insufficient permissions" });
}
console.log('🚨 EMERGENCY: Forcing SAV migration execution...');
// Import migration function