diff --git a/backend/src/daily-reward/daily-reward.controller.ts b/backend/src/daily-reward/daily-reward.controller.ts index b0165685..47b2c2ba 100644 --- a/backend/src/daily-reward/daily-reward.controller.ts +++ b/backend/src/daily-reward/daily-reward.controller.ts @@ -1,10 +1,4 @@ -import { - Controller, - Post, - Body, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Controller, Post, Body } from '@nestjs/common'; import { DailyRewardService } from './daily-reward.service'; import { DailyCheckinDto } from './dto/daily-checkin.dto'; @@ -13,7 +7,7 @@ export class DailyRewardController { constructor(private readonly dailyRewardService: DailyRewardService) {} @Post('daily-checkin') - @UsePipes(new ValidationPipe({ transform: true })) + // Relies on the global validation pipe (issue #340). dailyCheckIn(@Body() dailyCheckinDto: DailyCheckinDto) { return this.dailyRewardService.dailyCheckIn(dailyCheckinDto.userId); } diff --git a/backend/src/main.ts b/backend/src/main.ts index 6370dd8a..4e34510e 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -73,12 +73,8 @@ async function bootstrap(): Promise { referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, })); - // Global request validation (#335). `whitelist` strips properties that are - // not declared on the request DTO, and `forbidNonWhitelisted` turns any - // remaining unknown property into a 400 so public APIs reject unexpected - // fields instead of silently ignoring them. Exception: handlers that must - // accept extra fields (e.g. third-party webhooks) can opt out with a local - // pipe, e.g. `@UsePipes(new ValidationPipe({ forbidNonWhitelisted: false }))`. + // Global validation policy (issue #340): unknown properties are stripped, + // DTOs are transformed, and all controllers share the same defaults. app.useGlobalPipes( new ValidationPipe({ whitelist: true, diff --git a/backend/src/puzzle-access-log/puzzle-access-log.controller.ts b/backend/src/puzzle-access-log/puzzle-access-log.controller.ts index dcbef9d2..14c37438 100644 --- a/backend/src/puzzle-access-log/puzzle-access-log.controller.ts +++ b/backend/src/puzzle-access-log/puzzle-access-log.controller.ts @@ -7,7 +7,6 @@ import { Query, ParseIntPipe, DefaultValuePipe, - ValidationPipe, } from '@nestjs/common'; import { PuzzleAccessLogService } from './puzzle-access-log.service'; import { LogAccessDto } from './dto/log-access.dto'; @@ -17,7 +16,8 @@ export class PuzzleAccessLogController { constructor(private readonly accessLogService: PuzzleAccessLogService) {} @Post('log') - logAccess(@Body(new ValidationPipe()) dto: LogAccessDto) { + // Relies on the global validation pipe (issue #340). + logAccess(@Body() dto: LogAccessDto) { return this.accessLogService.logAccess(dto); } diff --git a/backend/src/puzzle-fork/puzzle-fork.controller.ts b/backend/src/puzzle-fork/puzzle-fork.controller.ts index bc608b4e..039ab3ca 100644 --- a/backend/src/puzzle-fork/puzzle-fork.controller.ts +++ b/backend/src/puzzle-fork/puzzle-fork.controller.ts @@ -1,10 +1,4 @@ -import { - Controller, - Post, - Body, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Controller, Post, Body } from '@nestjs/common'; import { PuzzleForkService } from './puzzle-fork.service'; import { CreateForkDto } from './dto/create-fork.dto'; @@ -18,7 +12,7 @@ export class PuzzleForkController { * Ex: @UseGuards(AdminGuard) */ @Post('fork') - @UsePipes(new ValidationPipe({ transform: true })) + // Relies on the global validation pipe (issue #340). forkPuzzle(@Body() createForkDto: CreateForkDto) { return this.puzzleForkService.fork(createForkDto); } diff --git a/backend/src/puzzle-versioning/puzzle-versioning.controller.ts b/backend/src/puzzle-versioning/puzzle-versioning.controller.ts index 4f06efce..927994ee 100644 --- a/backend/src/puzzle-versioning/puzzle-versioning.controller.ts +++ b/backend/src/puzzle-versioning/puzzle-versioning.controller.ts @@ -1,12 +1,4 @@ -import { - Controller, - Get, - Post, - Body, - Param, - UsePipes, - ValidationPipe, -} from '@nestjs/common'; +import { Controller, Get, Post, Body, Param } from '@nestjs/common'; import { PuzzleVersioningService } from './puzzle-versioning.service'; import { CreatePuzzleVersionDto } from './dto/create-puzzle-version.dto'; @@ -22,7 +14,7 @@ export class PuzzleVersioningController { } @Post('versions') - @UsePipes(new ValidationPipe({ transform: true })) + // Relies on the global validation pipe (issue #340). createNewVersion(@Body() createPuzzleVersionDto: CreatePuzzleVersionDto) { return this.puzzleVersioningService.createNewVersion( createPuzzleVersionDto, diff --git a/backend/src/quiz/controllers/quiz.controller.ts b/backend/src/quiz/controllers/quiz.controller.ts index 4ad416e6..1ac5e840 100644 --- a/backend/src/quiz/controllers/quiz.controller.ts +++ b/backend/src/quiz/controllers/quiz.controller.ts @@ -10,7 +10,6 @@ import { HttpCode, HttpStatus, ParseUUIDPipe, - ValidationPipe, } from '@nestjs/common'; import { QuizService } from '../services/quiz.service'; import { CreateQuizDto } from '../dto/create-quiz.dto'; @@ -25,9 +24,7 @@ export class QuizController { @Post() @HttpCode(HttpStatus.CREATED) - async createQuiz( - @Body(ValidationPipe) createQuizDto: CreateQuizDto, - ): Promise { + async createQuiz(@Body() createQuizDto: CreateQuizDto): Promise { return this.quizService.createQuiz(createQuizDto); } @@ -67,9 +64,8 @@ export class QuizController { @Post('submit') @HttpCode(HttpStatus.OK) - async submitQuiz( - @Body(ValidationPipe) submitQuizDto: SubmitQuizDto, - ): Promise { + // Relies on the global validation pipe (issue #340). + async submitQuiz(@Body() submitQuizDto: SubmitQuizDto): Promise { return this.quizService.submitQuiz(submitQuizDto); } diff --git a/backend/src/report/report.controller.ts b/backend/src/report/report.controller.ts index 19e5c07d..0b7c96ae 100644 --- a/backend/src/report/report.controller.ts +++ b/backend/src/report/report.controller.ts @@ -18,6 +18,9 @@ import { Roles } from 'src/common/decorators/roles.decorator'; import { RolesGuard } from 'src/common/gaurds/roles.gaurds'; @Controller('report') +// Stricter than the global policy (issue #340): report payloads are small, +// stable shapes where unknown fields are rejected outright rather than +// silently stripped. Kept as a justified, explicit override. @UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true })) export class ReportController { constructor(private readonly reportService: ReportService) {} diff --git a/backend/src/user/user.controller.ts b/backend/src/user/user.controller.ts index 11949f9b..9c164f63 100644 --- a/backend/src/user/user.controller.ts +++ b/backend/src/user/user.controller.ts @@ -7,7 +7,6 @@ import { Param, UseGuards, } from '@nestjs/common'; -import { ValidationPipe } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, @@ -28,7 +27,8 @@ export class UserController { @Post() @ApiOperation({ summary: 'Register new user' }) @ApiResponse({ status: 201, description: 'User created' }) - create(@Body(new ValidationPipe({ whitelist: true })) dto: CreateUserDto) { + // Relies on the global validation pipe (issue #340). + create(@Body() dto: CreateUserDto) { return this.userService.createUser(dto); } @@ -36,8 +36,9 @@ export class UserController { @Patch('profile') @ApiBearerAuth() @ApiOperation({ summary: 'Update user profile' }) + // Relies on the global validation pipe (issue #340). updateProfile( - @Body(new ValidationPipe({ whitelist: true })) dto: UpdateUserProfileDto, + @Body() dto: UpdateUserProfileDto, @Param('id') /* or use custom decorator to get id */ id: string, ) { return this.userService.updateProfile(id, dto); @@ -47,10 +48,8 @@ export class UserController { @Post('link-wallet') @ApiBearerAuth() @ApiOperation({ summary: 'Link or update wallet address' }) - linkWallet( - @Body(new ValidationPipe({ whitelist: true })) dto: LinkWalletDto, - @Param('id') id: string, - ) { + // Relies on the global validation pipe (issue #340). + linkWallet(@Body() dto: LinkWalletDto, @Param('id') id: string) { return this.userService.linkWallet(id, dto); }