Skip to content

feat: add secure file upload with virus scanning via pompelmi#316

Open
Gauravkachwaha wants to merge 3 commits into
hagopj13:masterfrom
Gauravkachwaha:feature/secure-file-upload
Open

feat: add secure file upload with virus scanning via pompelmi#316
Gauravkachwaha wants to merge 3 commits into
hagopj13:masterfrom
Gauravkachwaha:feature/secure-file-upload

Conversation

@Gauravkachwaha

@Gauravkachwaha Gauravkachwaha commented Apr 26, 2026

Copy link
Copy Markdown

Summary

Adds a secure file upload endpoint (POST /v1/upload) with virus scanning via pompelmi.

Builds on the earlier upload discussion in #204 and implements the secure-by-default approach proposed in #312.

Changes

  • src/controllers/upload.controller.js — scans file buffer using scanBytes() before accepting
  • src/routes/v1/upload.route.jsmulter.memoryStorage(), MIME type allowlist, and file size validation
  • src/validations/upload.validation.js — validation schema following existing pattern
  • src/routes/v1/index.js — registered /upload route
  • tests/integration/upload.test.js — added integration tests for clean, malicious, suspicious, and invalid upload cases

Security Approach

  • Uses memoryStorage() so files never touch disk before scanning
  • scanBytes() runs in-process with no external API calls
  • Blocks malicious and suspicious verdicts (failClosed: true)
  • MIME type allowlist enforced at Multer layer

Closes #312

@SonoTommy

Copy link
Copy Markdown

Hey @Gaurav8957999847, thanks for implementing this so cleanly!
The memoryStorage approach is exactly right.

A few small things before we merge:

1. Timeout — if clamd is unreachable, scanBytes will hang.
Add a timeout:

const report = await scanBytes(req.file.buffer, {
  host: STRICT_PUBLIC_UPLOAD.host,
  port: STRICT_PUBLIC_UPLOAD.port,
  timeout: 5000
});

2. Error handling — if clamd is down, scanBytes throws.
Wrap in try/catch and return 503:

try {
  const report = await scanBytes(req.file.buffer, options);
} catch (err) {
  return res.status(503).json({ 
    message: 'Virus scanner unavailable — try again later' 
  });
}

3. Handle ScanError verdict — pompelmi returns three verdicts:
Verdict.Clean, Verdict.Malicious, and Verdict.ScanError.
The current implementation only checks for clean/malicious —
add a case for ScanError and treat it as a rejection:

if (report.verdict === Verdict.ScanError) {
  return res.status(422).json({ 
    message: 'Scan incomplete — file rejected as precaution' 
  });
}

4. README prerequisite — add a note that clamd must be running
and reachable at the configured host/port. Devs cloning the
boilerplate need to know this upfront.
Docs on setup: https://github.com/pompelmi/pompelmi/blob/main/docs/docker.md

Everything else looks great. Happy to approve once these are in! 🍊

@Gauravkachwaha

Copy link
Copy Markdown
Author

Hi @hagopj13 , just following up on this PR.

The requested changes have been addressed, and @SonoTommy has left an approving review saying the timeout handling, ScanError verdict handling, and README prerequisite note look correct.

Please let me know if anything else is needed from my side before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add file upload route with built-in security scanning (multer + pompelmi)

2 participants