Skip to content

Commit 51efb1e

Browse files
authored
fix: Instance comparison with instanceof is not realm-safe (#10225)
1 parent cdc2551 commit 51efb1e

34 files changed

+443
-127
lines changed

eslint.config.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,46 @@ module.exports = [
4141
curly: ["error", "all"],
4242
"block-spacing": ["error", "always"],
4343
"no-unused-vars": "off",
44-
"no-console": "warn"
44+
"no-console": "warn",
45+
"no-restricted-syntax": [
46+
"error",
47+
{
48+
selector: "BinaryExpression[operator='instanceof'][right.name='Date']",
49+
message: "Use Utils.isDate() instead of instanceof Date (cross-realm safe).",
50+
},
51+
{
52+
selector: "BinaryExpression[operator='instanceof'][right.name='RegExp']",
53+
message: "Use Utils.isRegExp() instead of instanceof RegExp (cross-realm safe).",
54+
},
55+
{
56+
selector: "BinaryExpression[operator='instanceof'][right.name='Error']",
57+
message: "Use Utils.isNativeError() instead of instanceof Error (cross-realm safe).",
58+
},
59+
{
60+
selector: "BinaryExpression[operator='instanceof'][right.name='Promise']",
61+
message: "Use Utils.isPromise() instead of instanceof Promise (cross-realm safe).",
62+
},
63+
{
64+
selector: "BinaryExpression[operator='instanceof'][right.name='Map']",
65+
message: "Use Utils.isMap() instead of instanceof Map (cross-realm safe).",
66+
},
67+
{
68+
selector: "BinaryExpression[operator='instanceof'][right.name='Object']",
69+
message: "Use Utils.isObject() instead of instanceof Object (cross-realm safe).",
70+
},
71+
{
72+
selector: "BinaryExpression[operator='instanceof'][right.name='Set']",
73+
message: "Use Utils.isSet() instead of instanceof Set (cross-realm safe).",
74+
},
75+
{
76+
selector: "BinaryExpression[operator='instanceof'][right.name='Buffer']",
77+
message: "Use Buffer.isBuffer() instead of instanceof Buffer (cross-realm safe).",
78+
},
79+
{
80+
selector: "BinaryExpression[operator='instanceof'][right.name='Array']",
81+
message: "Use Array.isArray() instead of instanceof Array (cross-realm safe).",
82+
},
83+
]
4584
},
4685
},
4786
];

spec/AdapterLoader.spec.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const { loadAdapter, loadModule } = require('../lib/Adapters/AdapterLoader');
22
const FilesAdapter = require('@parse/fs-files-adapter').default;
33
const MockFilesAdapter = require('mock-files-adapter');
44
const Config = require('../lib/Config');
5+
const Utils = require('../lib/Utils');
56

