hagopj13 / hagopj13/node-express-boilerplate

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

Open
#312 10 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
7.7k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

## Add file upload route with built-in content scanning

This picks up where issue #204 left off. That issue proposed adding file upload via multer, which makes sense. This issue proposes adding it with security scanning built in from the start, so the boilerplate ships with a safe default rather than a bare upload route that developers have to secure themselves.

### Why secure-by-default matters here

A boilerplate is copied and deployed as-is more often than it should be. If the upload route ships without content inspection, every project based on this boilerplate inherits the same gap:

- **MIME spoofing**: a file named `document.pdf` with a malicious payload passes extension and client-reported MIME checks
- **ZIP bombs**: a compressed file that expands to exhaust memory or disk during later processing
- **Office macros / active PDF content**: macro-enabled files or PDFs with embedded JavaScript actions
- **Polyglot files**: valid in multiple formats simultaneously, bypass type-based routing

These are not theoretical — they are the most common real-world upload attack vectors.

### Proposed implementation

Add a `/upload` route using `multer` (memoryStorage, not diskStorage — scan before touching disk) with [pompelmi](https://github.com/pompelmi/pompelmi) as the scan step:

```js
const multer = require('multer');
const { scanBytes, STRICT_PUBLIC_UPLOAD } = require('pompelmi');

const upload = multer({ storage: multer.memoryStorage() });

router.post('/upload', auth(), upload.single('file'), async (req, res) => {
const report = await scanBytes(req.file.buffer, {
filename: req.file.originalname,
mimeType: req.file.mimetype,
policy: STRICT_PUBLIC_UPLOAD,
failClosed: true,
});

if (report.verdict !== 'clean') {
return res.status(422).json({
code: httpStatus.UNPROCESSABLE_ENTITY,
message: `Upload blocked: ${report.reasons.join(', ')}`,
});
}

// proceed to storage (S3, disk, etc.)
res.status(200).json({ message: 'File accepted', filename: req.file.originalname });
});
```

This uses `memoryStorage` intentionally: the file is scanned in-process before any write to disk or cloud storage. pompelmi runs with zero external API calls — no cloud service, no daemon, files never leave the process.

### Scope of the PR I can submit

1. New `upload.route.js` + `upload.controller.js` following the existing project structure
2. Validation middleware for file type and size using the existing `validate` pattern
3. Tests following the existing Mocha/chai/supertest pattern
4. README section documenting the upload endpoint

Happy to submit the PR immediately — let me know if the scope looks right or if you'd prefer a narrower starting point.

---

**References**
- Picks up from: #204
- pompelmi repo: https://github.com/pompelmi/pompelmi
- License: MIT
- Coverage: Node Weekly #594, Help Net Security, Stack Overflow Blog

Contributor guide

Open the contributing guide

Research direction

Start by reviewing issue #204 and the existing route, controller, and validation structure before adding the proposed upload.route.js and upload.controller.js. Follow the existing Mocha/chai/supertest pattern, then document the endpoint in the README; done means the route validates, scans, handles blocked files, and has passing tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
express, javascript, node.js
Domain
api, backend, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.