Skip to content

Commit ad463d2

Browse files
authored
test: LiveQuery operator type confusion (GHSA-fjxm-vhvc-gcmj) (#10208)
1 parent f403131 commit ad463d2

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

spec/vulnerabilities.spec.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,3 +2769,103 @@ describe('(GHSA-9xp9-j92r-p88v) Stack overflow process crash via deeply nested q
27692769
);
27702770
});
27712771
});
2772+
2773+
describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => {
2774+
const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery;
2775+
2776+
// Unit tests: matchesQuery receives the raw where clause (not {className, where})
2777+
// just as _matchesSubscription passes subscription.query (the where clause)
2778+
describe('matchesQuery with type-confused operators', () => {
2779+
it('$in with object instead of array throws', () => {
2780+
const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' };
2781+
const where = { name: { $in: { x: 1 } } };
2782+
expect(() => matchesQuery(object, where)).toThrow();
2783+
});
2784+
2785+
it('$nin with object instead of array throws', () => {
2786+
const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' };
2787+
const where = { name: { $nin: { x: 1 } } };
2788+
expect(() => matchesQuery(object, where)).toThrow();
2789+
});
2790+
2791+
it('$containedBy with object instead of array throws', () => {
2792+
const object = { className: 'TestObject', objectId: 'obj1', name: ['abc'] };
2793+
const where = { name: { $containedBy: { x: 1 } } };
2794+
expect(() => matchesQuery(object, where)).toThrow();
2795+
});
2796+
2797+
it('$containedBy with missing field throws', () => {
2798+
const object = { className: 'TestObject', objectId: 'obj1' };
2799+
const where = { name: { $containedBy: ['abc', 'xyz'] } };
2800+
expect(() => matchesQuery(object, where)).toThrow();
2801+
});
2802+
2803+
it('$all with object field value throws', () => {
2804+
const object = { className: 'TestObject', objectId: 'obj1', name: { x: 1 } };
2805+
const where = { name: { $all: ['abc'] } };
2806+
expect(() => matchesQuery(object, where)).toThrow();
2807+
});
2808+
2809+
it('$in with valid array does not throw', () => {
2810+
const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' };
2811+
const where = { name: { $in: ['abc', 'xyz'] } };
2812+
expect(() => matchesQuery(object, where)).not.toThrow();
2813+
expect(matchesQuery(object, where)).toBe(true);
2814+
});
2815+
});
2816+
2817+
// Integration test: verify that a LiveQuery subscription with type-confused
2818+
// operators does not crash the server and other subscriptions continue working
2819+
describe('LiveQuery integration', () => {
2820+
beforeEach(async () => {
2821+
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null);
2822+
await reconfigureServer({
2823+
liveQuery: { classNames: ['TestObject'] },
2824+
startLiveQueryServer: true,
2825+
verbose: false,
2826+
silent: true,
2827+
});
2828+
});
2829+
2830+
afterEach(async () => {
2831+
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
2832+
if (client) {
2833+
await client.close();
2834+
}
2835+
});
2836+
2837+
it('server does not crash and other subscriptions work when type-confused subscription exists', async () => {
2838+
// First subscribe with a malformed query via manual client
2839+
const malClient = new Parse.LiveQueryClient({
2840+
applicationId: 'test',
2841+
serverURL: 'ws://localhost:1337',
2842+
javascriptKey: 'test',
2843+
});
2844+
malClient.open();
2845+
const malformedQuery = new Parse.Query('TestObject');
2846+
malformedQuery._where = { name: { $in: { x: 1 } } };
2847+
await malClient.subscribe(malformedQuery);
2848+
2849+
// Then subscribe with a valid query using the default client
2850+
const validQuery = new Parse.Query('TestObject');
2851+
validQuery.equalTo('name', 'test');
2852+
const validSubscription = await validQuery.subscribe();
2853+
2854+
try {
2855+
const createPromise = new Promise(resolve => {
2856+
validSubscription.on('create', object => {
2857+
expect(object.get('name')).toBe('test');
2858+
resolve();
2859+
});
2860+
});
2861+
2862+
const obj = new Parse.Object('TestObject');
2863+
obj.set('name', 'test');
2864+
await obj.save();
2865+
await createPromise;
2866+
} finally {
2867+
malClient.close();
2868+
}
2869+
});
2870+
});
2871+
});

0 commit comments

Comments
 (0)