Skip to content

Commit 6b22b1e

Browse files
committed
test: move tests inside root Vulnerabilities describe block
1 parent 7158d89 commit 6b22b1e

File tree

1 file changed

+120
-120
lines changed

1 file changed

+120
-120
lines changed

spec/vulnerabilities.spec.js

Lines changed: 120 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,126 @@ describe('Vulnerabilities', () => {
918918
await expectAsync(obj.save()).toBeResolved();
919919
});
920920
});
921+
922+
describe('(GHSA-mmg8-87c5-jrc2) LiveQuery protected-field guard bypass via array-like $or/$and/$nor', () => {
923+
const { sleep } = require('../lib/TestUtils');
924+
let obj;
925+
926+
beforeEach(async () => {
927+
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null);
928+
await reconfigureServer({
929+
liveQuery: { classNames: ['SecretClass'] },
930+
startLiveQueryServer: true,
931+
verbose: false,
932+
silent: true,
933+
});
934+
const config = Config.get(Parse.applicationId);
935+
const schemaController = await config.database.loadSchema();
936+
await schemaController.addClassIfNotExists(
937+
'SecretClass',
938+
{ secretObj: { type: 'Object' }, publicField: { type: 'String' } },
939+
);
940+
await schemaController.updateClass(
941+
'SecretClass',
942+
{},
943+
{
944+
find: { '*': true },
945+
get: { '*': true },
946+
create: { '*': true },
947+
update: { '*': true },
948+
delete: { '*': true },
949+
addField: {},
950+
protectedFields: { '*': ['secretObj'] },
951+
}
952+
);
953+
954+
obj = new Parse.Object('SecretClass');
955+
obj.set('secretObj', { apiKey: 'SENSITIVE_KEY_123', score: 42 });
956+
obj.set('publicField', 'visible');
957+
await obj.save(null, { useMasterKey: true });
958+
});
959+
960+
afterEach(async () => {
961+
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
962+
if (client) {
963+
await client.close();
964+
}
965+
});
966+
967+
it('should reject subscription with array-like $or containing protected field', async () => {
968+
const query = new Parse.Query('SecretClass');
969+
query._where = {
970+
$or: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
971+
};
972+
await expectAsync(query.subscribe()).toBeRejectedWith(
973+
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
974+
);
975+
});
976+
977+
it('should reject subscription with array-like $and containing protected field', async () => {
978+
const query = new Parse.Query('SecretClass');
979+
query._where = {
980+
$and: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, '1': { publicField: 'visible' }, length: 2 },
981+
};
982+
await expectAsync(query.subscribe()).toBeRejectedWith(
983+
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
984+
);
985+
});
986+
987+
it('should reject subscription with array-like $nor containing protected field', async () => {
988+
const query = new Parse.Query('SecretClass');
989+
query._where = {
990+
$nor: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
991+
};
992+
await expectAsync(query.subscribe()).toBeRejectedWith(
993+
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
994+
);
995+
});
996+
997+
it('should reject subscription with array-like $or even on non-protected fields', async () => {
998+
const query = new Parse.Query('SecretClass');
999+
query._where = {
1000+
$or: { '0': { publicField: 'visible' }, length: 1 },
1001+
};
1002+
await expectAsync(query.subscribe()).toBeRejectedWith(
1003+
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
1004+
);
1005+
});
1006+
1007+
it('should not create oracle via array-like $or bypass on protected fields', async () => {
1008+
const query = new Parse.Query('SecretClass');
1009+
query._where = {
1010+
$or: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
1011+
};
1012+
1013+
// Subscription must be rejected; no event oracle should be possible
1014+
let subscriptionError;
1015+
let subscription;
1016+
try {
1017+
subscription = await query.subscribe();
1018+
} catch (e) {
1019+
subscriptionError = e;
1020+
}
1021+
1022+
if (!subscriptionError) {
1023+
const updateSpy = jasmine.createSpy('update');
1024+
subscription.on('create', updateSpy);
1025+
subscription.on('update', updateSpy);
1026+
1027+
// Trigger an object change
1028+
obj.set('publicField', 'changed');
1029+
await obj.save(null, { useMasterKey: true });
1030+
await sleep(500);
1031+
1032+
// If subscription somehow accepted, verify no events fired (evaluator defense)
1033+
expect(updateSpy).not.toHaveBeenCalled();
1034+
fail('Expected subscription to be rejected');
1035+
}
1036+
expect(subscriptionError).toEqual(
1037+
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
1038+
);
1039+
});
1040+
});
9211041
});
9221042

