From 952db2a65f60378b48b6007d26126c738a871480 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 5 Jul 2026 12:22:12 +1000 Subject: [PATCH] fix: LiveQuery matcher crashes when a pointer or typed field value is null --- spec/QueryTools.spec.js | 22 ++++++++++++++++++++++ src/LiveQuery/QueryTools.js | 2 ++ src/LiveQuery/equalObjects.js | 4 ++++ 3 files changed, 28 insertions(+) diff --git a/spec/QueryTools.spec.js b/spec/QueryTools.spec.js index 109c2a209b..5cdd0d74be 100644 --- a/spec/QueryTools.spec.js +++ b/spec/QueryTools.spec.js @@ -777,6 +777,28 @@ describe('matchesQuery', function () { expect(matchesQuery(message, q)).toBe(false); }); + it('does not match or crash when a pointer field is null (#7929)', () => { + // Parse Dashboard stores a cleared pointer as null rather than unsetting it. + const message = { + id: new Id('Message', 'O1'), + profile: null, + }; + const q = new Parse.Query('Message'); + q.equalTo('profile', Parse.Object.fromJSON({ className: 'Profile', objectId: 'abc' })); + expect(matchesQuery(message, q)).toBe(false); + }); + + it('does not match or crash when a typed field is null (#7929)', () => { + // Same null-field scenario against a non-pointer typed constraint (goes through equalObjects). + const obj = { + id: new Id('Person', 'O1'), + birthday: null, + }; + const q = new Parse.Query('Person'); + q.equalTo('birthday', new Date(1990, 1)); + expect(matchesQuery(obj, q)).toBe(false); + }); + it('should support containedIn with array of pointers', () => { const message = { id: new Id('Message', 'O2'), diff --git a/src/LiveQuery/QueryTools.js b/src/LiveQuery/QueryTools.js index 37cfbfa47f..8ff03c0657 100644 --- a/src/LiveQuery/QueryTools.js +++ b/src/LiveQuery/QueryTools.js @@ -264,8 +264,10 @@ function matchesKeyConstraints(object, key, constraints) { if (constraints.__type) { if (constraints.__type === 'Pointer') { return equalObjectsGeneric(object[key], constraints, function (obj, ptr) { + // A cleared pointer can be stored as null (e.g. via Parse Dashboard), guard it (#7929) return ( typeof obj !== 'undefined' && + obj !== null && ptr.className === obj.className && ptr.objectId === obj.objectId ); diff --git a/src/LiveQuery/equalObjects.js b/src/LiveQuery/equalObjects.js index 5bc3f5e957..477abbac61 100644 --- a/src/LiveQuery/equalObjects.js +++ b/src/LiveQuery/equalObjects.js @@ -14,6 +14,10 @@ function equalObjects(a, b) { if (a === b) { return true; } + // typeof null is 'object', so guard before dereferencing either side (#7929) + if (a === null || b === null) { + return a === b; + } if (toString.call(a) === '[object Date]') { if (toString.call(b) === '[object Date]') { return +a === +b;