Skip to content

Commit 4cdf835

Browse files
committed
fix: implement all LOW severity fixes with 19 tests (167 total)
- Update tsconfig target from es5 to es2020 for modern JS features - Remove X-Powered-By header to prevent server fingerprinting - Add Permissions-Policy header (camera, microphone, geolocation denied) - Add CORS preflight cache (maxAge: 86400) to reduce OPTIONS requests - Rename auth.gaurd.ts to auth.guard.ts and update all 11 controller imports - Add WebSocket authentication with JWT verification on connection - Add session ID format validation to prevent injection attacks - Replace console.log/error/warn with NestJS Logger in critical services: - FailedWebhookRetry usecase - UploadCleanupScheduler service - AuthController - Normalize error responses in production (exception filter hides internals) - Add 19 new tests covering all LOW severity fixes https://claude.ai/code/session_01KCbLtPYD29xF1zCFukZmbQ
1 parent 64e90c4 commit 4cdf835

21 files changed

Lines changed: 329 additions & 74 deletions

File tree

apps/api/src/app/activity/activity.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ValidateMongoId } from '@shared/validations/valid-mongo-id.validation';
44

55
import { UploadSummary, UploadHistory, RetryUpload, WebhookLogs } from './usecases';
66
import { ACCESS_KEY_NAME, Defaults } from '@impler/shared';
7-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
7+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
88
import { isDateString } from '@shared/helpers/common.helper';
99

1010
@Controller('/activity')

