Skip to content

File upload security: no content scanning before storage (MIME spoofing, ZIP bombs, macros) #2169

@SonoTommy

Description

@SonoTommy

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

  1. Add a scan step inside FilesLocalService and FilesS3PresignedService before committing to storage
  2. Add pompelmi to the docs under file-uploading.md as the recommended scanning approach
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions