Files
kami-parse-server/spec/vulnerabilities.spec.js

284 lines
9.5 KiB
JavaScript

const request = require('../lib/request');
describe('Vulnerabilities', () => {
describe('Object prototype pollution', () => {
it('denies object prototype to be polluted with keyword "constructor"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const response = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({
obj: {
constructor: {
prototype: {
dummy: 0,
},
},
},
}),
}).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
it('denies object prototype to be polluted with keypath string "constructor"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const objResponse = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({
obj: {},
}),
}).catch(e => e);
const pollResponse = await request({
headers: headers,
method: 'PUT',
url: `http://localhost:8378/1/classes/PP/${objResponse.data.objectId}`,
body: JSON.stringify({
'obj.constructor.prototype.dummy': {
__op: 'Increment',
amount: 1,
},
}),
}).catch(e => e);
expect(Object.prototype.dummy).toBeUndefined();
expect(pollResponse.status).toBe(400);
const text = JSON.parse(pollResponse.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"constructor"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
it('denies object prototype to be polluted with keyword "__proto__"', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const response = await request({
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/PP',
body: JSON.stringify({ 'obj.__proto__.dummy': 0 }),
}).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"__proto__"}.');
expect(Object.prototype.dummy).toBeUndefined();
});
});
describe('Request denylist', () => {
it('denies BSON type code data in write request by default', async () => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"_bsontype","value":"Code"}.'
);
});
it('allows BSON type code data in write request with custom denylist', async () => {
await reconfigureServer({
requestKeywordDenylist: [],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
_bsontype: 'Code',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(201);
const text = JSON.parse(response.text);
expect(text.objectId).toBeDefined();
});
it('denies write request with custom denylist of key/value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of nested key/value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
nested: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of key/value in array', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey', value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: [
{
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
],
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe(
'Prohibited keyword in request data: {"key":"a[K]ey","value":"aValue[123]*"}.'
);
});
it('denies write request with custom denylist of key', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ key: 'a[K]ey' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"key":"a[K]ey"}.');
});
it('denies write request with custom denylist of value', async () => {
await reconfigureServer({
requestKeywordDenylist: [{ value: 'aValue[123]*' }],
});
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const params = {
headers: headers,
method: 'POST',
url: 'http://localhost:8378/1/classes/RCE',
body: JSON.stringify({
obj: {
aKey: 'aValue321',
code: 'delete Object.prototype.evalFunctions',
},
}),
};
const response = await request(params).catch(e => e);
expect(response.status).toBe(400);
const text = JSON.parse(response.text);
expect(text.code).toBe(Parse.Error.INVALID_KEY_NAME);
expect(text.error).toBe('Prohibited keyword in request data: {"value":"aValue[123]*"}.');
});
});
});