Skip to content

Commit a5ef0be

Browse files
sunshineoGordon Sun
and
Gordon Sun
authored
catch JSON.parse and return 403 properly (#6614)
Co-authored-by: Gordon Sun <gordon.sun@pipe17.com>
1 parent b08b930 commit a5ef0be

File tree

2 files changed

+43
-23
lines changed

2 files changed

+43
-23
lines changed

spec/Middlewares.spec.js

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('middlewares', () => {
1212
_ApplicationId: 'FakeAppId',
1313
},
1414
headers: {},
15-
get: key => {
15+
get: (key) => {
1616
return fakeReq.headers[key.toLowerCase()];
1717
},
1818
};
@@ -24,7 +24,7 @@ describe('middlewares', () => {
2424
AppCache.del(fakeReq.body._ApplicationId);
2525
});
2626

27-
it('should use _ContentType if provided', done => {
27+
it('should use _ContentType if provided', (done) => {
2828
expect(fakeReq.headers['content-type']).toEqual(undefined);
2929
const contentType = 'image/jpeg';
3030
fakeReq.body._ContentType = contentType;
@@ -64,7 +64,7 @@ describe('middlewares', () => {
6464
expect(fakeRes.status).toHaveBeenCalledWith(403);
6565
});
6666

67-
it('should succeed when any one of the configured keys supplied', done => {
67+
it('should succeed when any one of the configured keys supplied', (done) => {
6868
AppCache.put(fakeReq.body._ApplicationId, {
6969
clientKey: 'clientKey',
7070
masterKey: 'masterKey',
@@ -77,7 +77,7 @@ describe('middlewares', () => {
7777
});
7878
});
7979

80-
it('should succeed when client key supplied but empty', done => {
80+
it('should succeed when client key supplied but empty', (done) => {
8181
AppCache.put(fakeReq.body._ApplicationId, {
8282
clientKey: '',
8383
masterKey: 'masterKey',
@@ -90,7 +90,7 @@ describe('middlewares', () => {
9090
});
9191
});
9292

93-
it('should succeed when no keys are configured and none supplied', done => {
93+
it('should succeed when no keys are configured and none supplied', (done) => {
9494
AppCache.put(fakeReq.body._ApplicationId, {
9595
masterKey: 'masterKey',
9696
});
@@ -110,22 +110,22 @@ describe('middlewares', () => {
110110

111111
const BodyKeys = Object.keys(BodyParams);
112112

113-
BodyKeys.forEach(infoKey => {
113+
BodyKeys.forEach((infoKey) => {
114114
const bodyKey = BodyParams[infoKey];
115115
const keyValue = 'Fake' + bodyKey;
116116
// javascriptKey is the only one that gets defaulted,
117117
const otherKeys = BodyKeys.filter(
118-
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
118+
(otherKey) => otherKey !== infoKey && otherKey !== 'javascriptKey'
119119
);
120120

121-
it(`it should pull ${bodyKey} into req.info`, done => {
121+
it(`it should pull ${bodyKey} into req.info`, (done) => {
122122
fakeReq.body[bodyKey] = keyValue;
123123

124124
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
125125
expect(fakeReq.body[bodyKey]).toEqual(undefined);
126126
expect(fakeReq.info[infoKey]).toEqual(keyValue);
127127

128-
otherKeys.forEach(otherKey => {
128+
otherKeys.forEach((otherKey) => {
129129
expect(fakeReq.info[otherKey]).toEqual(undefined);
130130
});
131131

@@ -145,7 +145,7 @@ describe('middlewares', () => {
145145
expect(fakeRes.status).toHaveBeenCalledWith(403);
146146
});
147147

148-
it('should succeed if the ip does belong to masterKeyIps list', done => {
148+
it('should succeed if the ip does belong to masterKeyIps list', (done) => {
149149
AppCache.put(fakeReq.body._ApplicationId, {
150150
masterKey: 'masterKey',
151151
masterKeyIps: ['ip1', 'ip2'],
@@ -169,7 +169,7 @@ describe('middlewares', () => {
169169
expect(fakeRes.status).toHaveBeenCalledWith(403);
170170
});
171171

172-
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', done => {
172+
it('should succeed if the connection.remoteAddress does belong to masterKeyIps list', (done) => {
173173
AppCache.put(fakeReq.body._ApplicationId, {
174174
masterKey: 'masterKey',
175175
masterKeyIps: ['ip1', 'ip2'],
@@ -193,7 +193,7 @@ describe('middlewares', () => {
193193
expect(fakeRes.status).toHaveBeenCalledWith(403);
194194
});
195195

196-
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', done => {
196+
it('should succeed if the socket.remoteAddress does belong to masterKeyIps list', (done) => {
197197
AppCache.put(fakeReq.body._ApplicationId, {
198198
masterKey: 'masterKey',
199199
masterKeyIps: ['ip1', 'ip2'],
@@ -217,7 +217,7 @@ describe('middlewares', () => {
217217
expect(fakeRes.status).toHaveBeenCalledWith(403);
218218
});
219219

220-
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', done => {
220+
it('should succeed if the connection.socket.remoteAddress does belong to masterKeyIps list', (done) => {
221221
AppCache.put(fakeReq.body._ApplicationId, {
222222
masterKey: 'masterKey',
223223
masterKeyIps: ['ip1', 'ip2'],
@@ -230,7 +230,7 @@ describe('middlewares', () => {
230230
});
231231
});
232232

233-
it('should allow any ip to use masterKey if masterKeyIps is empty', done => {
233+
it('should allow any ip to use masterKey if masterKeyIps is empty', (done) => {
234234
AppCache.put(fakeReq.body._ApplicationId, {
235235
masterKey: 'masterKey',
236236
masterKeyIps: [],
@@ -243,7 +243,7 @@ describe('middlewares', () => {
243243
});
244244
});
245245

246-
it('should succeed if xff header does belong to masterKeyIps', done => {
246+
it('should succeed if xff header does belong to masterKeyIps', (done) => {
247247
AppCache.put(fakeReq.body._ApplicationId, {
248248
masterKey: 'masterKey',
249249
masterKeyIps: ['ip1'],
@@ -256,7 +256,7 @@ describe('middlewares', () => {
256256
});
257257
});
258258

259-
it('should succeed if xff header with one ip does belong to masterKeyIps', done => {
259+
it('should succeed if xff header with one ip does belong to masterKeyIps', (done) => {
260260
AppCache.put(fakeReq.body._ApplicationId, {
261261
masterKey: 'masterKey',
262262
masterKeyIps: ['ip1'],
@@ -357,7 +357,7 @@ describe('middlewares', () => {
357357
);
358358
});
359359

360-
it('should use user provided on field userFromJWT', done => {
360+
it('should use user provided on field userFromJWT', (done) => {
361361
AppCache.put(fakeReq.body._ApplicationId, {
362362
masterKey: 'masterKey',
363363
});
@@ -367,4 +367,17 @@ describe('middlewares', () => {
367367
done();
368368
});
369369
});
370+
371+
it('should give invalid response when upload file without x-parse-application-id in header', () => {
372+
AppCache.put(fakeReq.body._ApplicationId, {
373+
masterKey: 'masterKey',
374+
});
375+
fakeReq.body = Buffer.from('fake-file');
376+
console.log('fakeReq.body.constructor');
377+
console.log(fakeReq.body.constructor);
378+
console.log('fakeReq.body instanceof Buffer');
379+
console.log(fakeReq.body instanceof Buffer);
380+
middlewares.handleParseHeaders(fakeReq, fakeRes);
381+
expect(fakeRes.status).toHaveBeenCalledWith(403);
382+
});
370383
});

src/middlewares.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import defaultLogger from './logger';
88
export const DEFAULT_ALLOWED_HEADERS =
99
'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, Content-Type, Pragma, Cache-Control';
1010

11-
const getMountForRequest = function(req) {
11+
const getMountForRequest = function (req) {
1212
const mountPathLength = req.originalUrl.length - req.url.length;
1313
const mountPath = req.originalUrl.slice(0, mountPathLength);
1414
return req.protocol + '://' + req.get('host') + mountPath;
@@ -59,7 +59,14 @@ export function handleParseHeaders(req, res, next) {
5959
if (req.body instanceof Buffer) {
6060
// The only chance to find the app id is if this is a file
6161
// upload that actually is a JSON body. So try to parse it.
62-
req.body = JSON.parse(req.body);
62+
// https://github.com/parse-community/parse-server/issues/6589
63+
// It is also possible that the client is trying to upload a file but forgot
64+
// to provide x-parse-app-id in header and parse a binary file will fail
65+
try {
66+
req.body = JSON.parse(req.body);
67+
} catch (e) {
68+
return invalidRequest(req, res);
69+
}
6370
fileViaJSON = true;
6471
}
6572

@@ -168,10 +175,10 @@ export function handleParseHeaders(req, res, next) {
168175
// Client keys are not required in parse-server, but if any have been configured in the server, validate them
169176
// to preserve original behavior.
170177
const keys = ['clientKey', 'javascriptKey', 'dotNetKey', 'restAPIKey'];
171-
const oneKeyConfigured = keys.some(function(key) {
178+
const oneKeyConfigured = keys.some(function (key) {
172179
return req.config[key] !== undefined;
173180
});
174-
const oneKeyMatches = keys.some(function(key) {
181+
const oneKeyMatches = keys.some(function (key) {
175182
return req.config[key] !== undefined && info[key] === req.config[key];
176183
});
177184

@@ -225,13 +232,13 @@ export function handleParseHeaders(req, res, next) {
225232
});
226233
}
227234
})
228-
.then(auth => {
235+
.then((auth) => {
229236
if (auth) {
230237
req.auth = auth;
231238
next();
232239
}
233240
})
234-
.catch(error => {
241+
.catch((error) => {
235242
if (error instanceof Parse.Error) {
236243
next(error);
237244
return;

0 commit comments

Comments
 (0)