Compare commits

...

4 Commits

Author SHA1 Message Date
Mathis HERRIOT
13f372390b
feat(backend): add cookie parser and CSRF protection middleware 2025-05-17 10:33:38 +02:00
Mathis HERRIOT
4028cebb63
feat(users): enhance exportUserData with projects, groups, persons, and collaborations 2025-05-17 10:33:24 +02:00
Mathis HERRIOT
c1a74d712b
feat: add cookie-parser and csurf dependencies to backend package.json and update pnpm-lock.yaml 2025-05-17 10:33:17 +02:00
Mathis HERRIOT
a4a259f119
feat(docs): update project status with completed security tasks
Mark input validation and CSRF protection as completed and adjust progress, timelines, and priorities accordingly.
2025-05-17 10:33:03 +02:00
5 changed files with 195 additions and 15 deletions

View File

@ -38,6 +38,8 @@
"@node-rs/argon2": "^2.0.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"cookie-parser": "^1.4.7",
"csurf": "^1.11.0",
"dotenv": "^16.5.0",
"drizzle-orm": "^0.30.4",
"jose": "^6.0.11",

View File

@ -2,6 +2,8 @@ import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as cookieParser from 'cookie-parser';
import * as csurf from 'csurf';
import { AppModule } from './app.module';
async function bootstrap() {
@ -17,8 +19,34 @@ async function bootstrap() {
}),
);
// Configuration CORS selon l'environnement
// Configure cookie parser
app.use(cookieParser());
// Get environment configuration
const environment = configService.get<string>('NODE_ENV', 'development');
// Configure CSRF protection
if (environment !== 'test') { // Skip CSRF in test environment
app.use(csurf({
cookie: {
httpOnly: true,
sameSite: 'strict',
secure: environment === 'production'
}
}));
// Add CSRF token to response
app.use((req, res, next) => {
res.cookie('XSRF-TOKEN', req.csrfToken?.() || '', {
httpOnly: false, // Client-side JavaScript needs to read this
sameSite: 'strict',
secure: environment === 'production'
});
next();
});
}
// Configuration CORS selon l'environnement
const frontendUrl = configService.get<string>('FRONTEND_URL', 'http://localhost:3001');
if (environment === 'development') {

View File

@ -1,5 +1,5 @@
import { Injectable, NotFoundException, Inject } from '@nestjs/common';
import { eq } from 'drizzle-orm';
import { eq, inArray } from 'drizzle-orm';
import { DRIZZLE } from '../../../database/database.module';
import * as schema from '../../../database/schema';
import { CreateUserDto } from '../dto/create-user.dto';
@ -111,17 +111,59 @@ export class UsersService {
*/
async exportUserData(id: string) {
const user = await this.findById(id);
// Get all projects owned by the user
const projects = await this.db
.select()
.from(schema.projects)
.where(eq(schema.projects.ownerId, id));
// Add empty groups and persons arrays for compatibility with tests
// Get all project IDs
const projectIds = projects.map(project => project.id);
// Get all persons in user's projects
const persons = projectIds.length > 0
? await this.db
.select()
.from(schema.persons)
.where(inArray(schema.persons.projectId, projectIds))
: [];
// Get all groups in user's projects
const groups = projectIds.length > 0
? await this.db
.select()
.from(schema.groups)
.where(inArray(schema.groups.projectId, projectIds))
: [];
// Get all project collaborations where the user is a collaborator
const collaborations = await this.db
.select({
collaboration: schema.projectCollaborators,
project: schema.projects
})
.from(schema.projectCollaborators)
.innerJoin(
schema.projects,
eq(schema.projectCollaborators.projectId, schema.projects.id)
)
.where(eq(schema.projectCollaborators.userId, id));
return {
user,
projects,
groups: [],
persons: []
groups,
persons,
collaborations: collaborations.map(c => ({
id: c.collaboration.id,
projectId: c.collaboration.projectId,
project: {
id: c.project.id,
name: c.project.name,
description: c.project.description
}
}))
};
}
}

View File

@ -92,9 +92,9 @@ Nous avons élaboré un plan de bataille complet pour l'implémentation du backe
- [x] Mettre en place le service WebSocket pour la gestion des connexions
##### Sécurité et Conformité RGPD
- [ ] Implémenter la validation des entrées avec class-validator
- [x] Implémenter la validation des entrées avec class-validator
- [x] Configurer CORS pour sécuriser les API
- [ ] Mettre en place la protection contre les attaques CSRF
- [x] Mettre en place la protection contre les attaques CSRF
- [x] Implémenter les fonctionnalités d'export de données utilisateur (RGPD) dans le backend
- [ ] Implémenter l'interface frontend pour l'export de données utilisateur
- [x] Implémenter le renouvellement du consentement utilisateur dans le backend
@ -184,9 +184,9 @@ Nous avons élaboré un plan de bataille complet pour l'implémentation du backe
- Documenter tous les endpoints API ✅
- Générer une documentation interactive ✅
3. **Sécurité**
- Implémenter la validation des entrées avec class-validator
- Mettre en place la protection contre les attaques CSRF
3. **Sécurité**
- Implémenter la validation des entrées avec class-validator
- Mettre en place la protection contre les attaques CSRF
### Frontend (Priorité Haute)
1. **Conformité RGPD**
@ -216,7 +216,7 @@ Nous avons élaboré un plan de bataille complet pour l'implémentation du backe
| Backend - Tests Unitaires | 100% |
| Backend - Tests e2e | 100% |
| Backend - Documentation API | 100% |
| Backend - Sécurité et RGPD | 67% |
| Backend - Sécurité et RGPD | 100% |
| Frontend - Structure de Base | 100% |
| Frontend - Pages et Composants | 100% |
| Frontend - Authentification | 100% |
@ -231,10 +231,10 @@ Nous avons élaboré un plan de bataille complet pour l'implémentation du backe
Basé sur l'état d'avancement actuel et les tâches restantes, l'estimation du temps nécessaire pour compléter le projet est la suivante:
- **Backend**: ~3-4 jours
- **Backend**: ~1-2 jours
- Tests e2e: ✅ Terminé
- Documentation API avec Swagger: ✅ Terminé
- Sécurité (validation des entrées, CSRF): 1-2 jours
- Sécurité (validation des entrées, CSRF): ✅ Terminé
- Finalisation des fonctionnalités RGPD: 1-2 jours
- **Frontend**: ~3 semaines
@ -279,12 +279,12 @@ Cependant, plusieurs aspects importants restent à finaliser:
1. **Conformité RGPD**: Bien que les fonctionnalités backend pour l'export de données et le renouvellement du consentement soient implémentées, les interfaces frontend correspondantes sont manquantes.
2. **Sécurité**: Des améliorations de sécurité comme la validation des entrées et la protection CSRF sont encore à implémenter. La configuration CORS a été mise en place avec des paramètres différents pour les environnements de développement et de production.
2. **Sécurité**: Les améliorations de sécurité comme la validation des entrées et la protection CSRF ont été implémentées. La configuration CORS a été mise en place avec des paramètres différents pour les environnements de développement et de production.
3. **Optimisations frontend**: Des optimisations de performance, une meilleure expérience mobile et des tests frontend sont nécessaires pour offrir une expérience utilisateur optimale.
Les prochaines étapes prioritaires devraient se concentrer sur:
1. Implémenter les interfaces frontend pour la conformité RGPD
2. Renforcer la sécurité du backend
2. Optimiser les performances du frontend
En suivant ces recommandations, le projet pourra atteindre un niveau de qualité production dans les 3-4 semaines à venir, offrant une application complète, sécurisée et conforme aux normes actuelles.

108
pnpm-lock.yaml generated
View File

@ -46,6 +46,12 @@ importers:
class-validator:
specifier: ^0.14.2
version: 0.14.2
cookie-parser:
specifier: ^1.4.7
version: 1.4.7
csurf:
specifier: ^1.11.0
version: 1.11.0
dotenv:
specifier: ^16.5.0
version: 16.5.0
@ -3223,10 +3229,21 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-parser@1.4.7:
resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==}
engines: {node: '>= 0.8.0'}
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie-signature@1.2.2:
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
engines: {node: '>=6.6.0'}
cookie@0.4.0:
resolution: {integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==}
engines: {node: '>= 0.6'}
cookie@0.7.2:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
@ -3262,9 +3279,18 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
csrf@3.1.0:
resolution: {integrity: sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==}
engines: {node: '>= 0.8'}
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
csurf@1.11.0:
resolution: {integrity: sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==}
engines: {node: '>= 0.8.0'}
deprecated: This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions
d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
@ -3371,6 +3397,10 @@ packages:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
depd@1.1.2:
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
engines: {node: '>= 0.6'}
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
@ -4010,6 +4040,10 @@ packages:
http-cache-semantics@4.2.0:
resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
http-errors@1.7.3:
resolution: {integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==}
engines: {node: '>= 0.6'}
http-errors@2.0.0:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
@ -5041,6 +5075,10 @@ packages:
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
engines: {node: '>=10'}
random-bytes@1.0.0:
resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==}
engines: {node: '>= 0.8'}
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
@ -5202,6 +5240,9 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
rndm@1.2.0:
resolution: {integrity: sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==}
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
@ -5267,6 +5308,9 @@ packages:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'}
setprototypeof@1.1.1:
resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==}
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@ -5373,6 +5417,10 @@ packages:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
@ -5554,6 +5602,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
toidentifier@1.0.0:
resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==}
engines: {node: '>=0.6'}
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
@ -5628,6 +5680,10 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
tsscmp@1.0.6:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
tw-animate-css@1.2.9:
resolution: {integrity: sha512-9O4k1at9pMQff9EAcCEuy1UNO43JmaPQvq+0lwza9Y0BQ6LB38NiMj+qHqjoQf40355MX+gs6wtlR6H9WsSXFg==}
@ -5673,6 +5729,10 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
uid-safe@2.1.5:
resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==}
engines: {node: '>= 0.8'}
uid2@0.0.4:
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
@ -8875,8 +8935,17 @@ snapshots:
convert-source-map@2.0.0: {}
cookie-parser@1.4.7:
dependencies:
cookie: 0.7.2
cookie-signature: 1.0.6
cookie-signature@1.0.6: {}
cookie-signature@1.2.2: {}
cookie@0.4.0: {}
cookie@0.7.2: {}
cookiejar@2.1.4: {}
@ -8920,8 +8989,21 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
csrf@3.1.0:
dependencies:
rndm: 1.2.0
tsscmp: 1.0.6
uid-safe: 2.1.5
csstype@3.1.3: {}
csurf@1.11.0:
dependencies:
cookie: 0.4.0
cookie-signature: 1.0.6
csrf: 3.1.0
http-errors: 1.7.3
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
@ -8997,6 +9079,8 @@ snapshots:
delayed-stream@1.0.0: {}
depd@1.1.2: {}
depd@2.0.0: {}
dequal@2.0.3: {}
@ -9705,6 +9789,14 @@ snapshots:
http-cache-semantics@4.2.0: {}
http-errors@1.7.3:
dependencies:
depd: 1.1.2
inherits: 2.0.4
setprototypeof: 1.1.1
statuses: 1.5.0
toidentifier: 1.0.0
http-errors@2.0.0:
dependencies:
depd: 2.0.0
@ -10807,6 +10899,8 @@ snapshots:
quick-lru@5.1.1: {}
random-bytes@1.0.0: {}
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
@ -10963,6 +11057,8 @@ snapshots:
reusify@1.1.0: {}
rndm@1.2.0: {}
router@2.2.0:
dependencies:
debug: 4.4.1
@ -11049,6 +11145,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
setprototypeof@1.1.1: {}
setprototypeof@1.2.0: {}
sharp@0.34.1:
@ -11204,6 +11302,8 @@ snapshots:
dependencies:
escape-string-regexp: 2.0.0
statuses@1.5.0: {}
statuses@2.0.1: {}
streamsearch@1.1.0: {}
@ -11393,6 +11493,8 @@ snapshots:
dependencies:
is-number: 7.0.0
toidentifier@1.0.0: {}
toidentifier@1.0.1: {}
token-types@6.0.0:
@ -11472,6 +11574,8 @@ snapshots:
tslib@2.8.1: {}
tsscmp@1.0.6: {}
tw-animate-css@1.2.9: {}
type-check@0.4.0:
@ -11511,6 +11615,10 @@ snapshots:
typescript@5.8.3: {}
uid-safe@2.1.5:
dependencies:
random-bytes: 1.0.0
uid2@0.0.4: {}
uid@2.0.2: