Multer-Upload-Errors: 415/413 statt 500
Pentest 2026-05-30 INFO: Upload-Endpoints lieferten 500 statt
sauberem 4xx, wenn der fileFilter den MIME-Type ablehnte
(z.B. WebP/GIF, die gar nicht in der Allowlist standen) oder
LIMIT_FILE_SIZE getroffen wurde.
Ursache: fileFilter rief cb(new Error(...)) – multer wirft das
weiter, und ohne dedizierten Error-Handler endete es als 500
"Interner Serverfehler" mit Stack-Trace im Log.
Fix:
- WebP + GIF in die Allowlist von upload.routes.ts (Bug-Pen-
test-Erwartung des Reporters).
- Globaler Express-Error-Handler in index.ts unterscheidet jetzt:
* MulterError code=LIMIT_FILE_SIZE → 413 "Datei ist zu groß"
* andere MulterError → 400 "Upload-Fehler: ..."
* Error mit "...erlaubt"-Message → 415 mit Original-Message
* sonst → bisheriger 4xx/500-Pfad
Live-verifiziert:
WebP/GIF/JPG → 200
SVG / text/plain → 415 + klare Message
11 MB PDF → 413 "Datei ist zu groß"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+30
-3
@@ -422,9 +422,36 @@ if (process.env.NODE_ENV === 'production') {
|
|||||||
// body-parser wirft 413 (PayloadTooLargeError) bzw. 400 (SyntaxError) mit einem
|
// body-parser wirft 413 (PayloadTooLargeError) bzw. 400 (SyntaxError) mit einem
|
||||||
// `status`-Feld. Ohne Respektierung werden legitime Client-Fehler als 500
|
// `status`-Feld. Ohne Respektierung werden legitime Client-Fehler als 500
|
||||||
// kaschiert und landen als "Interner Serverfehler" beim User.
|
// kaschiert und landen als "Interner Serverfehler" beim User.
|
||||||
app.use((err: Error & { status?: number; type?: string }, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
//
|
||||||
console.error(err.stack);
|
// Multer-Errors werden hier sauber abgefangen statt als 500 zu enden:
|
||||||
const status = typeof err.status === 'number' && err.status >= 400 && err.status < 600 ? err.status : 500;
|
// - MulterError (z.B. LIMIT_FILE_SIZE) → 413
|
||||||
|
// - LIMIT_UNEXPECTED_FILE / -PART_COUNT etc. → 400
|
||||||
|
// - fileFilter-Errors (unzulässiger Typ) → 415
|
||||||
|
// Pentest 2026-05-30, INFO: WebP/GIF-Uploads (und alle anderen vom
|
||||||
|
// fileFilter abgelehnten Typen) lieferten vorher 500 mit Stack-Trace.
|
||||||
|
app.use((err: any, req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
||||||
|
console.error(err?.stack || err);
|
||||||
|
|
||||||
|
// Multer-spezifische Fehler (importiert as namespace)
|
||||||
|
if (err?.name === 'MulterError') {
|
||||||
|
const code = err.code;
|
||||||
|
if (code === 'LIMIT_FILE_SIZE') {
|
||||||
|
return res.status(413).json({ success: false, error: 'Datei ist zu groß' });
|
||||||
|
}
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Upload-Fehler: ' + (err.message || code || 'unbekannt'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileFilter hat cb(new Error(...), false) gerufen – kein MulterError,
|
||||||
|
// sondern unsere eigene Reject-Nachricht ("Nur PDF, JPG, ... erlaubt").
|
||||||
|
const msg = typeof err?.message === 'string' ? err.message : '';
|
||||||
|
if (/sind erlaubt|nicht erlaubt/i.test(msg)) {
|
||||||
|
return res.status(415).json({ success: false, error: msg });
|
||||||
|
}
|
||||||
|
|
||||||
|
const status = typeof err?.status === 'number' && err.status >= 400 && err.status < 600 ? err.status : 500;
|
||||||
let message = 'Interner Serverfehler';
|
let message = 'Interner Serverfehler';
|
||||||
if (status === 413) message = 'Anfrage zu groß';
|
if (status === 413) message = 'Anfrage zu groß';
|
||||||
else if (status === 400 && (err.type === 'entity.parse.failed' || err instanceof SyntaxError)) {
|
else if (status === 400 && (err.type === 'entity.parse.failed' || err instanceof SyntaxError)) {
|
||||||
|
|||||||
@@ -38,12 +38,18 @@ const fileFilter = (
|
|||||||
file: Express.Multer.File,
|
file: Express.Multer.File,
|
||||||
cb: multer.FileFilterCallback
|
cb: multer.FileFilterCallback
|
||||||
) => {
|
) => {
|
||||||
// Nur PDFs und Bilder erlauben
|
// PDFs + gängige Web-Bildformate. WebP + GIF nachgezogen 2026-05-30
|
||||||
const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png', 'image/jpg'];
|
// (Pentest INFO: WebP/GIF lieferten 500 statt sauberem 4xx, weil
|
||||||
|
// erlaubter MIME-Type fehlte und der fileFilter dann throwte).
|
||||||
|
const allowedTypes = [
|
||||||
|
'application/pdf',
|
||||||
|
'image/jpeg', 'image/jpg', 'image/png',
|
||||||
|
'image/gif', 'image/webp',
|
||||||
|
];
|
||||||
if (allowedTypes.includes(file.mimetype)) {
|
if (allowedTypes.includes(file.mimetype)) {
|
||||||
cb(null, true);
|
cb(null, true);
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('Nur PDF, JPG und PNG Dateien sind erlaubt'));
|
cb(new Error('Nur PDF, JPG, PNG, GIF und WebP-Dateien sind erlaubt'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user