fix: Security upgrade to parse 7.0.1 (#9877)
This commit is contained in:
83
package-lock.json
generated
83
package-lock.json
generated
@@ -39,7 +39,7 @@
|
|||||||
"mongodb": "6.17.0",
|
"mongodb": "6.17.0",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"otpauth": "9.4.0",
|
"otpauth": "9.4.0",
|
||||||
"parse": "6.1.1",
|
"parse": "7.0.1",
|
||||||
"path-to-regexp": "6.3.0",
|
"path-to-regexp": "6.3.0",
|
||||||
"pg-monitor": "3.0.0",
|
"pg-monitor": "3.0.0",
|
||||||
"pg-promise": "12.2.0",
|
"pg-promise": "12.2.0",
|
||||||
@@ -2502,13 +2502,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime-corejs3": {
|
"node_modules/@babel/runtime-corejs3": {
|
||||||
"version": "7.27.0",
|
"version": "7.28.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.4.tgz",
|
||||||
"integrity": "sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew==",
|
"integrity": "sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js-pure": "^3.30.2",
|
"core-js-pure": "^3.43.0"
|
||||||
"regenerator-runtime": "^0.14.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@@ -9036,10 +9035,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js-pure": {
|
"node_modules/core-js-pure": {
|
||||||
"version": "3.41.0",
|
"version": "3.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.46.0.tgz",
|
||||||
"integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==",
|
"integrity": "sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/core-js"
|
"url": "https://opencollective.com/core-js"
|
||||||
@@ -18589,17 +18589,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse": {
|
"node_modules/parse": {
|
||||||
"version": "6.1.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse/-/parse-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse/-/parse-7.0.1.tgz",
|
||||||
"integrity": "sha512-zf70XcHKesDcqpO2RVKyIc1l7pngxBsYQVl0Yl/A38pftOSP8BQeampqqLEqMknzUetNZy8B+wrR3k5uTQDXOw==",
|
"integrity": "sha512-6hCnE8EWky/MqDtlpMnztzL0BEEsU3jVI7iKl2+AlJeSAeWkCgkPcb30eBNq57FcCnqWWC6uVJAaUMmX3+zrvg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime-corejs3": "7.27.0",
|
"@babel/runtime-corejs3": "7.28.4",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.2",
|
||||||
"react-native-crypto-js": "1.0.0",
|
"react-native-crypto-js": "1.0.0",
|
||||||
"uuid": "10.0.0",
|
"uuid": "10.0.0",
|
||||||
"ws": "8.18.1",
|
"ws": "8.18.3"
|
||||||
"xmlhttprequest": "1.8.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "18 || 19 || 20 || 22"
|
"node": "18 || 19 || 20 || 22"
|
||||||
@@ -18635,6 +18634,12 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse/node_modules/idb-keyval": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/parse/node_modules/uuid": {
|
"node_modules/parse/node_modules/uuid": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
@@ -18643,14 +18648,15 @@
|
|||||||
"https://github.com/sponsors/broofa",
|
"https://github.com/sponsors/broofa",
|
||||||
"https://github.com/sponsors/ctavan"
|
"https://github.com/sponsors/ctavan"
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"uuid": "dist/bin/uuid"
|
"uuid": "dist/bin/uuid"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parse/node_modules/ws": {
|
"node_modules/parse/node_modules/ws": {
|
||||||
"version": "8.18.1",
|
"version": "8.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
@@ -24615,12 +24621,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/runtime-corejs3": {
|
"@babel/runtime-corejs3": {
|
||||||
"version": "7.27.0",
|
"version": "7.28.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.28.4.tgz",
|
||||||
"integrity": "sha512-UWjX6t+v+0ckwZ50Y5ShZLnlk95pP5MyW/pon9tiYzl3+18pkTHTFNTKr7rQbfRXPkowt2QAn30o1b6oswszew==",
|
"integrity": "sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-js-pure": "^3.30.2",
|
"core-js-pure": "^3.43.0"
|
||||||
"regenerator-runtime": "^0.14.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/template": {
|
"@babel/template": {
|
||||||
@@ -29217,9 +29222,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-js-pure": {
|
"core-js-pure": {
|
||||||
"version": "3.41.0",
|
"version": "3.46.0",
|
||||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.46.0.tgz",
|
||||||
"integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q=="
|
"integrity": "sha512-NMCW30bHNofuhwLhYPt66OLOKTMbOhgTTatKVbaQC3KRHpTCiRIBYvtshr+NBYSnBxwAFhjW/RfJ0XbIjS16rw=="
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@@ -35848,28 +35853,32 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parse": {
|
"parse": {
|
||||||
"version": "6.1.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse/-/parse-6.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse/-/parse-7.0.1.tgz",
|
||||||
"integrity": "sha512-zf70XcHKesDcqpO2RVKyIc1l7pngxBsYQVl0Yl/A38pftOSP8BQeampqqLEqMknzUetNZy8B+wrR3k5uTQDXOw==",
|
"integrity": "sha512-6hCnE8EWky/MqDtlpMnztzL0BEEsU3jVI7iKl2+AlJeSAeWkCgkPcb30eBNq57FcCnqWWC6uVJAaUMmX3+zrvg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime-corejs3": "7.27.0",
|
"@babel/runtime-corejs3": "7.28.4",
|
||||||
"crypto-js": "4.2.0",
|
"crypto-js": "4.2.0",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.2",
|
||||||
"react-native-crypto-js": "1.0.0",
|
"react-native-crypto-js": "1.0.0",
|
||||||
"uuid": "10.0.0",
|
"uuid": "10.0.0",
|
||||||
"ws": "8.18.1",
|
"ws": "8.18.3"
|
||||||
"xmlhttprequest": "1.8.0"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"idb-keyval": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg=="
|
||||||
|
},
|
||||||
"uuid": {
|
"uuid": {
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||||
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="
|
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.18.1",
|
"version": "8.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"mongodb": "6.17.0",
|
"mongodb": "6.17.0",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"otpauth": "9.4.0",
|
"otpauth": "9.4.0",
|
||||||
"parse": "6.1.1",
|
"parse": "7.0.1",
|
||||||
"path-to-regexp": "6.3.0",
|
"path-to-regexp": "6.3.0",
|
||||||
"pg-monitor": "3.0.0",
|
"pg-monitor": "3.0.0",
|
||||||
"pg-promise": "12.2.0",
|
"pg-promise": "12.2.0",
|
||||||
|
|||||||
@@ -89,12 +89,16 @@ describe('LinkedInAdapter', function () {
|
|||||||
|
|
||||||
describe('Test getUserFromAccessToken', function () {
|
describe('Test getUserFromAccessToken', function () {
|
||||||
it('should fetch user successfully', async function () {
|
it('should fetch user successfully', async function () {
|
||||||
global.fetch = jasmine.createSpy().and.returnValue(
|
mockFetch([
|
||||||
Promise.resolve({
|
{
|
||||||
ok: true,
|
url: 'https://api.linkedin.com/v2/me',
|
||||||
json: () => Promise.resolve({ id: 'validUserId' }),
|
method: 'GET',
|
||||||
})
|
response: {
|
||||||
);
|
ok: true,
|
||||||
|
json: () => Promise.resolve({ id: 'validUserId' }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const user = await adapter.getUserFromAccessToken('validToken', false);
|
const user = await adapter.getUserFromAccessToken('validToken', false);
|
||||||
|
|
||||||
@@ -104,14 +108,21 @@ describe('LinkedInAdapter', function () {
|
|||||||
'x-li-format': 'json',
|
'x-li-format': 'json',
|
||||||
'x-li-src': undefined,
|
'x-li-src': undefined,
|
||||||
},
|
},
|
||||||
|
method: 'GET',
|
||||||
});
|
});
|
||||||
expect(user).toEqual({ id: 'validUserId' });
|
expect(user).toEqual({ id: 'validUserId' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error for invalid response', async function () {
|
it('should throw error for invalid response', async function () {
|
||||||
global.fetch = jasmine.createSpy().and.returnValue(
|
mockFetch([
|
||||||
Promise.resolve({ ok: false })
|
{
|
||||||
);
|
url: 'https://api.linkedin.com/v2/me',
|
||||||
|
method: 'GET',
|
||||||
|
response: {
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
await expectAsync(adapter.getUserFromAccessToken('invalidToken', false)).toBeRejectedWith(
|
await expectAsync(adapter.getUserFromAccessToken('invalidToken', false)).toBeRejectedWith(
|
||||||
new Error('LinkedIn API request failed.')
|
new Error('LinkedIn API request failed.')
|
||||||
@@ -121,12 +132,16 @@ describe('LinkedInAdapter', function () {
|
|||||||
|
|
||||||
describe('Test getAccessTokenFromCode', function () {
|
describe('Test getAccessTokenFromCode', function () {
|
||||||
it('should fetch token successfully', async function () {
|
it('should fetch token successfully', async function () {
|
||||||
global.fetch = jasmine.createSpy().and.returnValue(
|
mockFetch([
|
||||||
Promise.resolve({
|
{
|
||||||
ok: true,
|
url: 'https://www.linkedin.com/oauth/v2/accessToken',
|
||||||
json: () => Promise.resolve({ access_token: 'validToken' }),
|
method: 'POST',
|
||||||
})
|
response: {
|
||||||
);
|
ok: true,
|
||||||
|
json: () => Promise.resolve({ access_token: 'validToken' }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const tokenResponse = await adapter.getAccessTokenFromCode('validCode', 'http://example.com');
|
const tokenResponse = await adapter.getAccessTokenFromCode('validCode', 'http://example.com');
|
||||||
|
|
||||||
@@ -139,9 +154,15 @@ describe('LinkedInAdapter', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error for invalid response', async function () {
|
it('should throw error for invalid response', async function () {
|
||||||
global.fetch = jasmine.createSpy().and.returnValue(
|
mockFetch([
|
||||||
Promise.resolve({ ok: false })
|
{
|
||||||
);
|
url: 'https://www.linkedin.com/oauth/v2/accessToken',
|
||||||
|
method: 'POST',
|
||||||
|
response: {
|
||||||
|
ok: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
await expectAsync(
|
await expectAsync(
|
||||||
adapter.getAccessTokenFromCode('invalidCode', 'http://example.com')
|
adapter.getAccessTokenFromCode('invalidCode', 'http://example.com')
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ describe('WeChatAdapter', function () {
|
|||||||
const user = await adapter.getUserFromAccessToken('validToken', { id: 'validOpenId' });
|
const user = await adapter.getUserFromAccessToken('validToken', { id: 'validOpenId' });
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
'https://api.weixin.qq.com/sns/auth?access_token=validToken&openid=validOpenId'
|
'https://api.weixin.qq.com/sns/auth?access_token=validToken&openid=validOpenId',
|
||||||
|
jasmine.any(Object)
|
||||||
);
|
);
|
||||||
expect(user).toEqual({ errcode: 0, id: 'validUserId' });
|
expect(user).toEqual({ errcode: 0, id: 'validUserId' });
|
||||||
});
|
});
|
||||||
@@ -64,7 +65,8 @@ describe('WeChatAdapter', function () {
|
|||||||
const token = await adapter.getAccessTokenFromCode(authData);
|
const token = await adapter.getAccessTokenFromCode(authData);
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code'
|
'https://api.weixin.qq.com/sns/oauth2/access_token?appid=validAppId&secret=validAppSecret&code=validCode&grant_type=authorization_code',
|
||||||
|
jasmine.any(Object)
|
||||||
);
|
);
|
||||||
expect(token).toEqual('validToken');
|
expect(token).toEqual('validToken');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ describe('Cloud Code Logger', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async done => {
|
it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async () => {
|
||||||
Parse.Cloud.define('aFunction', () => {
|
Parse.Cloud.define('aFunction', () => {
|
||||||
return 'it worked!';
|
return 'it worked!';
|
||||||
});
|
});
|
||||||
@@ -203,6 +203,7 @@ describe('Cloud Code Logger', () => {
|
|||||||
expect(log).toEqual('info');
|
expect(log).toEqual('info');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Parse.Cloud._removeAllHooks();
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
silent: true,
|
silent: true,
|
||||||
logLevels: {
|
logLevels: {
|
||||||
@@ -211,6 +212,10 @@ describe('Cloud Code Logger', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Parse.Cloud.define('bFunction', () => {
|
||||||
|
throw new Error('Failed');
|
||||||
|
});
|
||||||
|
|
||||||
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
|
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -221,15 +226,12 @@ describe('Cloud Code Logger', () => {
|
|||||||
.allArgs()
|
.allArgs()
|
||||||
.find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0];
|
.find(log => log[1].startsWith('Failed running cloud function bFunction for '))?.[0];
|
||||||
expect(log).toEqual('info');
|
expect(log).toEqual('info');
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should log cloud function triggers using the custom log level', async () => {
|
it('should log cloud function triggers using the custom log level', async () => {
|
||||||
Parse.Cloud.beforeSave('TestClass', () => {});
|
|
||||||
Parse.Cloud.afterSave('TestClass', () => {});
|
|
||||||
|
|
||||||
const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => {
|
const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => {
|
||||||
|
Parse.Cloud._removeAllHooks();
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
silent: true,
|
silent: true,
|
||||||
logLevel,
|
logLevel,
|
||||||
@@ -239,6 +241,9 @@ describe('Cloud Code Logger', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Parse.Cloud.beforeSave('TestClass', () => { });
|
||||||
|
Parse.Cloud.afterSave('TestClass', () => { });
|
||||||
|
|
||||||
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
|
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
|
||||||
const obj = new Parse.Object('TestClass');
|
const obj = new Parse.Object('TestClass');
|
||||||
await obj.save();
|
await obj.save();
|
||||||
@@ -344,6 +349,7 @@ describe('Cloud Code Logger', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should log cloud function execution using the silent log level', async () => {
|
it('should log cloud function execution using the silent log level', async () => {
|
||||||
|
Parse.Cloud._removeAllHooks();
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
logLevels: {
|
logLevels: {
|
||||||
cloudFunctionSuccess: 'silent',
|
cloudFunctionSuccess: 'silent',
|
||||||
@@ -367,6 +373,7 @@ describe('Cloud Code Logger', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should log cloud function triggers using the silent log level', async () => {
|
it('should log cloud function triggers using the silent log level', async () => {
|
||||||
|
Parse.Cloud._removeAllHooks();
|
||||||
await reconfigureServer({
|
await reconfigureServer({
|
||||||
logLevels: {
|
logLevels: {
|
||||||
triggerAfter: 'silent',
|
triggerAfter: 'silent',
|
||||||
|
|||||||
@@ -1395,10 +1395,10 @@ describe('Parse.Object testing', () => {
|
|||||||
.save()
|
.save()
|
||||||
.then(function () {
|
.then(function () {
|
||||||
const query = new Parse.Query(TestObject);
|
const query = new Parse.Query(TestObject);
|
||||||
return query.find(object.id);
|
return query.get(object.id);
|
||||||
})
|
})
|
||||||
.then(function (results) {
|
.then(function (result) {
|
||||||
updatedObject = results[0];
|
updatedObject = result;
|
||||||
updatedObject.set('x', 11);
|
updatedObject.set('x', 11);
|
||||||
return updatedObject.save();
|
return updatedObject.save();
|
||||||
})
|
})
|
||||||
@@ -1409,7 +1409,8 @@ describe('Parse.Object testing', () => {
|
|||||||
equal(object.createdAt.getTime(), updatedObject.createdAt.getTime());
|
equal(object.createdAt.getTime(), updatedObject.createdAt.getTime());
|
||||||
equal(object.updatedAt.getTime(), updatedObject.updatedAt.getTime());
|
equal(object.updatedAt.getTime(), updatedObject.updatedAt.getTime());
|
||||||
done();
|
done();
|
||||||
});
|
})
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('fetchAll backbone-style callbacks', function (done) {
|
xit('fetchAll backbone-style callbacks', function (done) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('send comment with query through REST', async () => {
|
it('send comment with query through REST', async () => {
|
||||||
const comment = 'Hello Parse';
|
const comment = `Hello Parse ${Date.now()}`;
|
||||||
const object = new TestObject();
|
const object = new TestObject();
|
||||||
object.set('name', 'object');
|
object.set('name', 'object');
|
||||||
await object.save();
|
await object.save();
|
||||||
@@ -58,23 +58,55 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await request(options);
|
await request(options);
|
||||||
const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } });
|
|
||||||
|
// Wait for profile entry to appear with retry logic
|
||||||
|
let result;
|
||||||
|
const maxRetries = 10;
|
||||||
|
const retryDelay = 100;
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
result = await database.collection('system.profile').findOne(
|
||||||
|
{ 'command.explain.comment': comment },
|
||||||
|
{ sort: { ts: -1 } }
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
expect(result.command.explain.comment).toBe(comment);
|
expect(result.command.explain.comment).toBe(comment);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('send comment with query', async () => {
|
it('send comment with query', async () => {
|
||||||
const comment = 'Hello Parse';
|
const comment = `Hello Parse ${Date.now()}`;
|
||||||
const object = new TestObject();
|
const object = new TestObject();
|
||||||
object.set('name', 'object');
|
object.set('name', 'object');
|
||||||
await object.save();
|
await object.save();
|
||||||
const collection = await config.database.adapter._adaptiveCollection('TestObject');
|
const collection = await config.database.adapter._adaptiveCollection('TestObject');
|
||||||
await collection._rawFind({ name: 'object' }, { comment: comment });
|
await collection._rawFind({ name: 'object' }, { comment: comment });
|
||||||
const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } });
|
|
||||||
|
// Wait for profile entry to appear with retry logic
|
||||||
|
let result;
|
||||||
|
const maxRetries = 10;
|
||||||
|
const retryDelay = 100;
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
result = await database.collection('system.profile').findOne(
|
||||||
|
{ 'command.comment': comment },
|
||||||
|
{ sort: { ts: -1 } }
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
expect(result.command.comment).toBe(comment);
|
expect(result.command.comment).toBe(comment);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('send a comment with a count query', async () => {
|
it('send a comment with a count query', async () => {
|
||||||
const comment = 'Hello Parse';
|
const comment = `Hello Parse ${Date.now()}`;
|
||||||
const object = new TestObject();
|
const object = new TestObject();
|
||||||
object.set('name', 'object');
|
object.set('name', 'object');
|
||||||
await object.save();
|
await object.save();
|
||||||
@@ -86,12 +118,28 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => {
|
|||||||
const collection = await config.database.adapter._adaptiveCollection('TestObject');
|
const collection = await config.database.adapter._adaptiveCollection('TestObject');
|
||||||
const countResult = await collection.count({ name: 'object' }, { comment: comment });
|
const countResult = await collection.count({ name: 'object' }, { comment: comment });
|
||||||
expect(countResult).toEqual(2);
|
expect(countResult).toEqual(2);
|
||||||
const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } });
|
|
||||||
|
// Wait for profile entry to appear with retry logic
|
||||||
|
let result;
|
||||||
|
const maxRetries = 10;
|
||||||
|
const retryDelay = 100;
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
result = await database.collection('system.profile').findOne(
|
||||||
|
{ 'command.comment': comment },
|
||||||
|
{ sort: { ts: -1 } }
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
expect(result.command.comment).toBe(comment);
|
expect(result.command.comment).toBe(comment);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('attach a comment to an aggregation', async () => {
|
it('attach a comment to an aggregation', async () => {
|
||||||
const comment = 'Hello Parse';
|
const comment = `Hello Parse ${Date.now()}`;
|
||||||
const object = new TestObject();
|
const object = new TestObject();
|
||||||
object.set('name', 'object');
|
object.set('name', 'object');
|
||||||
await object.save();
|
await object.save();
|
||||||
@@ -100,7 +148,23 @@ describe_only_db('mongo')('Parse.Query with comment testing', () => {
|
|||||||
explain: true,
|
explain: true,
|
||||||
comment: comment,
|
comment: comment,
|
||||||
});
|
});
|
||||||
const result = await database.collection('system.profile').findOne({}, { sort: { ts: -1 } });
|
|
||||||
|
// Wait for profile entry to appear with retry logic
|
||||||
|
let result;
|
||||||
|
const maxRetries = 10;
|
||||||
|
const retryDelay = 100;
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
result = await database.collection('system.profile').findOne(
|
||||||
|
{ 'command.explain.comment': comment },
|
||||||
|
{ sort: { ts: -1 } }
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
expect(result.command.explain.comment).toBe(comment);
|
expect(result.command.explain.comment).toBe(comment);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ describe('Parse.Relation testing', () => {
|
|||||||
|
|
||||||
// Parent object is un-fetched, so this will call /1/classes/Car instead
|
// Parent object is un-fetched, so this will call /1/classes/Car instead
|
||||||
// of /1/classes/Wheel and pass { "redirectClassNameForKey":"wheels" }.
|
// of /1/classes/Wheel and pass { "redirectClassNameForKey":"wheels" }.
|
||||||
return query.find(origWheel.id);
|
return query.find();
|
||||||
})
|
})
|
||||||
.then(function (results) {
|
.then(function (results) {
|
||||||
// Make sure this is Wheel and not Car.
|
// Make sure this is Wheel and not Car.
|
||||||
|
|||||||
@@ -7,6 +7,15 @@ const { SpecReporter } = require('jasmine-spec-reporter');
|
|||||||
const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default;
|
const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default;
|
||||||
const { sleep, Connections } = require('../lib/TestUtils');
|
const { sleep, Connections } = require('../lib/TestUtils');
|
||||||
|
|
||||||
|
const originalFetch = global.fetch;
|
||||||
|
let fetchWasMocked = false;
|
||||||
|
|
||||||
|
global.restoreFetch = () => {
|
||||||
|
global.fetch = originalFetch;
|
||||||
|
fetchWasMocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Ensure localhost resolves to ipv4 address first on node v17+
|
// Ensure localhost resolves to ipv4 address first on node v17+
|
||||||
if (dns.setDefaultResultOrder) {
|
if (dns.setDefaultResultOrder) {
|
||||||
dns.setDefaultResultOrder('ipv4first');
|
dns.setDefaultResultOrder('ipv4first');
|
||||||
@@ -205,6 +214,7 @@ const reconfigureServer = async (changedConfiguration = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
global.restoreFetch();
|
||||||
await reconfigureServer();
|
await reconfigureServer();
|
||||||
Parse.initialize('test', 'test', 'test');
|
Parse.initialize('test', 'test', 'test');
|
||||||
Parse.serverURL = serverURL;
|
Parse.serverURL = serverURL;
|
||||||
@@ -212,7 +222,18 @@ beforeAll(async () => {
|
|||||||
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
|
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
if(fetchWasMocked) {
|
||||||
|
global.restoreFetch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
global.afterEachFn = async () => {
|
global.afterEachFn = async () => {
|
||||||
|
// Restore fetch to prevent mock pollution between tests (only if it was mocked)
|
||||||
|
if (fetchWasMocked) {
|
||||||
|
global.restoreFetch();
|
||||||
|
}
|
||||||
|
|
||||||
Parse.Cloud._removeAllHooks();
|
Parse.Cloud._removeAllHooks();
|
||||||
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient();
|
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient();
|
||||||
defaults.protectedFields = { _User: { '*': ['email'] } };
|
defaults.protectedFields = { _User: { '*': ['email'] } };
|
||||||
@@ -251,6 +272,7 @@ global.afterEachFn = async () => {
|
|||||||
afterEach(global.afterEachFn);
|
afterEach(global.afterEachFn);
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
|
global.restoreFetch();
|
||||||
global.displayTestStats();
|
global.displayTestStats();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -388,9 +410,22 @@ function mockShortLivedAuth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mockFetch(mockResponses) {
|
function mockFetch(mockResponses) {
|
||||||
global.fetch = jasmine.createSpy('fetch').and.callFake((url, options = { }) => {
|
const spy = jasmine.createSpy('fetch');
|
||||||
|
fetchWasMocked = true; // Track that fetch was mocked for cleanup
|
||||||
|
|
||||||
|
global.fetch = (url, options = {}) => {
|
||||||
|
// Allow requests to the Parse Server to pass through WITHOUT recording in spy
|
||||||
|
// This prevents tests from failing when they check that fetch wasn't called
|
||||||
|
// but the Parse SDK makes internal requests to the Parse Server
|
||||||
|
if (typeof url === 'string' && url.includes(serverURL)) {
|
||||||
|
return originalFetch(url, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record non-Parse-Server calls in the spy
|
||||||
|
spy(url, options);
|
||||||
|
|
||||||
options.method ||= 'GET';
|
options.method ||= 'GET';
|
||||||
const mockResponse = mockResponses.find(
|
const mockResponse = mockResponses?.find(
|
||||||
(mock) => mock.url === url && mock.method === options.method
|
(mock) => mock.url === url && mock.method === options.method
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -402,7 +437,11 @@ function mockFetch(mockResponses) {
|
|||||||
ok: false,
|
ok: false,
|
||||||
statusText: 'Unknown URL or method',
|
statusText: 'Unknown URL or method',
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Expose spy methods for test assertions
|
||||||
|
global.fetch.calls = spy.calls;
|
||||||
|
global.fetch.and = spy.and;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -175,12 +175,10 @@ describe('Vulnerabilities', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
|
// The new Parse SDK handles prototype pollution prevention in .set()
|
||||||
new Parse.Error(
|
// so no error is thrown, but the object prototype should not be polluted
|
||||||
Parse.Error.INVALID_KEY_NAME,
|
await new Parse.Object('TestObject').save();
|
||||||
'Prohibited keyword in request data: {"key":"constructor"}.'
|
expect(Object.prototype.dummy).toBeUndefined();
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('denies creating global config with polluted data', async () => {
|
it('denies creating global config with polluted data', async () => {
|
||||||
@@ -270,12 +268,10 @@ describe('Vulnerabilities', () => {
|
|||||||
res.json({ success: object });
|
res.json({ success: object });
|
||||||
});
|
});
|
||||||
await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave');
|
await Parse.Hooks.createTrigger('TestObject', 'beforeSave', hookServerURL + '/BeforeSave');
|
||||||
await expectAsync(new Parse.Object('TestObject').save()).toBeRejectedWith(
|
// The new Parse SDK handles prototype pollution prevention in .set()
|
||||||
new Parse.Error(
|
// so no error is thrown, but the object prototype should not be polluted
|
||||||
Parse.Error.INVALID_KEY_NAME,
|
await new Parse.Object('TestObject').save();
|
||||||
'Prohibited keyword in request data: {"key":"constructor"}.'
|
expect(Object.prototype.dummy).toBeUndefined();
|
||||||
)
|
|
||||||
);
|
|
||||||
await new Promise(resolve => server.close(resolve));
|
await new Promise(resolve => server.close(resolve));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user