File upload security: no content scanning before storage
Context
This boilerplate ships with a well-structured file upload module supporting local and S3 drivers, including s3-presigned for production. The architecture is correct — the problem is that no step inspects file content before the record is committed to the database.
This means a client can upload:
- A file with a spoofed MIME type or extension (e.g. a script disguised as
avatar.jpg)
- A ZIP bomb that expands to exhaust resources when later extracted or processed
- An Office file with embedded macros, or a PDF with embedded JavaScript actions
- A polyglot file that is valid in multiple formats simultaneously
These bypass the current size and extension checks entirely, because they operate at the byte-content level — not at the filename or client-reported MIME level.
Natural integration point
For the local driver: scan req.file.buffer (already in memory via multer) before writing to disk.
For the s3-presigned driver: scan after the client uploads to the quarantine bucket and before promoting to the production bucket and saving the DB record.
Suggested tool
pompelmi is an MIT-licensed, in-process Node.js scanner with a dedicated NestJS integration package (@pompelmi/nestjs-integration, already listed in awesome-nestjs). Zero external API calls — files never leave the process.
Minimal NestJS guard using the existing module structure:
import { scanBytes, STRICT_PUBLIC_UPLOAD } from 'pompelmi';
// Inside your FilesService, before saving:
const report = await scanBytes(file.buffer, {
filename: file.originalname,
mimeType: file.mimetype,
policy: STRICT_PUBLIC_UPLOAD,
failClosed: true,
});
if (report.verdict !== 'clean') {
throw new UnprocessableEntityException({
status: HttpStatus.UNPROCESSABLE_ENTITY,
errors: { file: `blocked: ${report.reasons.join(', ')}` },
});
}
Proposed changes
- Add a scan step inside
FilesLocalService and FilesS3PresignedService before committing to storage
- Add pompelmi to the docs under file-uploading.md as the recommended scanning approach
- Optionally: expose scan policy as a config option so teams can tune sensitivity
Happy to submit a PR — let me know which scope works best.
References
File upload security: no content scanning before storage
Context
This boilerplate ships with a well-structured file upload module supporting local and S3 drivers, including s3-presigned for production. The architecture is correct — the problem is that no step inspects file content before the record is committed to the database.
This means a client can upload:
avatar.jpg)These bypass the current size and extension checks entirely, because they operate at the byte-content level — not at the filename or client-reported MIME level.
Natural integration point
For the local driver: scan
req.file.buffer(already in memory via multer) before writing to disk.For the s3-presigned driver: scan after the client uploads to the quarantine bucket and before promoting to the production bucket and saving the DB record.
Suggested tool
pompelmi is an MIT-licensed, in-process Node.js scanner with a dedicated NestJS integration package (
@pompelmi/nestjs-integration, already listed in awesome-nestjs). Zero external API calls — files never leave the process.Minimal NestJS guard using the existing module structure:
Proposed changes
FilesLocalServiceandFilesS3PresignedServicebefore committing to storageHappy to submit a PR — let me know which scope works best.
References