feat: Upgrade to express 5.0.1 (#9530)
BREAKING CHANGE: This upgrades the internally used Express framework from version 4 to 5, which may be a breaking change. If Parse Server is set up to be mounted on an Express application, we recommend to also use version 5 of the Express framework to avoid any compatibility issues. Note that even if there are no issues after upgrading, future releases of Parse Server may introduce issues if Parse Server internally relies on Express 5-specific features which are unsupported by the Express version on which it is mounted. See the Express [migration guide](https://expressjs.com/en/guide/migrating-5.html) and [release announcement](https://expressjs.com/2024/10/15/v5-release.html#breaking-changes) for more info.
This commit is contained in:
1261
package-lock.json
generated
1261
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -28,11 +28,10 @@
|
||||
"@parse/fs-files-adapter": "3.0.0",
|
||||
"@parse/push-adapter": "6.10.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.20.3",
|
||||
"commander": "13.0.0",
|
||||
"cors": "2.8.5",
|
||||
"deepcopy": "2.1.0",
|
||||
"express": "4.21.2",
|
||||
"express": "5.0.1",
|
||||
"express-rate-limit": "7.5.0",
|
||||
"follow-redirects": "1.15.9",
|
||||
"graphql": "16.9.0",
|
||||
@@ -58,6 +57,7 @@
|
||||
"punycode": "2.3.1",
|
||||
"rate-limit-redis": "4.2.0",
|
||||
"redis": "4.7.0",
|
||||
"router": "2.0.0",
|
||||
"semver": "7.7.1",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"tv4": "1.3.0",
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
const httpRequest = require('../lib/request'),
|
||||
HTTPResponse = require('../lib/request').HTTPResponse,
|
||||
bodyParser = require('body-parser'),
|
||||
express = require('express');
|
||||
|
||||
const port = 13371;
|
||||
@@ -10,7 +9,7 @@ const httpRequestServer = `http://localhost:${port}`;
|
||||
|
||||
function startServer(done) {
|
||||
const app = express();
|
||||
app.use(bodyParser.json({ type: '*/*' }));
|
||||
app.use(express.json({ type: '*/*' }));
|
||||
app.get('/hello', function (req, res) {
|
||||
res.json({ response: 'OK' });
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ const request = require('../lib/request');
|
||||
const triggers = require('../lib/triggers');
|
||||
const HooksController = require('../lib/Controllers/HooksController').default;
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const auth = require('../lib/Auth');
|
||||
const Config = require('../lib/Config');
|
||||
|
||||
@@ -17,7 +16,7 @@ describe('Hooks', () => {
|
||||
beforeEach(done => {
|
||||
if (!app) {
|
||||
app = express();
|
||||
app.use(bodyParser.json({ type: '*/*' }));
|
||||
app.use(express.json({ type: '*/*' }));
|
||||
server = app.listen(port, undefined, done);
|
||||
} else {
|
||||
done();
|
||||
|
||||
@@ -250,11 +250,10 @@ describe('Vulnerabilities', () => {
|
||||
|
||||
it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => {
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const port = 34567;
|
||||
const hookServerURL = 'http://localhost:' + port;
|
||||
const app = express();
|
||||
app.use(bodyParser.json({ type: '*/*' }));
|
||||
app.use(express.json({ type: '*/*' }));
|
||||
const server = await new Promise(resolve => {
|
||||
const res = app.listen(port, undefined, () => resolve(res));
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ export class AnalyticsController extends AdaptableController {
|
||||
appOpened(req) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return this.adapter.appOpened(req.body, req);
|
||||
return this.adapter.appOpened(req.body || {}, req);
|
||||
})
|
||||
.then(response => {
|
||||
return { response: response || {} };
|
||||
@@ -18,7 +18,7 @@ export class AnalyticsController extends AdaptableController {
|
||||
trackEvent(req) {
|
||||
return Promise.resolve()
|
||||
.then(() => {
|
||||
return this.adapter.trackEvent(req.params.eventName, req.body, req);
|
||||
return this.adapter.trackEvent(req.params.eventName, req.body || {}, req);
|
||||
})
|
||||
.then(response => {
|
||||
return { response: response || {} };
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// ParseServer - open-source compatible API Server for Parse apps
|
||||
|
||||
var batch = require('./batch'),
|
||||
bodyParser = require('body-parser'),
|
||||
express = require('express'),
|
||||
middlewares = require('./middlewares'),
|
||||
Parse = require('parse/node').Parse,
|
||||
@@ -253,6 +252,7 @@ class ParseServer {
|
||||
var api = express();
|
||||
//api.use("/apps", express.static(__dirname + "/public"));
|
||||
api.use(middlewares.allowCrossDomain(appId));
|
||||
api.use(middlewares.allowDoubleForwardSlash);
|
||||
// File handling needs to be before default middlewares are applied
|
||||
api.use(
|
||||
'/',
|
||||
@@ -273,15 +273,16 @@ class ParseServer {
|
||||
|
||||
api.use(
|
||||
'/',
|
||||
bodyParser.urlencoded({ extended: false }),
|
||||
express.urlencoded({ extended: false }),
|
||||
pages.enableRouter
|
||||
? new PagesRouter(pages).expressRouter()
|
||||
: new PublicAPIRouter().expressRouter()
|
||||
);
|
||||
|
||||
api.use(bodyParser.json({ type: '*/*', limit: maxUploadSize }));
|
||||
api.use(express.json({ type: '*/*', limit: maxUploadSize }));
|
||||
api.use(middlewares.allowMethodOverride);
|
||||
api.use(middlewares.handleParseHeaders);
|
||||
api.set('query parser', 'extended');
|
||||
const routes = Array.isArray(rateLimit) ? rateLimit : [rateLimit];
|
||||
for (const route of routes) {
|
||||
middlewares.addRateLimit(route, options);
|
||||
|
||||
@@ -9,7 +9,7 @@ import Parse from 'parse/node';
|
||||
import express from 'express';
|
||||
import log from './logger';
|
||||
import { inspect } from 'util';
|
||||
const Layer = require('express/lib/router/layer');
|
||||
const Layer = require('router/lib/layer');
|
||||
|
||||
function validateParameter(key, value) {
|
||||
if (key == 'className') {
|
||||
|
||||
@@ -6,7 +6,7 @@ import UsersRouter from './UsersRouter';
|
||||
|
||||
export class AggregateRouter extends ClassesRouter {
|
||||
handleFind(req) {
|
||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||
const options = {};
|
||||
if (body.distinct) {
|
||||
options.distinct = String(body.distinct);
|
||||
|
||||
@@ -8,7 +8,7 @@ export class AudiencesRouter extends ClassesRouter {
|
||||
}
|
||||
|
||||
handleFind(req) {
|
||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||
|
||||
return rest
|
||||
|
||||
@@ -19,7 +19,7 @@ export class ClassesRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
handleFind(req) {
|
||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||
if (req.config.maxLimit && body.limit > req.config.maxLimit) {
|
||||
// Silently replace the limit on the query with the max configured
|
||||
@@ -48,7 +48,7 @@ export class ClassesRouter extends PromiseRouter {
|
||||
|
||||
// Returns a promise for a {response} object.
|
||||
handleGet(req) {
|
||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||
const options = {};
|
||||
|
||||
for (const key of Object.keys(body)) {
|
||||
@@ -117,7 +117,7 @@ export class ClassesRouter extends PromiseRouter {
|
||||
req.config,
|
||||
req.auth,
|
||||
this.className(req),
|
||||
req.body,
|
||||
req.body || {},
|
||||
req.info.clientSDK,
|
||||
req.info.context
|
||||
);
|
||||
@@ -130,7 +130,7 @@ export class ClassesRouter extends PromiseRouter {
|
||||
req.auth,
|
||||
this.className(req),
|
||||
where,
|
||||
req.body,
|
||||
req.body || {},
|
||||
req.info.clientSDK,
|
||||
req.info.context
|
||||
);
|
||||
|
||||
@@ -77,7 +77,7 @@ export class CloudCodeRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
static createJob(req) {
|
||||
const { job_schedule } = req.body;
|
||||
const { job_schedule } = req.body || {};
|
||||
validateJobSchedule(req.config, job_schedule);
|
||||
return rest.create(
|
||||
req.config,
|
||||
@@ -91,7 +91,7 @@ export class CloudCodeRouter extends PromiseRouter {
|
||||
|
||||
static editJob(req) {
|
||||
const { objectId } = req.params;
|
||||
const { job_schedule } = req.body;
|
||||
const { job_schedule } = req.body || {};
|
||||
validateJobSchedule(req.config, job_schedule);
|
||||
return rest
|
||||
.update(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import express from 'express';
|
||||
import BodyParser from 'body-parser';
|
||||
import * as Middlewares from '../middlewares';
|
||||
import Parse from 'parse/node';
|
||||
import Config from '../Config';
|
||||
@@ -45,7 +44,7 @@ export class FilesRouter {
|
||||
|
||||
router.post(
|
||||
'/files/:filename',
|
||||
BodyParser.raw({
|
||||
express.raw({
|
||||
type: () => {
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
static handleCloudJob(req) {
|
||||
const jobName = req.params.jobName || req.body.jobName;
|
||||
const jobName = req.params.jobName || req.body?.jobName;
|
||||
const applicationId = req.config.applicationId;
|
||||
const jobHandler = jobStatusHandler(req.config);
|
||||
const jobFunction = triggers.getJob(jobName, applicationId);
|
||||
|
||||
@@ -46,8 +46,8 @@ export class GlobalConfigRouter extends PromiseRouter {
|
||||
"read-only masterKey isn't allowed to update the config."
|
||||
);
|
||||
}
|
||||
const params = req.body.params;
|
||||
const masterKeyOnly = req.body.masterKeyOnly || {};
|
||||
const params = req.body.params || {};
|
||||
const masterKeyOnly = req.body?.masterKeyOnly || {};
|
||||
// Transform in dot notation to make sure it works
|
||||
const update = Object.keys(params).reduce((acc, key) => {
|
||||
acc[`params.${key}`] = params[key];
|
||||
|
||||
@@ -19,7 +19,7 @@ export class GraphQLRouter extends PromiseRouter {
|
||||
"read-only masterKey isn't allowed to update the GraphQL config."
|
||||
);
|
||||
}
|
||||
const data = await req.config.parseGraphQLController.updateGraphQLConfig(req.body.params);
|
||||
const data = await req.config.parseGraphQLController.updateGraphQLConfig(req.body?.params || {});
|
||||
return {
|
||||
response: data,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ export class HooksRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
handlePost(req) {
|
||||
return this.createHook(req.body, req.config);
|
||||
return this.createHook(req.body || {}, req.config);
|
||||
}
|
||||
|
||||
handleGetFunctions(req) {
|
||||
@@ -66,11 +66,11 @@ export class HooksRouter extends PromiseRouter {
|
||||
|
||||
handleUpdate(req) {
|
||||
var hook;
|
||||
if (req.params.functionName && req.body.url) {
|
||||
if (req.params.functionName && req.body?.url) {
|
||||
hook = {};
|
||||
hook.functionName = req.params.functionName;
|
||||
hook.url = req.body.url;
|
||||
} else if (req.params.className && req.params.triggerName && req.body.url) {
|
||||
} else if (req.params.className && req.params.triggerName && req.body?.url) {
|
||||
hook = {};
|
||||
hook.className = req.params.className;
|
||||
hook.triggerName = req.params.triggerName;
|
||||
@@ -82,7 +82,7 @@ export class HooksRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
handlePut(req) {
|
||||
var body = req.body;
|
||||
var body = req.body || {};
|
||||
if (body.__op == 'Delete') {
|
||||
return this.handleDelete(req);
|
||||
} else {
|
||||
|
||||
@@ -68,8 +68,8 @@ function getFileForProductIdentifier(productIdentifier, req) {
|
||||
|
||||
export class IAPValidationRouter extends PromiseRouter {
|
||||
handleRequest(req) {
|
||||
let receipt = req.body.receipt;
|
||||
const productIdentifier = req.body.productIdentifier;
|
||||
let receipt = req.body?.receipt;
|
||||
const productIdentifier = req.body?.productIdentifier;
|
||||
|
||||
if (!receipt || !productIdentifier) {
|
||||
// TODO: Error, malformed request
|
||||
@@ -84,7 +84,7 @@ export class IAPValidationRouter extends PromiseRouter {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.TESTING == '1' && req.body.bypassAppStoreValidation) {
|
||||
if (process.env.TESTING == '1' && req.body?.bypassAppStoreValidation) {
|
||||
return getFileForProductIdentifier(productIdentifier, req);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export class InstallationsRouter extends ClassesRouter {
|
||||
}
|
||||
|
||||
handleFind(req) {
|
||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||
const body = Object.assign(req.body || {}, ClassesRouter.JSONFromQuery(req.query));
|
||||
const options = ClassesRouter.optionsFromBody(body, req.config.defaultLimit);
|
||||
return rest
|
||||
.find(
|
||||
|
||||
@@ -107,8 +107,8 @@ export class PagesRouter extends PromiseRouter {
|
||||
|
||||
resendVerificationEmail(req) {
|
||||
const config = req.config;
|
||||
const username = req.body.username;
|
||||
const token = req.body.token;
|
||||
const username = req.body?.username;
|
||||
const token = req.body?.token;
|
||||
|
||||
if (!config) {
|
||||
this.invalidRequest();
|
||||
@@ -178,7 +178,7 @@ export class PagesRouter extends PromiseRouter {
|
||||
this.invalidRequest();
|
||||
}
|
||||
|
||||
const { new_password, token: rawToken } = req.body;
|
||||
const { new_password, token: rawToken } = req.body || {};
|
||||
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
||||
|
||||
if ((!token || !new_password) && req.xhr === false) {
|
||||
@@ -320,7 +320,7 @@ export class PagesRouter extends PromiseRouter {
|
||||
*/
|
||||
staticRoute(req) {
|
||||
// Get requested path
|
||||
const relativePath = req.params[0];
|
||||
const relativePath = req.params['resource'][0];
|
||||
|
||||
// Resolve requested path to absolute path
|
||||
const absolutePath = path.resolve(this.pagesPath, relativePath);
|
||||
@@ -716,7 +716,7 @@ export class PagesRouter extends PromiseRouter {
|
||||
mountStaticRoute() {
|
||||
this.route(
|
||||
'GET',
|
||||
`/${this.pagesEndpoint}/(*)?`,
|
||||
`/${this.pagesEndpoint}/*resource`,
|
||||
req => {
|
||||
this.setConfig(req, true);
|
||||
},
|
||||
|
||||
@@ -52,7 +52,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
}
|
||||
|
||||
resendVerificationEmail(req) {
|
||||
const username = req.body.username;
|
||||
const username = req.body?.username;
|
||||
const appId = req.params.appId;
|
||||
const config = Config.get(appId);
|
||||
|
||||
@@ -162,7 +162,7 @@ export class PublicAPIRouter extends PromiseRouter {
|
||||
return this.missingPublicServerURL();
|
||||
}
|
||||
|
||||
const { new_password, token: rawToken } = req.body;
|
||||
const { new_password, token: rawToken } = req.body || {};
|
||||
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
|
||||
|
||||
if ((!token || !new_password) && req.xhr === false) {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class PushRouter extends PromiseRouter {
|
||||
});
|
||||
let pushStatusId;
|
||||
pushController
|
||||
.sendPush(req.body, where, req.config, req.auth, objectId => {
|
||||
.sendPush(req.body || {}, where, req.config, req.auth, objectId => {
|
||||
pushStatusId = objectId;
|
||||
resolve({
|
||||
headers: {
|
||||
|
||||
@@ -77,18 +77,18 @@ async function createSchema(req) {
|
||||
"read-only masterKey isn't allowed to create a schema."
|
||||
);
|
||||
}
|
||||
if (req.params.className && req.body.className) {
|
||||
if (req.params.className && req.body?.className) {
|
||||
if (req.params.className != req.body.className) {
|
||||
return classNameMismatchResponse(req.body.className, req.params.className);
|
||||
}
|
||||
}
|
||||
|
||||
const className = req.params.className || req.body.className;
|
||||
const className = req.params.className || req.body?.className;
|
||||
if (!className) {
|
||||
throw new Parse.Error(135, `POST ${req.path} needs a class name.`);
|
||||
}
|
||||
|
||||
return await internalCreateSchema(className, req.body, req.config);
|
||||
return await internalCreateSchema(className, req.body || {}, req.config);
|
||||
}
|
||||
|
||||
function modifySchema(req) {
|
||||
@@ -99,12 +99,12 @@ function modifySchema(req) {
|
||||
"read-only masterKey isn't allowed to update a schema."
|
||||
);
|
||||
}
|
||||
if (req.body.className && req.body.className != req.params.className) {
|
||||
if (req.body?.className && req.body.className != req.params.className) {
|
||||
return classNameMismatchResponse(req.body.className, req.params.className);
|
||||
}
|
||||
const className = req.params.className;
|
||||
|
||||
return internalUpdateSchema(className, req.body, req.config);
|
||||
return internalUpdateSchema(className, req.body || {}, req.config);
|
||||
}
|
||||
|
||||
const deleteSchema = req => {
|
||||
|
||||
@@ -68,7 +68,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
_authenticateUserFromRequest(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Use query parameters instead if provided in url
|
||||
let payload = req.body;
|
||||
let payload = req.body || {};
|
||||
if (
|
||||
(!payload.username && req.query && req.query.username) ||
|
||||
(!payload.email && req.query && req.query.email)
|
||||
@@ -219,7 +219,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
req.auth,
|
||||
'_User',
|
||||
{ objectId: user.objectId },
|
||||
req.body,
|
||||
req.body || {},
|
||||
user,
|
||||
req.info.clientSDK,
|
||||
req.info.context
|
||||
@@ -336,7 +336,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'master key is required');
|
||||
}
|
||||
|
||||
const userId = req.body.userId || req.query.userId;
|
||||
const userId = req.body?.userId || req.query.userId;
|
||||
if (!userId) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_VALUE,
|
||||
@@ -438,8 +438,9 @@ export class UsersRouter extends ClassesRouter {
|
||||
async handleResetRequest(req) {
|
||||
this._throwOnBadEmailConfig(req);
|
||||
|
||||
let email = req.body.email;
|
||||
const token = req.body.token;
|
||||
let email = req.body?.email;
|
||||
const token = req.body?.token;
|
||||
|
||||
if (!email && !token) {
|
||||
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
||||
}
|
||||
@@ -480,7 +481,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
async handleVerificationEmailRequest(req) {
|
||||
this._throwOnBadEmailConfig(req);
|
||||
|
||||
const { email } = req.body;
|
||||
const { email } = req.body || {};
|
||||
if (!email) {
|
||||
throw new Parse.Error(Parse.Error.EMAIL_MISSING, 'you must provide an email');
|
||||
}
|
||||
@@ -513,7 +514,7 @@ export class UsersRouter extends ClassesRouter {
|
||||
}
|
||||
|
||||
async handleChallenge(req) {
|
||||
const { username, email, password, authData, challengeData } = req.body;
|
||||
const { username, email, password, authData, challengeData } = req.body || {};
|
||||
|
||||
// if username or email provided with password try to authenticate the user by username
|
||||
let user;
|
||||
|
||||
@@ -64,7 +64,7 @@ function makeBatchRoutingPathFunction(originalUrl, serverURL, publicServerURL) {
|
||||
// Returns a promise for a {response} object.
|
||||
// TODO: pass along auth correctly
|
||||
function handleBatch(router, req) {
|
||||
if (!Array.isArray(req.body.requests)) {
|
||||
if (!Array.isArray(req.body?.requests)) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'requests must be an array');
|
||||
}
|
||||
|
||||
@@ -85,12 +85,12 @@ function handleBatch(router, req) {
|
||||
|
||||
const batch = transactionRetries => {
|
||||
let initialPromise = Promise.resolve();
|
||||
if (req.body.transaction === true) {
|
||||
if (req.body?.transaction === true) {
|
||||
initialPromise = req.config.database.createTransactionalSession();
|
||||
}
|
||||
|
||||
return initialPromise.then(() => {
|
||||
const promises = req.body.requests.map(restRequest => {
|
||||
const promises = req.body?.requests.map(restRequest => {
|
||||
const routablePath = makeRoutablePath(restRequest.path);
|
||||
|
||||
// Construct a request that we can send to a handler
|
||||
@@ -113,7 +113,7 @@ function handleBatch(router, req) {
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(results => {
|
||||
if (req.body.transaction === true) {
|
||||
if (req.body?.transaction === true) {
|
||||
if (results.find(result => typeof result.error === 'object')) {
|
||||
return req.config.database.abortTransactionalSession().then(() => {
|
||||
return Promise.reject({ response: results });
|
||||
|
||||
@@ -196,7 +196,7 @@ export async function handleParseHeaders(req, res, next) {
|
||||
info.clientSDK = ClientSDK.fromString(info.clientVersion);
|
||||
}
|
||||
|
||||
if (fileViaJSON) {
|
||||
if (fileViaJSON && req.body) {
|
||||
req.fileData = req.body.fileData;
|
||||
// We need to repopulate req.body with a buffer
|
||||
var base64 = req.body.base64;
|
||||
@@ -450,7 +450,7 @@ export function allowCrossDomain(appId) {
|
||||
}
|
||||
|
||||
export function allowMethodOverride(req, res, next) {
|
||||
if (req.method === 'POST' && req.body._method) {
|
||||
if (req.method === 'POST' && req.body?._method) {
|
||||
req.originalMethod = req.method;
|
||||
req.method = req.body._method;
|
||||
delete req.body._method;
|
||||
@@ -685,3 +685,16 @@ function malformedContext(req, res) {
|
||||
res.status(400);
|
||||
res.json({ code: Parse.Error.INVALID_JSON, error: 'Invalid object for context.' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Express 4 allowed a double forward slash between a route and router. Although
|
||||
* this should be considered an anti-pattern, we need to support it for backwards
|
||||
* compatibility.
|
||||
*
|
||||
* Technically valid URL with double foroward slash:
|
||||
* http://localhost:1337/parse//functions/testFunction
|
||||
*/
|
||||
export function allowDoubleForwardSlash(req, res, next) {
|
||||
req.url = req.url.startsWith('//') ? req.url.substring(1) : req.url;
|
||||
next();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user