Skip to content

Commit 3da7ffb

Browse files
committed
feat: Enhance beforeFind security tests for object visibility and access control
1 parent 840a3e4 commit 3da7ffb

File tree

1 file changed

+40
-8
lines changed

1 file changed

+40
-8
lines changed

spec/CloudCode.spec.js

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,18 @@ describe('Cloud Code', () => {
282282
});
283283

284284
describe('beforeFind security with returned objects', () => {
285-
it('should not expose objects not readable by current user', async () => {
286-
const userA = new Parse.User();
287-
userA.setUsername('userA');
285+
let userA;
286+
let userB;
287+
let secret;
288+
289+
beforeEach(async () => {
290+
userA = new Parse.User();
291+
userA.setUsername('userA_' + Date.now());
288292
userA.setPassword('passA');
289293
await userA.signUp();
290294

291-
const userB = new Parse.User();
292-
userB.setUsername('userB');
295+
userB = new Parse.User();
296+
userB.setUsername('userB_' + Date.now());
293297
userB.setPassword('passB');
294298
await userB.signUp();
295299

@@ -300,7 +304,7 @@ describe('Cloud Code', () => {
300304
acl.setReadAccess(userB.id, true);
301305
acl.setWriteAccess(userB.id, true);
302306

303-
const secret = new Parse.Object('SecretDoc');
307+
secret = new Parse.Object('SecretDoc');
304308
secret.set('title', 'top');
305309
secret.set('content', 'classified');
306310
secret.setACL(acl);
@@ -309,13 +313,37 @@ describe('Cloud Code', () => {
309313
Parse.Cloud.beforeFind('SecretDoc', () => {
310314
return [secret];
311315
});
316+
});
312317

313-
// Query as userA should NOT see the secret
318+
it('should not expose objects not readable by current user', async () => {
314319
const q = new Parse.Query('SecretDoc');
315320
const results = await q.find({ sessionToken: userA.getSessionToken() });
316321
expect(results.length).toBe(0);
317322
});
318323

324+
it('should allow authorized user to see their objects', async () => {
325+
const q = new Parse.Query('SecretDoc');
326+
const results = await q.find({ sessionToken: userB.getSessionToken() });
327+
expect(results.length).toBe(1);
328+
expect(results[0].id).toBe(secret.id);
329+
expect(results[0].get('title')).toBe('top');
330+
expect(results[0].get('content')).toBe('classified');
331+
});
332+
333+
it('should return OBJECT_NOT_FOUND on get() for unauthorized user', async () => {
334+
const q = new Parse.Query('SecretDoc');
335+
await expectAsync(
336+
q.get(secret.id, { sessionToken: userA.getSessionToken() })
337+
).toBeRejectedWith(jasmine.objectContaining({ code: Parse.Error.OBJECT_NOT_FOUND }));
338+
});
339+
340+
it('should allow master key to bypass ACL filtering when returning objects', async () => {
341+
const q = new Parse.Query('SecretDoc');
342+
const results = await q.find({ useMasterKey: true });
343+
expect(results.length).toBe(1);
344+
expect(results[0].id).toBe(secret.id);
345+
});
346+
319347
it('should apply protectedFields masking after re-filtering', async () => {
320348
// Configure protectedFields for SecretMask: mask `secretField` for everyone
321349
const protectedFields = { SecretMask: { '*': ['secretField'] } };
@@ -460,11 +488,15 @@ describe('Cloud Code', () => {
460488
className,
461489
[mockObject],
462490
testConfig,
463-
{ limit: 5, skip: 0 },
491+
{ limit: 5, skip: 1 },
464492
{}
465493
);
466494

467495
expect(receivedQuery).toBeInstanceOf(Parse.Query);
496+
const qJSON = receivedQuery.toJSON();
497+
expect(qJSON.limit).toBe(5);
498+
expect(qJSON.skip).toBe(1);
499+
expect(qJSON.where).toEqual({});
468500
expect(result).toBeDefined();
469501
});
470502

0 commit comments

Comments
 (0)