diff --git a/packages/runtime/src/enhancements/node/policy/policy-utils.ts b/packages/runtime/src/enhancements/node/policy/policy-utils.ts index 71985b30f..ec8a2cfc8 100644 --- a/packages/runtime/src/enhancements/node/policy/policy-utils.ts +++ b/packages/runtime/src/enhancements/node/policy/policy-utils.ts @@ -608,13 +608,13 @@ export class PolicyUtil extends QueryUtils { // to-one: direct-conditions/is/isNot // regular fields const mergedGuard = this.buildReadGuardForFields(db, model, args.where, {}); - this.mergeWhereClause(args.where, mergedGuard); + args.where = this.mergeWhereClause(args.where, mergedGuard); } if (args.where) { if (injected.where && Object.keys(injected.where).length > 0) { // merge injected guard with the user-provided where clause - this.mergeWhereClause(args.where, injected.where); + args.where = this.mergeWhereClause(args.where, injected.where); } } else if (injected.where) { // no user-provided where clause, use the injected one @@ -630,7 +630,7 @@ export class PolicyUtil extends QueryUtils { if (!args.where) { args.where = this.and(...hoistedConditions); } else { - this.mergeWhereClause(args.where, this.and(...hoistedConditions)); + args.where = this.mergeWhereClause(args.where, this.and(...hoistedConditions)); } } @@ -1552,7 +1552,11 @@ export class PolicyUtil extends QueryUtils { } if (this.isTrue(extra)) { - return; + return where; + } + + if (this.isFalse(extra)) { + return this.makeFalse(); } // instead of simply wrapping with AND, we preserve the structure @@ -1565,10 +1569,10 @@ export class PolicyUtil extends QueryUtils { const combined: any = this.and(...conditions); // make sure the merging always goes under AND - where.AND = combined.AND ?? combined; + return { ...where, AND: combined.AND ?? combined }; } else { // insert an AND clause - where.AND = [extra]; + return { ...where, AND: [extra] }; } } diff --git a/tests/integration/tests/enhancements/with-password/with-password.test.ts b/tests/integration/tests/enhancements/with-password/with-password.test.ts index 9755cd6ef..b2fd89a65 100644 --- a/tests/integration/tests/enhancements/with-password/with-password.test.ts +++ b/tests/integration/tests/enhancements/with-password/with-password.test.ts @@ -93,4 +93,36 @@ describe('Password test', () => { }) ).toBeRejectedByPolicy(['must start with "abc" at "password"']); }); + + it('prevents query enumeration if password is not readable', async () => { + const { prisma, enhance } = await loadSchema( + ` + model User { + id Int @id @default(autoincrement()) + email String @unique + password String @password @omit @deny('read', true) + @@allow('all', true) + } + ` + ); + + const db = enhance(); + + const user = await db.user.create({ + data: { + email: 'alice@abc.com', + password: '123456', + }, + }); + expect(user.password).toBeUndefined(); + + const u = await prisma.user.findFirstOrThrow(); + + await expect(db.user.findFirst({ where: { password: u.password } })).toResolveNull(); + await expect( + db.user.findFirst({ + where: { password: { startsWith: u?.password.substring(0, 3) } }, + }) + ).toResolveNull(); + }); });