The multer filename kept path.extname(file.originalname), while the
fileFilter only checked the spoofable client-supplied mimetype. An
attacker could send Content-Type: image/png with originalname x.html,
landing an .html file in /uploads that express.static serves as
text/html — same-origin stored XSS.
- Store the extension from a whitelist keyed by the accepted mimetype
(MIME_EXT), never from originalname. The fileFilter uses the same map
as its single source of truth, so only mimetypes with a safe mapped
extension pass.
- Use crypto.randomBytes for the random filename component.
- Serve /uploads with an explicit X-Content-Type-Options: nosniff
(defense in depth alongside helmet's global setting).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>