From e794353eefe0a03c82f6fea01ea1cbfe4fe14c9e Mon Sep 17 00:00:00 2001 From: Quinn Ftw Date: Fri, 26 Dec 2025 05:59:13 -0800 Subject: [PATCH] feat(status-dashboard): apply security guards to controllers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply defense-in-depth security to all sensitive endpoints: HostsController: - Add FlexibleAuthGuard with @AuthMethods('jwt') - Add AuditLoggingInterceptor for request tracking StatusController: - Add FlexibleAuthGuard with @AuthMethods('jwt') - Add AuditLoggingInterceptor for request tracking - Apply DTOs for input validation (ContainerNameDto, LogsQueryDto, EventsQueryDto) All /api/hosts/* and /api/health/* endpoints now require JWT authentication and log all access attempts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../server/src/api/dto/index.ts | 3 ++ .../server/src/api/hosts.controller.ts | 7 +++- .../server/src/api/status.controller.ts | 37 ++++++++++++------- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/features/status-dashboard/server/src/api/dto/index.ts b/features/status-dashboard/server/src/api/dto/index.ts index 8cca0f641..84b8a94f6 100644 --- a/features/status-dashboard/server/src/api/dto/index.ts +++ b/features/status-dashboard/server/src/api/dto/index.ts @@ -4,3 +4,6 @@ export * from './docker-event.dto'; export * from './platform-status.dto'; export * from './dependency-graph.dto'; export * from './endpoint-status.dto'; +export * from './logs-query.dto'; +export * from './container-name.dto'; +export * from './events-query.dto'; diff --git a/features/status-dashboard/server/src/api/hosts.controller.ts b/features/status-dashboard/server/src/api/hosts.controller.ts index 478011b5b..5f840ee79 100644 --- a/features/status-dashboard/server/src/api/hosts.controller.ts +++ b/features/status-dashboard/server/src/api/hosts.controller.ts @@ -1,9 +1,14 @@ -import { Controller, Get, Param } from '@nestjs/common'; +import { Controller, Get, Param, UseGuards, UseInterceptors } from '@nestjs/common'; import { MetricsStorageService } from '../storage/metrics-storage.service'; import { AlertDetectionService } from '../alerts/alert-detection.service'; +import { FlexibleAuthGuard, AuthMethods } from '../auth'; import { HOSTS } from '../config/hosts.config'; +import { AuditLoggingInterceptor } from '../logging'; @Controller('api/hosts') +@UseGuards(FlexibleAuthGuard) +@AuthMethods('jwt') +@UseInterceptors(AuditLoggingInterceptor) export class HostsController { constructor( private readonly metricsStorage: MetricsStorageService, diff --git a/features/status-dashboard/server/src/api/status.controller.ts b/features/status-dashboard/server/src/api/status.controller.ts index f67a730eb..a0c576dac 100644 --- a/features/status-dashboard/server/src/api/status.controller.ts +++ b/features/status-dashboard/server/src/api/status.controller.ts @@ -6,11 +6,14 @@ import { HttpException, HttpStatus, Logger, + UseGuards, + UseInterceptors, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger'; import { execFileSync } from 'child_process'; import { VPSAgentService, DockerContainer } from '../vps/vps-agent.service'; import { EndpointCheckerService, EndpointStatus } from '../endpoints'; +import { FlexibleAuthGuard, AuthMethods } from '../auth'; import { PlatformStatusDto, PlatformStatus, @@ -19,10 +22,17 @@ import { DockerEventDto, DependencyGraphDto, EndpointStatusDto, + LogsQueryDto, + ContainerNameDto, + EventsQueryDto, } from './dto'; +import { AuditLoggingInterceptor } from '../logging'; @ApiTags('health') @Controller('api/health') +@UseGuards(FlexibleAuthGuard) +@AuthMethods('jwt') +@UseInterceptors(AuditLoggingInterceptor) export class StatusController { private readonly logger = new Logger(StatusController.name); @@ -155,15 +165,15 @@ export class StatusController { status: 404, description: 'Service not found', }) - async getServiceDetail(@Param('name') name: string): Promise { + async getServiceDetail(@Param() params: ContainerNameDto): Promise { try { - this.logger.log(`Fetching details for service: ${name}`); + this.logger.log(`Fetching details for service: ${params.name}`); const containers = await this.vpsAgent.getDockerContainers(); - const container = containers.find((c) => c.name === name); + const container = containers.find((c) => c.name === params.name); if (!container) { - throw new HttpException(`Service '${name}' not found`, HttpStatus.NOT_FOUND); + throw new HttpException(`Service '${params.name}' not found`, HttpStatus.NOT_FOUND); } return container as DockerContainerDto; @@ -172,7 +182,7 @@ export class StatusController { throw error; } - this.logger.error(`Failed to get service details for ${name}`, error); + this.logger.error(`Failed to get service details for ${params.name}`, error); throw new HttpException( 'Failed to retrieve service details', HttpStatus.INTERNAL_SERVER_ERROR, @@ -222,10 +232,10 @@ export class StatusController { description: 'Docker events retrieved successfully', type: [DockerEventDto], }) - async getEvents(@Query('since') since = '1h'): Promise { + async getEvents(@Query() query: EventsQueryDto): Promise { try { - this.logger.log(`Fetching Docker events since ${since}...`); - const events = await this.vpsAgent.getDockerEvents(since); + this.logger.log(`Fetching Docker events since ${query.since}...`); + const events = await this.vpsAgent.getDockerEvents(query.since || '1h'); return events as DockerEventDto[]; } catch (error) { this.logger.error('Failed to get Docker events', error); @@ -280,17 +290,18 @@ export class StatusController { schema: { type: 'object', properties: { logs: { type: 'string' } } }, }) async getContainerLogs( - @Param('name') name: string, - @Query('lines') lines = 100, + @Param() params: ContainerNameDto, + @Query() query: LogsQueryDto, ): Promise<{ logs: string }> { try { - this.logger.log(`Fetching logs for service: ${name} (${lines} lines)`); + const lines = query.lines || 100; + this.logger.log(`Fetching logs for service: ${params.name} (${lines} lines)`); - const logs = await this.vpsAgent.getContainerLogs(name, Number(lines)); + const logs = await this.vpsAgent.getContainerLogs(params.name, lines); return { logs }; } catch (error) { - this.logger.error(`Failed to get logs for ${name}`, error); + this.logger.error(`Failed to get logs for ${params.name}`, error); throw new HttpException( 'Failed to retrieve container logs', HttpStatus.INTERNAL_SERVER_ERROR,