0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Handled uploads with invalid form bodies

fix https://linear.app/tryghost/issue/SLO-101/http-500-with-invalid-multipart-data

- previously, busboy would error out if we supplied a body that was
  invalid (such as an empty FormData)
- we would then return a HTTP 500 to the user, which causes all manner
  of problems
- now we catch errors from busboy and return a nice BadRequestError
This commit is contained in:
Daniel Lockyer 2024-05-08 10:37:57 +02:00 committed by Daniel Lockyer
parent ae88dc8548
commit d82b136a6a
3 changed files with 37 additions and 4 deletions

View file

@ -62,8 +62,8 @@ const single = name => function singleUploadFunction(req, res, next) {
singleUpload(req, res, (err) => { singleUpload(req, res, (err) => {
if (err) { if (err) {
// Multer or Dicer errors are usually caused by invalid file uploads // Busboy, Multer or Dicer errors are usually caused by invalid file uploads
if (err instanceof multer.MulterError || err.stack?.includes('dicer')) { if (err instanceof multer.MulterError || err.stack?.includes('dicer') || err.stack?.includes('busboy')) {
return next(new errors.BadRequestError({ return next(new errors.BadRequestError({
err err
})); }));
@ -105,8 +105,8 @@ const media = (fileName, thumbName) => function mediaUploadFunction(req, res, ne
mediaUpload(req, res, (err) => { mediaUpload(req, res, (err) => {
if (err) { if (err) {
// Multer or Dicer errors are usually caused by invalid file uploads // Busboy, Multer or Dicer errors are usually caused by invalid file uploads
if (err instanceof multer.MulterError || err.stack?.includes('dicer')) { if (err instanceof multer.MulterError || err.stack?.includes('dicer') || err.stack?.includes('busboy')) {
return next(new errors.BadRequestError({ return next(new errors.BadRequestError({
err err
})); }));

View file

@ -144,6 +144,24 @@ Object {
} }
`; `;
exports[`Images API Errors when trying to upload invalid form body 1: [body] 1`] = `
Object {
"errors": Array [
Object {
"code": null,
"context": null,
"details": null,
"ghostErrorCode": null,
"help": null,
"id": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
"message": "The request could not be understood.",
"property": null,
"type": "BadRequestError",
},
],
}
`;
exports[`Images API Will error when filename is too long 1: [body] 1`] = ` exports[`Images API Will error when filename is too long 1: [body] 1`] = `
Object { Object {
"errors": Array [ "errors": Array [

View file

@ -321,6 +321,21 @@ describe('Images API', function () {
sinon.assert.notCalled(unlinkStub); sinon.assert.notCalled(unlinkStub);
}); });
it('Errors when trying to upload invalid form body', async function () {
sinon.stub(logging, 'error');
await agent
.post('/images/upload/')
.header('Content-Type', 'multipart/form-data')
.body(new FormData())
.expectStatus(400)
.matchBodySnapshot({
errors: [{
id: anyErrorId
}]
});
});
it('Errors when image request body is broken', async function () { it('Errors when image request body is broken', async function () {
// Manually construct a broken request body // Manually construct a broken request body
const blob = await fetch('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==').then(res => res.blob()); const blob = await fetch('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==').then(res => res.blob());