Skip to content

Commit 2eecae5

Browse files
authored
fix(encryption): decrypt fields in nested read results (#1934)
1 parent b477ad8 commit 2eecae5

File tree

2 files changed

+64
-17
lines changed

2 files changed

+64
-17
lines changed

packages/runtime/src/enhancements/node/encryption.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,21 +138,29 @@ class EncryptedHandler extends DefaultPrismaProxyHandler {
138138
const realModel = this.queryUtils.getDelegateConcreteModel(model, entityData);
139139

140140
for (const field of getModelFields(entityData)) {
141-
const fieldInfo = await resolveField(this.options.modelMeta, realModel, field);
141+
// Don't decrypt null, undefined or empty string values
142+
if (!entityData[field]) continue;
142143

144+
const fieldInfo = await resolveField(this.options.modelMeta, realModel, field);
143145
if (!fieldInfo) {
144146
continue;
145147
}
146148

147-
const shouldDecrypt = fieldInfo.attributes?.find((attr) => attr.name === '@encrypted');
148-
if (shouldDecrypt) {
149-
// Don't decrypt null, undefined or empty string values
150-
if (!entityData[field]) continue;
151-
152-
try {
153-
entityData[field] = await this.decrypt(fieldInfo, entityData[field]);
154-
} catch (error) {
155-
this.logger.warn(`Decryption failed, keeping original value: ${error}`);
149+
if (fieldInfo.isDataModel) {
150+
const items =
151+
fieldInfo.isArray && Array.isArray(entityData[field]) ? entityData[field] : [entityData[field]];
152+
for (const item of items) {
153+
// recurse
154+
await this.doPostProcess(item, fieldInfo.type);
155+
}
156+
} else {
157+
const shouldDecrypt = fieldInfo.attributes?.find((attr) => attr.name === '@encrypted');
158+
if (shouldDecrypt) {
159+
try {
160+
entityData[field] = await this.decrypt(fieldInfo, entityData[field]);
161+
} catch (error) {
162+
this.logger.warn(`Decryption failed, keeping original value: ${error}`);
163+
}
156164
}
157165
}
158166
}

tests/integration/tests/enhancements/with-encrypted/with-encrypted.test.ts

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ describe('Encrypted test', () => {
2020
model User {
2121
id String @id @default(cuid())
2222
encrypted_value String @encrypted()
23-
24-
@@allow('all', true)
2523
}`,
2624
{
2725
enhancements: ['encryption'],
@@ -62,15 +60,59 @@ describe('Encrypted test', () => {
6260
expect(rawRead.encrypted_value).not.toBe('abc123');
6361
});
6462

63+
it('Decrypts nested fields', async () => {
64+
const { enhance, prisma } = await loadSchema(
65+
`
66+
model User {
67+
id String @id @default(cuid())
68+
posts Post[]
69+
}
70+
71+
model Post {
72+
id String @id @default(cuid())
73+
title String @encrypted()
74+
author User @relation(fields: [authorId], references: [id])
75+
authorId String
76+
}
77+
`,
78+
{
79+
enhancements: ['encryption'],
80+
enhanceOptions: {
81+
encryption: { encryptionKey },
82+
},
83+
}
84+
);
85+
86+
const db = enhance();
87+
88+
const create = await db.user.create({
89+
data: {
90+
id: '1',
91+
posts: { create: { title: 'Post1' } },
92+
},
93+
include: { posts: true },
94+
});
95+
expect(create.posts[0].title).toBe('Post1');
96+
97+
const read = await db.user.findUnique({
98+
where: {
99+
id: '1',
100+
},
101+
include: { posts: true },
102+
});
103+
expect(read.posts[0].title).toBe('Post1');
104+
105+
const rawRead = await prisma.user.findUnique({ where: { id: '1' }, include: { posts: true } });
106+
expect(rawRead.posts[0].title).not.toBe('Post1');
107+
});
108+
65109
it('Multi-field encryption test', async () => {
66110
const { enhance } = await loadSchema(
67111
`
68112
model User {
69113
id String @id @default(cuid())
70114
x1 String @encrypted()
71115
x2 String @encrypted()
72-
73-
@@allow('all', true)
74116
}`,
75117
{
76118
enhancements: ['encryption'],
@@ -105,8 +147,6 @@ describe('Encrypted test', () => {
105147
model User {
106148
id String @id @default(cuid())
107149
encrypted_value String @encrypted()
108-
109-
@@allow('all', true)
110150
}`);
111151

112152
const sudoDb = enhance(undefined, { kinds: [] });
@@ -203,7 +243,6 @@ describe('Encrypted test', () => {
203243
model User {
204244
id String @id @default(cuid())
205245
encrypted_value String @encrypted() @length(0, 6)
206-
207246
@@allow('all', true)
208247
}`,
209248
{

0 commit comments

Comments
 (0)