67
describe('AdapterLoader', () => {
78
it('should instantiate an adapter from string in object', done => {
@@ -15,7 +16,7 @@ describe('AdapterLoader', () => {
1516
},
1617
});
1718

18-
expect(adapter instanceof Object).toBe(true);
19+
expect(Utils.isObject(adapter)).toBe(true);
1920
expect(adapter.options.key).toBe('value');
2021
expect(adapter.options.foo).toBe('bar');
2122
done();
@@ -25,7 +26,7 @@ describe('AdapterLoader', () => {
2526
const adapterPath = require('path').resolve('./spec/support/MockAdapter');
2627
const adapter = loadAdapter(adapterPath);
2728

28-
expect(adapter instanceof Object).toBe(true);
29+
expect(Utils.isObject(adapter)).toBe(true);
2930
done();
3031
});
3132

spec/CloudCode.spec.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const ParseServer = require('../lib/index').ParseServer;
55
const request = require('../lib/request');
66
const InMemoryCacheAdapter = require('../lib/Adapters/Cache/InMemoryCacheAdapter')
77
.InMemoryCacheAdapter;
8+
const Utils = require('../lib/Utils');
89

910
const mockAdapter = {
1011
createFile: async filename => ({
@@ -1272,15 +1273,15 @@ describe('Cloud Code', () => {
12721273

12731274
it('test cloud function request params types', function (done) {
12741275
Parse.Cloud.define('params', function (req) {
1275-
expect(req.params.date instanceof Date).toBe(true);
1276+
expect(Utils.isDate(req.params.date)).toBe(true);
12761277
expect(req.params.date.getTime()).toBe(1463907600000);
1277-
expect(req.params.dateList[0] instanceof Date).toBe(true);
1278+
expect(Utils.isDate(req.params.dateList[0])).toBe(true);
12781279
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
1279-
expect(req.params.complexStructure.date[0] instanceof Date).toBe(true);
1280+
expect(Utils.isDate(req.params.complexStructure.date[0])).toBe(true);
12801281
expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
1281-
expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true);
1282+
expect(Utils.isDate(req.params.complexStructure.deepDate.date[0])).toBe(true);
12821283
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
1283-
expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true);
1284+
expect(Utils.isDate(req.params.complexStructure.deepDate2[0].date)).toBe(true);
12841285
expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
12851286
// Regression for #2294
12861287
expect(req.params.file instanceof Parse.File).toBe(true);

spec/GridFSBucketStorageAdapter.spec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ describe_only_db('mongo')('GridFSBucket', () => {
103103
).toEqual(1);
104104
expect(notRotated.length).toEqual(0);
105105
let result = await encryptedAdapter.getFileData(fileName1);
106-
expect(result instanceof Buffer).toBe(true);
106+
expect(Buffer.isBuffer(result)).toBe(true);
107107
expect(result.toString('utf-8')).toEqual(data1);
108108
const encryptedData1 = await unencryptedAdapter.getFileData(fileName1);
109109
expect(encryptedData1.toString('utf-8')).not.toEqual(unencryptedResult1);
110110
result = await encryptedAdapter.getFileData(fileName2);
111-
expect(result instanceof Buffer).toBe(true);
111+
expect(Buffer.isBuffer(result)).toBe(true);
112112
expect(result.toString('utf-8')).toEqual(data2);
113113
const encryptedData2 = await unencryptedAdapter.getFileData(fileName2);
114114
expect(encryptedData2.toString('utf-8')).not.toEqual(unencryptedResult2);
@@ -146,7 +146,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
146146
).toEqual(1);
147147
expect(notRotated.length).toEqual(0);
148148
let result = await encryptedAdapter.getFileData(fileName1);
149-
expect(result instanceof Buffer).toBe(true);
149+
expect(Buffer.isBuffer(result)).toBe(true);
150150
expect(result.toString('utf-8')).toEqual(data1);
151151
let decryptionError1;
152152
let encryptedData1;
@@ -158,7 +158,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
158158
expect(decryptionError1).toMatch('Error');
159159
expect(encryptedData1).toBeUndefined();
160160
result = await encryptedAdapter.getFileData(fileName2);
161-
expect(result instanceof Buffer).toBe(true);
161+
expect(Buffer.isBuffer(result)).toBe(true);
162162
expect(result.toString('utf-8')).toEqual(data2);
163163
let decryptionError2;
164164
let encryptedData2;
@@ -203,7 +203,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
203203
).toEqual(1);
204204
expect(notRotated.length).toEqual(0);
205205
let result = await unEncryptedAdapter.getFileData(fileName1);
206-
expect(result instanceof Buffer).toBe(true);
206+
expect(Buffer.isBuffer(result)).toBe(true);
207207
expect(result.toString('utf-8')).toEqual(data1);
208208
let decryptionError1;
209209
let encryptedData1;
@@ -215,7 +215,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
215215
expect(decryptionError1).toMatch('Error');
216216
expect(encryptedData1).toBeUndefined();
217217
result = await unEncryptedAdapter.getFileData(fileName2);
218-
expect(result instanceof Buffer).toBe(true);
218+
expect(Buffer.isBuffer(result)).toBe(true);
219219
expect(result.toString('utf-8')).toEqual(data2);
220220
let decryptionError2;
221221
let encryptedData2;
@@ -271,7 +271,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
271271
}).length
272272
).toEqual(0);
273273
let result = await encryptedAdapter.getFileData(fileName1);
274-
expect(result instanceof Buffer).toBe(true);
274+
expect(Buffer.isBuffer(result)).toBe(true);
275275
expect(result.toString('utf-8')).toEqual(data1);
276276
let decryptionError1;
277277
let encryptedData1;
@@ -283,7 +283,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
283283
expect(decryptionError1).toMatch('Error');
284284
expect(encryptedData1).toBeUndefined();
285285
result = await encryptedAdapter.getFileData(fileName2);
286-
expect(result instanceof Buffer).toBe(true);
286+
expect(Buffer.isBuffer(result)).toBe(true);
287287
expect(result.toString('utf-8')).toEqual(data2);
288288
let decryptionError2;
289289
let encryptedData2;
@@ -338,7 +338,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
338338
}).length
339339
).toEqual(1);
340340
let result = await encryptedAdapter.getFileData(fileName1);
341-
expect(result instanceof Buffer).toBe(true);
341+
expect(Buffer.isBuffer(result)).toBe(true);
342342
expect(result.toString('utf-8')).toEqual(data1);
343343
let decryptionError1;
344344
let encryptedData1;
@@ -350,7 +350,7 @@ describe_only_db('mongo')('GridFSBucket', () => {
350350
expect(decryptionError1).toMatch('Error');
351351
expect(encryptedData1).toBeUndefined();
352352
result = await encryptedAdapter.getFileData(fileName2);
353-
expect(result instanceof Buffer).toBe(true);
353+
expect(Buffer.isBuffer(result)).toBe(true);
354354
expect(result.toString('utf-8')).toEqual(data2);
355355
let decryptionError2;
356356
let encryptedData2;

spec/MongoStorageAdapter.spec.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDataba
66
const request = require('../lib/request');
77
const Config = require('../lib/Config');
88
const TestUtils = require('../lib/TestUtils');
9+
const Utils = require('../lib/Utils');
910

1011
const fakeClient = {
1112
s: { options: { dbName: null } },
@@ -243,15 +244,15 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
243244
.then(results => {
244245
expect(results.length).toEqual(1);
245246
const mob = results[0];
246-
expect(mob.array instanceof Array).toBe(true);
247+
expect(Array.isArray(mob.array)).toBe(true);
247248
expect(typeof mob.object).toBe('object');
248-
expect(mob.date instanceof Date).toBe(true);
249+
expect(Utils.isDate(mob.date)).toBe(true);
249250
return adapter.find('MyClass', schema, {}, {});
250251
})
251252
.then(results => {
252253
expect(results.length).toEqual(1);
253254
const mob = results[0];
254-
expect(mob.array instanceof Array).toBe(true);
255+
expect(Array.isArray(mob.array)).toBe(true);
255256
expect(typeof mob.object).toBe('object');
256257
expect(mob.date.__type).toBe('Date');
257258
expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z');
@@ -278,9 +279,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
278279
}).save();
279280
const adapter = Config.get(Parse.applicationId).database.adapter;
280281
const [object] = await adapter._rawFind('MyClass', {});
281-
expect(object.date instanceof Date).toBeTrue();
282-
expect(object.bar.date instanceof Date).toBeTrue();
283-
expect(object.foo.test.date instanceof Date).toBeTrue();
282+
expect(Utils.isDate(object.date)).toBeTrue();
283+
expect(Utils.isDate(object.bar.date)).toBeTrue();
284+
expect(Utils.isDate(object.foo.test.date)).toBeTrue();
284285
});
285286

286287
it('handles nested dates in array ', async () => {
@@ -297,13 +298,13 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
297298
}).save();
298299
const adapter = Config.get(Parse.applicationId).database.adapter;
299300
const [object] = await adapter._rawFind('MyClass', {});
300-
expect(object.date[0] instanceof Date).toBeTrue();
301-
expect(object.bar.date[0] instanceof Date).toBeTrue();
302-
expect(object.foo.test.date[0] instanceof Date).toBeTrue();
301+
expect(Utils.isDate(object.date[0])).toBeTrue();
302+
expect(Utils.isDate(object.bar.date[0])).toBeTrue();
303+
expect(Utils.isDate(object.foo.test.date[0])).toBeTrue();
303304
const obj = await new Parse.Query('MyClass').first({ useMasterKey: true });
304-
expect(obj.get('date')[0] instanceof Date).toBeTrue();
305-
expect(obj.get('bar').date[0] instanceof Date).toBeTrue();
306-
expect(obj.get('foo').test.date[0] instanceof Date).toBeTrue();
305+
expect(Utils.isDate(obj.get('date')[0])).toBeTrue();
306+
expect(Utils.isDate(obj.get('bar').date[0])).toBeTrue();
307+
expect(Utils.isDate(obj.get('foo').test.date[0])).toBeTrue();
307308
});
308309

309310
it('upserts with $setOnInsert', async () => {
@@ -376,7 +377,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
376377
})
377378
.then(results => {
378379
const mob = results;
379-
expect(mob.array instanceof Array).toBe(true);
380+
expect(Array.isArray(mob.array)).toBe(true);
380381
expect(typeof mob.object).toBe('object');
381382
expect(mob.date.__type).toBe('Date');
382383
expect(mob.date.iso).toBe('2016-05-26T20:55:01.154Z');
@@ -385,9 +386,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
385386
.then(results => {
386387
expect(results.length).toEqual(1);
387388
const mob = results[0];
388-
expect(mob.array instanceof Array).toBe(true);
389+
expect(Array.isArray(mob.array)).toBe(true);
389390
expect(typeof mob.object).toBe('object');
390-
expect(mob.date instanceof Date).toBe(true);
391+
expect(Utils.isDate(mob.date)).toBe(true);
391392
done();
392393
})
393394
.catch(error => {

spec/MongoTransform.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ describe('parseObjectToMongoObjectForCreate', () => {
3333
const output = transform.parseObjectToMongoObjectForCreate(null, input, {
3434
fields: {},
3535
});
36-
expect(output._created_at instanceof Date).toBe(true);
37-
expect(output._updated_at instanceof Date).toBe(true);
36+
expect(Utils.isDate(output._created_at)).toBe(true);
37+
expect(Utils.isDate(output._updated_at)).toBe(true);
3838
done();
3939
});
4040

spec/ParseAPI.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const Parse = require('parse/node');
77
const Config = require('../lib/Config');
88
const SchemaController = require('../lib/Controllers/SchemaController');
99
const { destroyAllDataPermanently } = require('../lib/TestUtils');
10+
const Utils = require('../lib/Utils');
1011

1112
const userSchema = SchemaController.convertSchemaToAdapterSchema({
1213
className: '_User',
@@ -327,10 +328,10 @@ describe('miscellaneous', () => {
327328
return obj2.fetch();
328329
})
329330
.then(obj2 => {
330-
expect(obj2.get('date') instanceof Date).toBe(true);
331-
expect(obj2.get('array') instanceof Array).toBe(true);
332-
expect(obj2.get('object') instanceof Array).toBe(false);
333-
expect(obj2.get('object') instanceof Object).toBe(true);
331+
expect(Utils.isDate(obj2.get('date'))).toBe(true);
332+
expect(Array.isArray(obj2.get('array'))).toBe(true);
333+
expect(Array.isArray(obj2.get('object'))).toBe(false);
334+
expect(Utils.isObject(obj2.get('object'))).toBe(true);
334335
done();
335336
});
336337
});

spec/ParseGraphQLServer.spec.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fet
55
const FormData = require('form-data');
66
require('./helper');
77
const { updateCLP } = require('./support/dev');
8+
const Utils = require('../lib/Utils');
89

910
const pluralize = require('pluralize');
1011
const createUploadLink = (...args) => import('apollo-upload-client/createUploadLink.mjs').then(({ default: fn }) => fn(...args));
@@ -8476,15 +8477,15 @@ describe('ParseGraphQLServer', () => {
84768477

84778478
it('should accept different params', done => {
84788479
Parse.Cloud.define('hello', async req => {
8479-
expect(req.params.date instanceof Date).toBe(true);
8480+
expect(Utils.isDate(req.params.date)).toBe(true);
84808481
expect(req.params.date.getTime()).toBe(1463907600000);
8481-
expect(req.params.dateList[0] instanceof Date).toBe(true);
8482+
expect(Utils.isDate(req.params.dateList[0])).toBe(true);
84828483
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
8483-
expect(req.params.complexStructure.date[0] instanceof Date).toBe(true);
8484+
expect(Utils.isDate(req.params.complexStructure.date[0])).toBe(true);
84848485
expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
8485-
expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true);
8486+
expect(Utils.isDate(req.params.complexStructure.deepDate.date[0])).toBe(true);
84868487
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
8487-
expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true);
8488+
expect(Utils.isDate(req.params.complexStructure.deepDate2[0].date)).toBe(true);
84888489
expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
84898490
// Regression for #2294
84908491
expect(req.params.file instanceof Parse.File).toBe(true);

spec/ParseQuery.spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const request = require('../lib/request');
99
const ParseServerRESTController = require('../lib/ParseServerRESTController').ParseServerRESTController;
1010
const ParseServer = require('../lib/ParseServer').default;
1111
const Deprecator = require('../lib/Deprecator/Deprecator').default;
12+
const Utils = require('../lib/Utils');
1213

1314
const masterKeyHeaders = {
1415
'X-Parse-Application-Id': 'test',
@@ -1452,8 +1453,8 @@ describe('Parse.Query testing', () => {
14521453
ok(result);
14531454
equal(result.id, objectId);
14541455
equal(result.get('foo'), 'bar');
1455-
ok(result.createdAt instanceof Date);
1456-
ok(result.updatedAt instanceof Date);
1456+
ok(Utils.isDate(result.createdAt));
1457+
ok(Utils.isDate(result.updatedAt));
14571458
done();
14581459
});
14591460
});
@@ -3902,7 +3903,7 @@ describe('Parse.Query testing', () => {
39023903
objs => {
39033904
expect(objs.length).toBe(1);
39043905
expect(objs[0].get('child').get('hello')).toEqual('world');
3905-
expect(objs[0].createdAt instanceof Date).toBe(true);
3906+
expect(Utils.isDate(objs[0].createdAt)).toBe(true);
39063907
done();
39073908
},
39083909
() => {

spec/ParseUser.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const request = require('../lib/request');
1212
const passwordCrypto = require('../lib/password');
1313
const Config = require('../lib/Config');
1414
const cryptoUtils = require('../lib/cryptoUtils');
15+
const Utils = require('../lib/Utils');
1516

1617

1718
describe('allowExpiredAuthDataToken option', () => {
@@ -1128,9 +1129,9 @@ describe('Parse.User testing', () => {
11281129

11291130
equal(userInMemory.id, id, 'id should be set');
11301131

1131-
expect(userInMemory.updatedAt instanceof Date).toBe(true);
1132+
expect(Utils.isDate(userInMemory.updatedAt)).toBe(true);
11321133

1133-
ok(userInMemory.createdAt instanceof Date);
1134+
ok(Utils.isDate(userInMemory.createdAt));
11341135

11351136
ok(userInMemory.getSessionToken(), 'user should have a sessionToken after saving');
11361137

@@ -1167,9 +1168,9 @@ describe('Parse.User testing', () => {
11671168

11681169
equal(userFromDisk.id, id, 'id should be set on userFromDisk');
11691170

1170-
ok(userFromDisk.updatedAt instanceof Date);
1171+
ok(Utils.isDate(userFromDisk.updatedAt));
11711172

1172-
ok(userFromDisk.createdAt instanceof Date);
1173+
ok(Utils.isDate(userFromDisk.createdAt));
11731174

11741175
ok(userFromDisk.getSessionToken(), 'userFromDisk should have a sessionToken');
11751176

0 commit comments

Comments
 (0)