refactor: atualizações e remoção de módulos não utilizados
This commit is contained in:
@@ -50,7 +50,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
describe('canActivate', () => {
|
||||
/**
|
||||
* NOTA: Estes testes identificam problemas no método canActivate.
|
||||
*
|
||||
*
|
||||
* PROBLEMAS IDENTIFICADOS:
|
||||
* 1. Não valida se IP extraído é válido
|
||||
* 2. Não valida se rate limiting service retorna dados válidos
|
||||
@@ -196,7 +196,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
|
||||
mockGetRequest.mockReturnValue(request);
|
||||
mockRateLimitingService.isAllowed.mockRejectedValue(
|
||||
new Error('Erro de conexão com Redis')
|
||||
new Error('Erro de conexão com Redis'),
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -225,7 +225,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
mockGetRequest.mockReturnValue(request);
|
||||
mockRateLimitingService.isAllowed.mockResolvedValue(false);
|
||||
mockRateLimitingService.getAttemptInfo.mockRejectedValue(
|
||||
new Error('Erro ao buscar informações')
|
||||
new Error('Erro ao buscar informações'),
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -336,7 +336,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
const result = await guard.canActivate(mockExecutionContext);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('192.168.1.1');
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
|
||||
'192.168.1.1',
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle concurrent requests with same IP', async () => {
|
||||
@@ -363,7 +365,7 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
results.forEach(result => {
|
||||
results.forEach((result) => {
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -394,7 +396,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
fail('Deveria ter lançado exceção');
|
||||
} catch (error) {
|
||||
expect(error).toBeInstanceOf(HttpException);
|
||||
expect((error as HttpException).getStatus()).toBe(HttpStatus.TOO_MANY_REQUESTS);
|
||||
expect((error as HttpException).getStatus()).toBe(
|
||||
HttpStatus.TOO_MANY_REQUESTS,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -419,7 +423,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
fail('Deveria ter lançado exceção');
|
||||
} catch (error) {
|
||||
const response = (error as HttpException).getResponse() as any;
|
||||
expect(response.error).toBe('Muitas tentativas de login. Tente novamente em alguns minutos.');
|
||||
expect(response.error).toBe(
|
||||
'Muitas tentativas de login. Tente novamente em alguns minutos.',
|
||||
);
|
||||
expect(response.success).toBe(false);
|
||||
}
|
||||
});
|
||||
@@ -512,7 +518,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
const result = await guard.canActivate(mockExecutionContext);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('2001:0db8:85a3:0000:0000:8a2e:0370:7334');
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
|
||||
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject invalid IPv6 format', async () => {
|
||||
@@ -556,7 +564,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
|
||||
await guard.canActivate(mockExecutionContext);
|
||||
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('192.168.1.1');
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
|
||||
'192.168.1.1',
|
||||
);
|
||||
});
|
||||
|
||||
it('should fallback to connection.remoteAddress when x-forwarded-for is missing', async () => {
|
||||
@@ -572,7 +582,9 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
|
||||
await guard.canActivate(mockExecutionContext);
|
||||
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith('10.0.0.1');
|
||||
expect(mockRateLimitingService.isAllowed).toHaveBeenCalledWith(
|
||||
'10.0.0.1',
|
||||
);
|
||||
});
|
||||
|
||||
it('should use default IP when all sources are missing', async () => {
|
||||
@@ -603,4 +615,3 @@ describe('RateLimitingGuard - Tests that expose problems', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { RateLimitingService } from '../services/rate-limiting.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -19,7 +25,8 @@ export class RateLimitingGuard implements CanActivate {
|
||||
try {
|
||||
isAllowed = await this.rateLimitingService.isAllowed(ip);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
@@ -30,13 +37,14 @@ export class RateLimitingGuard implements CanActivate {
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!isAllowed) {
|
||||
let attemptInfo;
|
||||
try {
|
||||
attemptInfo = await this.rateLimitingService.getAttemptInfo(ip);
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
@@ -49,11 +57,12 @@ export class RateLimitingGuard implements CanActivate {
|
||||
}
|
||||
|
||||
this.validateAttemptInfo(attemptInfo);
|
||||
|
||||
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
error: 'Muitas tentativas de login. Tente novamente em alguns minutos.',
|
||||
error:
|
||||
'Muitas tentativas de login. Tente novamente em alguns minutos.',
|
||||
data: null,
|
||||
details: {
|
||||
attempts: attemptInfo.attempts,
|
||||
@@ -73,13 +82,16 @@ export class RateLimitingGuard implements CanActivate {
|
||||
* @returns Endereço IP do cliente ou '127.0.0.1' se não encontrado
|
||||
*/
|
||||
private getClientIp(request: any): string {
|
||||
const forwardedFor = request.headers['x-forwarded-for']?.split(',')[0]?.trim();
|
||||
const forwardedFor = request.headers['x-forwarded-for']
|
||||
?.split(',')[0]
|
||||
?.trim();
|
||||
const realIp = request.headers['x-real-ip']?.trim();
|
||||
const connectionIp = request.connection?.remoteAddress;
|
||||
const socketIp = request.socket?.remoteAddress;
|
||||
const requestIp = request.ip;
|
||||
|
||||
const rawIp = forwardedFor || realIp || connectionIp || socketIp || requestIp;
|
||||
const rawIp =
|
||||
forwardedFor || realIp || connectionIp || socketIp || requestIp;
|
||||
|
||||
if (rawIp === null || rawIp === undefined) {
|
||||
return '';
|
||||
@@ -90,7 +102,7 @@ export class RateLimitingGuard implements CanActivate {
|
||||
}
|
||||
|
||||
const trimmedIp = rawIp.trim();
|
||||
|
||||
|
||||
if (trimmedIp === '') {
|
||||
return '';
|
||||
}
|
||||
@@ -144,7 +156,11 @@ export class RateLimitingGuard implements CanActivate {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ipv4Regex.test(ip) && !ipv6Regex.test(ip) && !ipv6CompressedRegex.test(ip)) {
|
||||
if (
|
||||
!ipv4Regex.test(ip) &&
|
||||
!ipv6Regex.test(ip) &&
|
||||
!ipv6CompressedRegex.test(ip)
|
||||
) {
|
||||
if (!this.isValidIpv4(ip) && !this.isValidIpv6(ip)) {
|
||||
throw new HttpException(
|
||||
{
|
||||
@@ -166,7 +182,7 @@ export class RateLimitingGuard implements CanActivate {
|
||||
const parts = ip.split('.');
|
||||
if (parts.length !== 4) return false;
|
||||
|
||||
return parts.every(part => {
|
||||
return parts.every((part) => {
|
||||
const num = parseInt(part, 10);
|
||||
return !isNaN(num) && num >= 0 && num <= 255;
|
||||
});
|
||||
@@ -180,17 +196,17 @@ export class RateLimitingGuard implements CanActivate {
|
||||
if (ip.includes('::')) {
|
||||
const parts = ip.split('::');
|
||||
if (parts.length > 2) return false;
|
||||
|
||||
|
||||
const leftParts = parts[0] ? parts[0].split(':') : [];
|
||||
const rightParts = parts[1] ? parts[1].split(':') : [];
|
||||
|
||||
return (leftParts.length + rightParts.length) <= 8;
|
||||
|
||||
return leftParts.length + rightParts.length <= 8;
|
||||
}
|
||||
|
||||
const parts = ip.split(':');
|
||||
if (parts.length !== 8) return false;
|
||||
|
||||
return parts.every(part => {
|
||||
return parts.every((part) => {
|
||||
if (!part) return false;
|
||||
return /^[0-9a-fA-F]{1,4}$/.test(part);
|
||||
});
|
||||
@@ -223,8 +239,11 @@ export class RateLimitingGuard implements CanActivate {
|
||||
);
|
||||
}
|
||||
|
||||
if (attemptInfo.remainingTime !== undefined &&
|
||||
(typeof attemptInfo.remainingTime !== 'number' || attemptInfo.remainingTime < 0)) {
|
||||
if (
|
||||
attemptInfo.remainingTime !== undefined &&
|
||||
(typeof attemptInfo.remainingTime !== 'number' ||
|
||||
attemptInfo.remainingTime < 0)
|
||||
) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
|
||||
Reference in New Issue
Block a user