apps/api/src/app/auth/auth.controller.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ClassSerializerInterceptor,
77
Controller,
88
Get,
9+
Logger,
910
Post,
1011
Put,
1112
Res,
@@ -47,6 +48,8 @@ import {
4748
@ApiExcludeController()
4849
@UseInterceptors(ClassSerializerInterceptor)
4950
export class AuthController {
51+
private readonly logger = new Logger(AuthController.name);
52+
5053
constructor(
5154
private verify: Verify,
5255
private resendOTP: ResendOTP,
@@ -99,7 +102,7 @@ export class AuthController {
99102

100103
return response.redirect(url);
101104
} catch (error) {
102-
console.error('[AuthController] GitHub OAuth callback failed:', error?.message || error);
105+
this.logger.error(`GitHub OAuth callback failed: ${error?.message || error}`);
103106

104107
return response.redirect(`${process.env.WEB_BASE_URL}/auth/signin?error=AuthenticationError`);
105108
}

apps/api/src/app/column/column.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Controller, Put, Param, Body, UseGuards, Post, Delete } from '@nestjs/c
33
import { ValidateMongoId } from '@shared/validations/valid-mongo-id.validation';
44

55
import { ACCESS_KEY_NAME } from '@impler/shared';
6-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
6+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
77
import { ColumnRequestDto } from './dtos/column-request.dto';
88
import { ColumnResponseDto } from './dtos/column-response.dto';
99
import { AddColumn, UpdateColumn, DeleteColumn } from './usecases';

apps/api/src/app/common/common.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '@nestjs/common';
1414

1515
import { ACCESS_KEY_NAME, IImportConfig } from '@impler/shared';
16-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
16+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
1717
import { ValidRequestDto, SignedUrlDto } from './dtos';
1818
import { ValidImportFile } from '@shared/validations/valid-import-file.validation';
1919
import { ValidRequestCommand, GetSignedUrl, ValidRequest, GetImportConfig, GetSheetNames } from './usecases';

apps/api/src/app/failed-webhook-request-retry/usecase/failed-webhook-request-retry.usecase.ts

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { Injectable, Logger } from '@nestjs/common';
22
import { Cron, CronExpression } from '@nestjs/schedule';
33

44
import { QueuesEnum } from '@impler/shared';
@@ -7,6 +7,7 @@ import { FailedWebhookRetryRequestsEntity, FailedWebhookRetryRequestsRepository
77

88
@Injectable()
99
export class FailedWebhookRetry {
10+
private readonly logger = new Logger(FailedWebhookRetry.name);
1011
constructor(
1112
private failedWebhookRetryRequestsRepository: FailedWebhookRetryRequestsRepository = new FailedWebhookRetryRequestsRepository(),
1213
private queueService: QueueService
@@ -18,20 +19,20 @@ export class FailedWebhookRetry {
1819
const memUsageStart = process.memoryUsage();
1920
const cpuUsageStart = process.cpuUsage();
2021

21-
console.log('========================================');
22-
console.log(`[FAILED-WEBHOOK-RETRY] Cron Started at ${startTime.toISOString()}`);
23-
console.log(`[FAILED-WEBHOOK-RETRY] Memory Usage (Start): RSS=${(memUsageStart.rss / 1024 / 1024).toFixed(2)}MB, Heap=${(memUsageStart.heapUsed / 1024 / 1024).toFixed(2)}MB`);
24-
console.log('========================================');
22+
this.logger.log('========================================');
23+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Cron Started at ${startTime.toISOString()}`);
24+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Memory Usage (Start): RSS=${(memUsageStart.rss / 1024 / 1024).toFixed(2)}MB, Heap=${(memUsageStart.heapUsed / 1024 / 1024).toFixed(2)}MB`);
25+
this.logger.log('========================================');
2526

2627
try {
2728
const failedWebhooks: FailedWebhookRetryRequestsEntity[] = await this.failedWebhookRetryRequestsRepository.find({
2829
nextRequestTime: { $lt: new Date() },
2930
});
3031

31-
console.log(`[FAILED-WEBHOOK-RETRY] Found ${failedWebhooks.length} failed webhooks to retry`);
32+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Found ${failedWebhooks.length} failed webhooks to retry`);
3233

3334
if (!failedWebhooks.length) {
34-
console.log(`[FAILED-WEBHOOK-RETRY] No webhooks to process, exiting`);
35+
this.logger.log(`[FAILED-WEBHOOK-RETRY] No webhooks to process, exiting`);
3536
return;
3637
}
3738

@@ -47,49 +48,49 @@ export class FailedWebhookRetry {
4748
const memUsageEnd = process.memoryUsage();
4849
const cpuUsageEnd = process.cpuUsage(cpuUsageStart);
4950

50-
console.log('========================================');
51-
console.log(`[FAILED-WEBHOOK-RETRY] Cron Completed at ${endTime.toISOString()}`);
52-
console.log(`[FAILED-WEBHOOK-RETRY] Results - Successful: ${successful}, Failed: ${failed}, Total: ${failedWebhooks.length}`);
53-
console.log(`[FAILED-WEBHOOK-RETRY] Processing Duration: ${processDuration}ms`);
54-
console.log(`[FAILED-WEBHOOK-RETRY] Total Duration: ${duration}ms`);
55-
console.log(`[FAILED-WEBHOOK-RETRY] Memory Usage (End): RSS=${(memUsageEnd.rss / 1024 / 1024).toFixed(2)}MB, Heap=${(memUsageEnd.heapUsed / 1024 / 1024).toFixed(2)}MB`);
56-
console.log(`[FAILED-WEBHOOK-RETRY] Memory Delta: RSS=${((memUsageEnd.rss - memUsageStart.rss) / 1024 / 1024).toFixed(2)}MB, Heap=${((memUsageEnd.heapUsed - memUsageStart.heapUsed) / 1024 / 1024).toFixed(2)}MB`);
57-
console.log(`[FAILED-WEBHOOK-RETRY] CPU Usage: User=${(cpuUsageEnd.user / 1000).toFixed(2)}ms, System=${(cpuUsageEnd.system / 1000).toFixed(2)}ms`);
51+
this.logger.log('========================================');
52+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Cron Completed at ${endTime.toISOString()}`);
53+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Results - Successful: ${successful}, Failed: ${failed}, Total: ${failedWebhooks.length}`);
54+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Processing Duration: ${processDuration}ms`);
55+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Total Duration: ${duration}ms`);
56+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Memory Usage (End): RSS=${(memUsageEnd.rss / 1024 / 1024).toFixed(2)}MB, Heap=${(memUsageEnd.heapUsed / 1024 / 1024).toFixed(2)}MB`);
57+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Memory Delta: RSS=${((memUsageEnd.rss - memUsageStart.rss) / 1024 / 1024).toFixed(2)}MB, Heap=${((memUsageEnd.heapUsed - memUsageStart.heapUsed) / 1024 / 1024).toFixed(2)}MB`);
58+
this.logger.log(`[FAILED-WEBHOOK-RETRY] CPU Usage: User=${(cpuUsageEnd.user / 1000).toFixed(2)}ms, System=${(cpuUsageEnd.system / 1000).toFixed(2)}ms`);
5859

5960
if (duration > 5000) {
60-
console.warn(`[FAILED-WEBHOOK-RETRY] ⚠️ WARNING: Cron execution took ${duration}ms (>5s threshold)`);
61+
this.logger.warn(`[FAILED-WEBHOOK-RETRY] ⚠️ WARNING: Cron execution took ${duration}ms (>5s threshold)`);
6162
}
6263

6364
if (failedWebhooks.length > 100) {
64-
console.warn(`[FAILED-WEBHOOK-RETRY] ⚠️ WARNING: Processing large batch of ${failedWebhooks.length} webhooks`);
65+
this.logger.warn(`[FAILED-WEBHOOK-RETRY] ⚠️ WARNING: Processing large batch of ${failedWebhooks.length} webhooks`);
6566
}
6667

67-
console.log('========================================');
68+
this.logger.log('========================================');
6869
} catch (error) {
6970
const endTime = new Date();
7071
const duration = endTime.getTime() - startTime.getTime();
7172

72-
console.error('========================================');
73-
console.error(`[FAILED-WEBHOOK-RETRY] ❌ ERROR at ${endTime.toISOString()}`);
74-
console.error(`[FAILED-WEBHOOK-RETRY] Duration before error: ${duration}ms`);
75-
console.error('[FAILED-WEBHOOK-RETRY] Error details:', error);
76-
console.error('========================================');
73+
this.logger.error('========================================');
74+
this.logger.error(`[FAILED-WEBHOOK-RETRY] ❌ ERROR at ${endTime.toISOString()}`);
75+
this.logger.error(`[FAILED-WEBHOOK-RETRY] Duration before error: ${duration}ms`);
76+
this.logger.error('[FAILED-WEBHOOK-RETRY] Error details:', error);
77+
this.logger.error('========================================');
7778
throw error;
7879
}
7980
}
8081

8182
private async processWebhook(webhook: FailedWebhookRetryRequestsEntity) {
8283
const webhookStartTime = Date.now();
8384
try {
84-
console.log(`[FAILED-WEBHOOK-RETRY] Processing webhook - ID: ${webhook._id}, Time: ${new Date().toISOString()}`);
85+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Processing webhook - ID: ${webhook._id}, Time: ${new Date().toISOString()}`);
8586

8687
this.queueService.publishToQueue(QueuesEnum.SEND_FAILED_WEBHOOK_DATA, webhook._id as string);
8788

8889
const webhookDuration = Date.now() - webhookStartTime;
89-
console.log(`[FAILED-WEBHOOK-RETRY] Webhook queued - ID: ${webhook._id}, Duration: ${webhookDuration}ms`);
90+
this.logger.log(`[FAILED-WEBHOOK-RETRY] Webhook queued - ID: ${webhook._id}, Duration: ${webhookDuration}ms`);
9091
} catch (error) {
9192
const webhookDuration = Date.now() - webhookStartTime;
92-
console.error(`[FAILED-WEBHOOK-RETRY] ❌ Error processing webhook - ID: ${webhook._id}, Duration: ${webhookDuration}ms, Time: ${new Date().toISOString()}`, error);
93+
this.logger.error(`[FAILED-WEBHOOK-RETRY] ❌ Error processing webhook - ID: ${webhook._id}, Duration: ${webhookDuration}ms, Time: ${new Date().toISOString()}`, error);
9394
throw error;
9495
}
9596
}

