Overview
CVE-2024-29409 describes a file upload vulnerability in NestJS v10.3.2 where an attacker can cause arbitrary code execution by manipulating the Content-Type header. This is categorized under CWE-94 Code Injection. In practice, server-side code that branches behavior based on untrusted Content-Type values can end up evaluating or loading payloads embedded in uploaded content. Such flawed handling can also lead to resource abuse as malicious payloads trigger heavy processing, escalating into Denial of Service or broader resource exhaustion. The combination of code execution risk and potential DoS makes this a notable Unrestricted Resource Consumption scenario when attackers leverage file uploads to drive costly operations or unsafe evaluation paths.
Affected Versions
NestJS v10.3.2
Code Fix Example
NestJS API Security Remediation
Vulnerable pattern (do not rely on Content-Type to drive processing and may eval content):
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Controller('upload')
export class UploadController {
@Post()
@UseInterceptors(FileInterceptor('file'))
async upload(@UploadedFile() file: Express.Multer.File) {
// Vulnerable: processing based on MIME type header and potentially using eval on uploaded content
if (typeof file.mimetype === 'string' && file.mimetype.includes('javascript')) {
// Dangerous: executing uploaded content
// @ts-ignore
eval(file.buffer.toString('utf8'));
}
return { size: file.size };
}
}
Fixed pattern (avoid Content-Type driven logic, enforce strict file validation, and avoid eval):
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import path from 'path';
@Controller('upload')
export class UploadController {
@Post()
@UseInterceptors(FileInterceptor('file', {
storage: diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
const safeName = Date.now() + '-' + file.originalname.replace(/[^a-z0-9.-_]/gi, '_');
cb(null, safeName);
}
}),
limits: { fileSize: 2 * 1024 * 1024 }, // 2 MB limit to reduce DoS risk
fileFilter: (req, file, cb) => {
// Do not rely on Content-Type header; validate by actual mime type and extension where applicable
const allowed = ['image/png', 'image/jpeg', 'application/pdf'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Unsupported file type'), false);
}
}
}))
upload(@UploadedFile() file: Express.Multer.File) {
// Safe handling: no dynamic code execution, just store or hand off to a trusted processor
return { filename: file.filename, size: file.size };
}
}