test(files): upload filters fail closed on an excluded or unresolvable type

This commit is contained in:
jubnl
2026-09-22 01:21:57 +02:00
parent fc49c0c768
commit fdb12aed1e
4 changed files with 150 additions and 19 deletions
+10 -3
View File
@@ -56,14 +56,21 @@ export function filesUploadFileFilter(allowedTypes: AllowedFileTypesService): Op
// allowed-extension list is now an async read. The decision runs in a
// detached async function that always answers through `cb` — multer holds
// the file stream until it does (make-middleware.js says as much). A
// rejection refuses the file, so the filter still fails closed.
// rejection refuses the file, so the filter still fails closed. Only the
// await is wrapped in try/catch, so a throw from cb() itself is never
// re-routed into a second cb() call.
void (async () => {
const allowed = (await allowedTypes.get()).split(',').map((e) => e.trim().toLowerCase());
let allowed: string[];
try {
allowed = (await allowedTypes.get()).split(',').map((e) => e.trim().toLowerCase());
} catch {
return reject();
}
const fileExt = ext.replace('.', '');
// Video is accepted as media regardless of the admin doc-types allowlist (#823).
if (allowed.includes(fileExt) || isVideoExtension(fileExt) || (allowed.includes('*') && !BLOCKED_EXTENSIONS.includes(ext))) return cb(null, true);
reject();
})().catch(() => reject());
})();
};
}
+18 -14
View File
@@ -78,23 +78,27 @@ export function journeyImageFileFilter(allowedTypes: AllowedFileTypesService): O
return cb(err);
}
const ext = path.extname(file.originalname).toLowerCase().replace('.', '');
// R1.5: multer's fileFilter is a callback API that cannot await, and the
// allowed-extension list is now an async read. The decision runs in a
// detached async function that always answers through `cb`; a rejection
// refuses the file, so the filter still fails closed.
void (async () => {
const allowed = (await allowedTypes.get()).split(',').map((e) => e.trim().toLowerCase());
if (!allowed.includes('*') && !allowed.includes(ext)) {
const err: Error & { statusCode?: number } = new Error(`File type .${ext} is not allowed`);
err.statusCode = 400;
return cb(err);
}
cb(null, true);
})().catch(() => {
const reject = () => {
const err: Error & { statusCode?: number } = new Error(`File type .${ext} is not allowed`);
err.statusCode = 400;
cb(err);
});
};
// R1.5: multer's fileFilter is a callback API that cannot await, and the
// allowed-extension list is now an async read. The decision runs in a
// detached async function that always answers through `cb`; a rejection
// refuses the file, so the filter still fails closed. Only the await is
// wrapped in try/catch, so a throw from cb() itself is never re-routed
// into a second cb() call.
void (async () => {
let allowed: string[];
try {
allowed = (await allowedTypes.get()).split(',').map((e) => e.trim().toLowerCase());
} catch {
return reject();
}
if (!allowed.includes('*') && !allowed.includes(ext)) return reject();
cb(null, true);
})();
};
}