apps/api/src/app/import-jobs/import-jobs.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
UserJobTerminate,
1313
} from './usecase';
1414
import { ACCESS_KEY_NAME } from '@impler/shared';
15-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
15+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
1616
import { UpdateJobDto, CreateUserJobDto, UpdateJobMappingDto } from './dtos';
1717

1818
@ApiTags('Import Jobs')

apps/api/src/app/mapping/mapping.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Param, ParseArrayPipe, Post, UseGuards } from '@
22
import { ApiTags, ApiSecurity, ApiOperation, ApiBody } from '@nestjs/swagger';
33
import { ACCESS_KEY_NAME, Defaults, ITemplateSchemaItem, UploadStatusEnum } from '@impler/shared';
44

5-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
5+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
66
import { validateNotFound } from '@shared/helpers/common.helper';
77
import { validateUploadStatus } from '@shared/helpers/upload.helpers';
88
import { GetUpload } from '@shared/usecases/get-upload/get-upload.usecase';

apps/api/src/app/project/project.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
} from './usecases';
3838
import { AuthService } from 'app/auth/services/auth.service';
3939
import { CONSTANTS, COOKIE_CONFIG } from '@shared/constants';
40-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
40+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
4141
import { EnvironmentResponseDto } from 'app/environment/dtos/environment-response.dto';
4242

