From 87f9ac4dc2d1fa0e7eaf00d88327dedba5351b20 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 15 Mar 2026 12:24:16 +0000 Subject: [PATCH 1/3] Update vulnerabilities.spec.js --- spec/vulnerabilities.spec.js | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 8fa3436537..1e70f24b09 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -2769,3 +2769,80 @@ describe('(GHSA-9xp9-j92r-p88v) Stack overflow process crash via deeply nested q ); }); }); + +describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => { + const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery; + + // Unit tests: matchesQuery receives the raw where clause (not {className, where}) + // just as _matchesSubscription passes subscription.query (the where clause) + describe('matchesQuery with type-confused operators', () => { + it('$in with object instead of array throws', () => { + const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' }; + const where = { name: { $in: { x: 1 } } }; + expect(() => matchesQuery(object, where)).toThrow(); + }); + + it('$nin with object instead of array throws', () => { + const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' }; + const where = { name: { $nin: { x: 1 } } }; + expect(() => matchesQuery(object, where)).toThrow(); + }); + + it('$containedBy with object instead of array throws', () => { + const object = { className: 'TestObject', objectId: 'obj1', name: ['abc'] }; + const where = { name: { $containedBy: { x: 1 } } }; + expect(() => matchesQuery(object, where)).toThrow(); + }); + + it('$containedBy with missing field throws', () => { + const object = { className: 'TestObject', objectId: 'obj1' }; + const where = { name: { $containedBy: ['abc', 'xyz'] } }; + expect(() => matchesQuery(object, where)).toThrow(); + }); + + it('$all with object field value throws', () => { + const object = { className: 'TestObject', objectId: 'obj1', name: { x: 1 } }; + const where = { name: { $all: ['abc'] } }; + expect(() => matchesQuery(object, where)).toThrow(); + }); + + it('$in with valid array does not throw', () => { + const object = { className: 'TestObject', objectId: 'obj1', name: 'abc' }; + const where = { name: { $in: ['abc', 'xyz'] } }; + expect(() => matchesQuery(object, where)).not.toThrow(); + expect(matchesQuery(object, where)).toBe(true); + }); + }); + + // Integration test: verify that a LiveQuery subscription with type-confused + // operators does not crash the server and other subscriptions continue working + it('server does not crash and other subscriptions work when type-confused subscription exists', async done => { + // First subscribe with a malformed query via manual client + const malClient = new Parse.LiveQueryClient({ + applicationId: 'test', + serverURL: 'ws://localhost:1337', + javascriptKey: 'test', + }); + malClient.open(); + const malformedQuery = new Parse.Query('TestObject'); + malformedQuery._where = { name: { $in: { x: 1 } } }; + await malClient.subscribe(malformedQuery); + + // Then subscribe with a valid query using the default client + const validQuery = new Parse.Query('TestObject'); + validQuery.equalTo('name', 'test'); + const validSubscription = await validQuery.subscribe(); + + validSubscription.on('create', object => { + // The valid subscription should still receive events + expect(object.get('name')).toBe('test'); + malClient.close(); + done(); + }); + + // Trigger an event + const obj = new Parse.Object('TestObject'); + obj.set('name', 'test'); + await obj.save(); + }); +}); From 5b9cb5cdf8be992674f6165f12640d4e3df1ca58 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:17:13 +0000 Subject: [PATCH 2/3] Update vulnerabilities.spec.js --- spec/vulnerabilities.spec.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 1e70f24b09..928ca670c8 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -2816,7 +2816,7 @@ describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => { // Integration test: verify that a LiveQuery subscription with type-confused // operators does not crash the server and other subscriptions continue working - it('server does not crash and other subscriptions work when type-confused subscription exists', async done => { + it('server does not crash and other subscriptions work when type-confused subscription exists', async () => { // First subscribe with a malformed query via manual client const malClient = new Parse.LiveQueryClient({ applicationId: 'test', @@ -2833,16 +2833,20 @@ describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => { validQuery.equalTo('name', 'test'); const validSubscription = await validQuery.subscribe(); - validSubscription.on('create', object => { - // The valid subscription should still receive events - expect(object.get('name')).toBe('test'); - malClient.close(); - done(); - }); + try { + const createPromise = new Promise(resolve => { + validSubscription.on('create', object => { + expect(object.get('name')).toBe('test'); + resolve(); + }); + }); - // Trigger an event - const obj = new Parse.Object('TestObject'); - obj.set('name', 'test'); - await obj.save(); + const obj = new Parse.Object('TestObject'); + obj.set('name', 'test'); + await obj.save(); + await createPromise; + } finally { + malClient.close(); + } }); }); From 3db72b9d66533c28da36a5e4dfde42a1b4001531 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 15 Mar 2026 14:09:39 +0000 Subject: [PATCH 3/3] Update vulnerabilities.spec.js --- spec/vulnerabilities.spec.js | 73 +++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/spec/vulnerabilities.spec.js b/spec/vulnerabilities.spec.js index 928ca670c8..135edf694e 100644 --- a/spec/vulnerabilities.spec.js +++ b/spec/vulnerabilities.spec.js @@ -2816,37 +2816,56 @@ describe('(GHSA-fjxm-vhvc-gcmj) LiveQuery Operator Type Confusion', () => { // Integration test: verify that a LiveQuery subscription with type-confused // operators does not crash the server and other subscriptions continue working - it('server does not crash and other subscriptions work when type-confused subscription exists', async () => { - // First subscribe with a malformed query via manual client - const malClient = new Parse.LiveQueryClient({ - applicationId: 'test', - serverURL: 'ws://localhost:1337', - javascriptKey: 'test', + describe('LiveQuery integration', () => { + beforeEach(async () => { + Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null); + await reconfigureServer({ + liveQuery: { classNames: ['TestObject'] }, + startLiveQueryServer: true, + verbose: false, + silent: true, + }); }); - malClient.open(); - const malformedQuery = new Parse.Query('TestObject'); - malformedQuery._where = { name: { $in: { x: 1 } } }; - await malClient.subscribe(malformedQuery); - // Then subscribe with a valid query using the default client - const validQuery = new Parse.Query('TestObject'); - validQuery.equalTo('name', 'test'); - const validSubscription = await validQuery.subscribe(); + afterEach(async () => { + const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); + if (client) { + await client.close(); + } + }); - try { - const createPromise = new Promise(resolve => { - validSubscription.on('create', object => { - expect(object.get('name')).toBe('test'); - resolve(); - }); + it('server does not crash and other subscriptions work when type-confused subscription exists', async () => { + // First subscribe with a malformed query via manual client + const malClient = new Parse.LiveQueryClient({ + applicationId: 'test', + serverURL: 'ws://localhost:1337', + javascriptKey: 'test', }); + malClient.open(); + const malformedQuery = new Parse.Query('TestObject'); + malformedQuery._where = { name: { $in: { x: 1 } } }; + await malClient.subscribe(malformedQuery); + + // Then subscribe with a valid query using the default client + const validQuery = new Parse.Query('TestObject'); + validQuery.equalTo('name', 'test'); + const validSubscription = await validQuery.subscribe(); + + try { + const createPromise = new Promise(resolve => { + validSubscription.on('create', object => { + expect(object.get('name')).toBe('test'); + resolve(); + }); + }); - const obj = new Parse.Object('TestObject'); - obj.set('name', 'test'); - await obj.save(); - await createPromise; - } finally { - malClient.close(); - } + const obj = new Parse.Object('TestObject'); + obj.set('name', 'test'); + await obj.save(); + await createPromise; + } finally { + malClient.close(); + } + }); }); });