Skip to content

Commit 04be4b0

Browse files
committed
refactor: silently skip events for pointer permission mismatches instead of throwing
1 parent ac3c6c4 commit 04be4b0

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed

spec/vulnerabilities.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,10 @@ describe('(GHSA-5hmj-jcgp-6hff) Protected fields leak via LiveQuery afterEvent t
33533353
describe('(GHSA-fph2-r4qg-9576) LiveQuery bypasses CLP pointer permission enforcement', () => {
33543354
const { sleep } = require('../lib/TestUtils');
33553355

3356+
beforeEach(() => {
3357+
Parse.CoreManager.getLiveQueryController().setDefaultLiveQueryClient(null);
3358+
});
3359+
33563360
async function updateCLP(className, permissions) {
33573361
const response = await fetch(Parse.serverURL + '/schemas/' + className, {
33583362
method: 'PUT',

src/LiveQuery/ParseLiveQueryServer.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,16 @@ class ParseLiveQueryServer {
211211
const op = this._getCLPOperation(subscription.query);
212212
let res: any = {};
213213
try {
214-
await this._matchesCLP(
214+
const matchesCLP = await this._matchesCLP(
215215
classLevelPermissions,
216216
message.currentParseObject,
217217
client,
218218
requestId,
219219
op
220220
);
221+
if (matchesCLP === false) {
222+
return null;
223+
}
221224
const isMatched = await this._matchesACL(acl, client, requestId);
222225
if (!isMatched) {
223226
return null;
@@ -339,13 +342,16 @@ class ParseLiveQueryServer {
339342
}
340343
try {
341344
const op = this._getCLPOperation(subscription.query);
342-
await this._matchesCLP(
345+
const matchesCLP = await this._matchesCLP(
343346
classLevelPermissions,
344347
message.currentParseObject,
345348
client,
346349
requestId,
347350
op
348351
);
352+
if (matchesCLP === false) {
353+
return;
354+
}
349355
const [isOriginalMatched, isCurrentMatched] = await Promise.all([
350356
originalACLCheckingPromise,
351357
currentACLCheckingPromise,
@@ -673,7 +679,9 @@ class ParseLiveQueryServer {
673679
aclGroup,
674680
op
675681
);
676-
// Enforce pointer permissions that validatePermission defers
682+
// Enforce pointer permissions that validatePermission defers.
683+
// Returns false to silently skip the event (like ACL), rather than
684+
// throwing which would push errors to the client and log noise.
677685
if (!client.hasMasterKey && classLevelPermissions) {
678686
const permissionField =
679687
['get', 'find', 'count'].indexOf(op) > -1 ? 'readUserFields' : 'writeUserFields';
@@ -694,10 +702,7 @@ class ParseLiveQueryServer {
694702
!SchemaController.testPermissions(classLevelPermissions, aclGroup, op)
695703
) {
696704
if (!userId) {
697-
throw new Parse.Error(
698-
Parse.Error.OPERATION_FORBIDDEN,
699-
'Permission denied for this action.'
700-
);
705+
return false;
701706
}
702707
// Check if any pointer field points to the current user
703708
const hasAccess = pointerFields.some(field => {
@@ -729,10 +734,7 @@ class ParseLiveQueryServer {
729734
return false;
730735
});
731736
if (!hasAccess) {
732-
throw new Parse.Error(
733-
Parse.Error.OPERATION_FORBIDDEN,
734-
'Permission denied for this action.'
735-
);
737+
return false;
736738
}
737739
}
738740
}

0 commit comments

Comments
 (0)