Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 27x 27x 27x 27x 145x 145x 151x 151x 151x 105x 151x 105x 105x 152x 18x 134x | import { ExecutionContext, Injectable, UnauthorizedException, ForbiddenException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private readonly config?: ConfigService) {
super();
}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const method = request.method;
// CSRF protection for state-changing methods (POST, PUT, DELETE, PATCH)
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(method)) {
this.validateCsrf(request);
}
return super.canActivate(context) as any;
}
private validateCsrf(request: any): void {
// Skip CSRF validation in test mode
if (process.env.NODE_ENV === 'test' || process.env.TEST_MODE === '1') {
return;
}
const origin = request.headers.origin;
const referer = request.headers.referer;
const frontendUrl = this.config?.get<string>('FRONTEND_URL');
// If FRONTEND_URL is configured, validate Origin header matches
Iif (frontendUrl) {
try {
const expectedOrigin = new URL(frontendUrl).origin.toLowerCase();
if (origin) {
const requestOrigin = new URL(origin).origin.toLowerCase();
Iif (requestOrigin !== expectedOrigin) {
throw new ForbiddenException('CSRF validation failed: Invalid origin');
}
} else if (referer) {
// Fallback to Referer header if Origin is missing
const refererOrigin = new URL(referer).origin.toLowerCase();
Iif (refererOrigin !== expectedOrigin) {
throw new ForbiddenException('CSRF validation failed: Invalid referer');
}
} else {
// No Origin or Referer header - require X-Requested-With header as additional protection
// This header cannot be set by simple HTML forms (CSRF protection)
const requestedWith = request.headers['x-requested-with'];
Iif (!requestedWith || requestedWith.toLowerCase() !== 'xmlhttprequest') {
throw new ForbiddenException('CSRF validation failed: Missing required headers');
}
}
} catch (error) {
Iif (error instanceof ForbiddenException) {
throw error;
}
// If URL parsing fails, fall through to X-Requested-With check
const requestedWith = request.headers['x-requested-with'];
Iif (!requestedWith || requestedWith.toLowerCase() !== 'xmlhttprequest') {
throw new ForbiddenException('CSRF validation failed: Invalid request format');
}
}
}
}
handleRequest(err: any, user: any, _info: any) {
if (err || !user) {
// Hide token parsing specifics; standardize error
throw new UnauthorizedException('Authentication required');
}
return user;
}
} |