diff --git a/src/middleware/json.js b/src/middleware/json.js index a3c45d3f6..71512cc83 100644 --- a/src/middleware/json.js +++ b/src/middleware/json.js @@ -11,8 +11,11 @@ exports = module.exports = function (options, forceContentType = true) { const json = express.json(options); return function (req, res, next) { - // enforce json body type. without this the json middleware will skip parsing and req.body might be undefined - if (forceContentType && _mime(req) !== 'application/json') return res.status(400).send('incorrect mime type. expecting application/json'); + // optionally enforce json body type. without this the json middleware will skip parsing and req.body might be undefined + if (_mime(req) !== 'application/json') { + if (forceContentType) return res.status(400).send('Invalid content-type. Expecting application/json'); + else return next(); + } json(req, res, next); }; }; diff --git a/src/middleware/multipart.js b/src/middleware/multipart.js index 490d3e9d8..a9264a484 100644 --- a/src/middleware/multipart.js +++ b/src/middleware/multipart.js @@ -11,9 +11,12 @@ function _mime(req) { return str.split(';')[0]; } -module.exports = function multipart(options) { +module.exports = function multipart(options, forceContentType = true) { return function (req, res, next) { - if (_mime(req) !== 'multipart/form-data') return res.status(400).send('Invalid content-type. Expecting multipart'); + if (_mime(req) !== 'multipart/form-data') { + if (forceContentType) return res.status(400).send('Invalid content-type. Expecting multipart'); + else return next(); + } const form = new multiparty.Form({ uploadDir: '/tmp', diff --git a/src/routes/apps.js b/src/routes/apps.js index 5370bcec1..46a8d4f11 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -205,7 +205,7 @@ async function install(req, res, next) { data.appStoreId = result.appStoreId; data.manifest = result.manifest; - data.sourceArchiveFilePath = req.files.sourceArchive?.path || null; + data.sourceArchiveFilePath = req.files && req.files.sourceArchive?.path || null; // if we have a source archive upload, craft a custom docker image URI for later if (data.sourceArchiveFilePath) { @@ -713,7 +713,7 @@ async function update(req, res, next) { data.appStoreId = appStoreId; data.manifest = manifest; - data.sourceArchiveFilePath = req.files.sourceArchive?.path || null; + data.sourceArchiveFilePath = req.files && req.files.sourceArchive?.path || null; // if we have a source archive upload, craft a custom docker image URI for later if (data.sourceArchiveFilePath) { diff --git a/src/server.js b/src/server.js index 55db336d5..af268658b 100644 --- a/src/server.js +++ b/src/server.js @@ -45,9 +45,12 @@ async function initializeExpressSync() { const QUERY_LIMIT = '2mb', // max size for json queries (see also client_max_body_size in nginx) FIELD_LIMIT = 2 * 1024 * 1024; // max fields that can appear in multipart - const multipart = middleware.multipart({ maxFieldsSize: FIELD_LIMIT, limit: FILE_SIZE_LIMIT, timeout: FILE_TIMEOUT }); + const multipart = middleware.multipart({ maxFieldsSize: FIELD_LIMIT, limit: FILE_SIZE_LIMIT, timeout: FILE_TIMEOUT }, true); // forces multipart content-type const json = middleware.json({ strict: true, limit: QUERY_LIMIT }, true); // forces json content-type - const jsonOrMultipart = [ middleware.json({ strict: true, limit: QUERY_LIMIT }, false), multipart ]; + const jsonOrMultipart = [ + middleware.json({ strict: true, limit: QUERY_LIMIT }, false), + middleware.multipart({ maxFieldsSize: FIELD_LIMIT, limit: FILE_SIZE_LIMIT, timeout: FILE_TIMEOUT }, false) + ]; app.set('json spaces', 2); // pretty json app.enable('trust proxy'); // trust the X-Forwarded-* headers