4343
@Controller('/project')

apps/api/src/app/review/review.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from '@nestjs/common';
1414

1515
import { APIMessages } from '@shared/constants';
16-
import { JwtAuthGuard } from '@shared/framework/auth.gaurd';
16+
import { JwtAuthGuard } from '@shared/framework/auth.guard';
1717
import { validateUploadStatus } from '@shared/helpers/upload.helpers';
1818
import { Defaults, ACCESS_KEY_NAME, UploadStatusEnum, ReviewDataTypesEnum } from '@impler/shared';
1919

apps/api/src/app/shared/filters/exception.filter.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
1-
import { Catch, ArgumentsHost, HttpServer, HttpException } from '@nestjs/common';
1+
import { Catch, ArgumentsHost, HttpServer, HttpException, Logger } from '@nestjs/common';
22
import { AbstractHttpAdapter, BaseExceptionFilter } from '@nestjs/core';
33
import * as Sentry from '@sentry/node';
44

55
@Catch()
66
export class SentryFilter extends BaseExceptionFilter {
7+
private readonly logger = new Logger(SentryFilter.name);
8+
79
catch(exception: unknown, host: ArgumentsHost): void {
810
// Only report non-HTTP exceptions (unexpected errors) to Sentry
911
if (!(exception instanceof HttpException)) {
1012
Sentry.captureException(exception);
1113
}
14+
15+
// In production, normalize unexpected error responses to avoid leaking internals
16+
if (process.env.NODE_ENV === 'production' && !(exception instanceof HttpException)) {
17+
this.logger.error('Unhandled exception', exception instanceof Error ? exception.stack : String(exception));
18+
const ctx = host.switchToHttp();
19+
const response = ctx.getResponse();
20+
if (response && typeof response.status === 'function') {
21+
response.status(500).json({
22+
statusCode: 500,
23+
message: 'Internal server error',
24+
});
25+
26+
return;
27+
}
28+
}
29+
1230
super.catch(exception, host);
1331
}
1432

@@ -18,6 +36,22 @@ export class SentryFilter extends BaseExceptionFilter {
1836
applicationRef: HttpServer<any, any> | AbstractHttpAdapter<any, any, any>
1937
): void {
2038
Sentry.captureException(exception);
39+
40+
// In production, normalize unknown errors
41+
if (process.env.NODE_ENV === 'production') {
42+
this.logger.error('Unknown error', exception?.stack || String(exception));
43+
const ctx = host.switchToHttp();
44+
const response = ctx.getResponse();
45+
if (response && typeof response.status === 'function') {
46+
response.status(500).json({
47+
statusCode: 500,
48+
message: 'Internal server error',
49+
});
50+
51+
return;
52+
}
53+
}
54+
2155
super.handleUnknownError(exception, host, applicationRef);
2256
}
2357
}

0 commit comments

Comments
 (0)