9231043
describe('Malformed $regex information disclosure', () => {
@@ -5286,123 +5406,3 @@ describe('(GHSA-p2w6-rmh7-w8q3) SQL Injection via aggregate and distinct field n
52865406
});
52875407
});
52885408
});
5289-
5290-
describe('(GHSA-mmg8-87c5-jrc2) LiveQuery protected-field guard bypass via array-like $or/$and/$nor', () => {
5291-
const { sleep } = require('../lib/TestUtils');
5292-
let obj;
5293-
5294-
beforeEach(async () => {
5295-
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null);
5296-
await reconfigureServer({
5297-
liveQuery: { classNames: ['SecretClass'] },
5298-
startLiveQueryServer: true,
5299-
verbose: false,
5300-
silent: true,
5301-
});
5302-
const config = Config.get(Parse.applicationId);
5303-
const schemaController = await config.database.loadSchema();
5304-
await schemaController.addClassIfNotExists(
5305-
'SecretClass',
5306-
{ secretObj: { type: 'Object' }, publicField: { type: 'String' } },
5307-
);
5308-
await schemaController.updateClass(
5309-
'SecretClass',
5310-
{},
5311-
{
5312-
find: { '*': true },
5313-
get: { '*': true },
5314-
create: { '*': true },
5315-
update: { '*': true },
5316-
delete: { '*': true },
5317-
addField: {},
5318-
protectedFields: { '*': ['secretObj'] },
5319-
}
5320-
);
5321-
5322-
obj = new Parse.Object('SecretClass');
5323-
obj.set('secretObj', { apiKey: 'SENSITIVE_KEY_123', score: 42 });
5324-
obj.set('publicField', 'visible');
5325-
await obj.save(null, { useMasterKey: true });
5326-
});
5327-
5328-
afterEach(async () => {
5329-
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
5330-
if (client) {
5331-
await client.close();
5332-
}
5333-
});
5334-
5335-
it('should reject subscription with array-like $or containing protected field', async () => {
5336-
const query = new Parse.Query('SecretClass');
5337-
query._where = {
5338-
$or: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
5339-
};
5340-
await expectAsync(query.subscribe()).toBeRejectedWith(
5341-
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
5342-
);
5343-
});
5344-
5345-
it('should reject subscription with array-like $and containing protected field', async () => {
5346-
const query = new Parse.Query('SecretClass');
5347-
query._where = {
5348-
$and: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, '1': { publicField: 'visible' }, length: 2 },
5349-
};
5350-
await expectAsync(query.subscribe()).toBeRejectedWith(
5351-
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
5352-
);
5353-
});
5354-
5355-
it('should reject subscription with array-like $nor containing protected field', async () => {
5356-
const query = new Parse.Query('SecretClass');
5357-
query._where = {
5358-
$nor: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
5359-
};
5360-
await expectAsync(query.subscribe()).toBeRejectedWith(
5361-
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
5362-
);
5363-
});
5364-
5365-
it('should reject subscription with array-like $or even on non-protected fields', async () => {
5366-
const query = new Parse.Query('SecretClass');
5367-
query._where = {
5368-
$or: { '0': { publicField: 'visible' }, length: 1 },
5369-
};
5370-
await expectAsync(query.subscribe()).toBeRejectedWith(
5371-
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
5372-
);
5373-
});
5374-
5375-
it('should not create oracle via array-like $or bypass on protected fields', async () => {
5376-
const query = new Parse.Query('SecretClass');
5377-
query._where = {
5378-
$or: { '0': { 'secretObj.apiKey': 'SENSITIVE_KEY_123' }, length: 1 },
5379-
};
5380-
5381-
// Subscription must be rejected; no event oracle should be possible
5382-
let subscriptionError;
5383-
let subscription;
5384-
try {
5385-
subscription = await query.subscribe();
5386-
} catch (e) {
5387-
subscriptionError = e;
5388-
}
5389-
5390-
if (!subscriptionError) {
5391-
const updateSpy = jasmine.createSpy('update');
5392-
subscription.on('create', updateSpy);
5393-
subscription.on('update', updateSpy);
5394-
5395-
// Trigger an object change
5396-
obj.set('publicField', 'changed');
5397-
await obj.save(null, { useMasterKey: true });
5398-
await sleep(500);
5399-
5400-
// If subscription somehow accepted, verify no events fired (evaluator defense)
5401-
expect(updateSpy).not.toHaveBeenCalled();
5402-
fail('Expected subscription to be rejected');
5403-
}
5404-
expect(subscriptionError).toEqual(
5405-
jasmine.objectContaining({ code: Parse.Error.INVALID_QUERY })
5406-
);
5407-
});
5408-
});

0 commit comments

Comments
 (0)