Skip to content

Commit 1c84755

Browse files
committed
test(prisma-next): pin v3 read-path decrypt (decryptAll over v3 envelopes)
1 parent 7d7e990 commit 1c84755

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* v3 read-path pin. v3 reuses the EncryptedString envelope and the v2 decrypt
3+
* path verbatim: a v3 STORED payload `{v, i:{t,c}, c}` is exactly the protect-ffi
4+
* Encrypted shape, so it passes `isEncryptedPayload` / `ensureEncryptedEnvelope`
5+
* unchanged, and `decryptAll` / `EncryptedString#decrypt()` are version-agnostic.
6+
* No decrypt code change was needed (Round-3 §B1) — this test pins it as a
7+
* regression.
8+
*/
9+
import { isEncryptedPayload } from '@cipherstash/stack'
10+
import { describe, expect, it, vi } from 'vitest'
11+
import { createCipherstashStringV3Codec } from '../../src/execution/codec-v3'
12+
import { decryptAll } from '../../src/execution/decrypt-all'
13+
import { EncryptedString } from '../../src/execution/envelope-string'
14+
import { makeFakeSdk } from './helpers/fake-sdk'
15+
16+
const ctx = (table: string, name: string) => ({ column: { table, name } }) as never
17+
18+
describe('decryptAll over v3 envelopes', () => {
19+
it('groups v3 read-side envelopes by (table,column) and bulk-decrypts once per group', async () => {
20+
const bulkDecrypt = vi.fn(makeFakeSdk().bulkDecrypt)
21+
const sdk = makeFakeSdk({ bulkDecrypt })
22+
const codec = createCipherstashStringV3Codec(sdk)
23+
const env = await codec.decode('{"v":2,"i":{"t":"users","c":"email"},"c":"ct"}', ctx('users', 'email'))
24+
const rows = [{ email: env }]
25+
26+
await decryptAll(rows)
27+
28+
expect(bulkDecrypt).toHaveBeenCalledOnce()
29+
expect(await (rows[0]!.email as EncryptedString).decrypt()).toBe('plaintext')
30+
})
31+
32+
it('a v3 stored payload satisfies isEncryptedPayload (the v2 read-path gate)', () => {
33+
// A v3 STORED value is a full payload; the stack helper that the v2 decrypt
34+
// path uses must accept it unchanged.
35+
expect(isEncryptedPayload({ v: 2, i: { t: 'users', c: 'email' }, c: 'ct' })).toBe(true)
36+
// A v3 SEARCH term (no ciphertext `c`) is NOT a stored value and must never be
37+
// routed into decrypt; it correctly fails the stored-payload gate.
38+
expect(isEncryptedPayload({ v: 2, i: { t: 'users', c: 'email' }, hm: 'h' })).toBe(false)
39+
})
40+
})

0 commit comments

Comments
 (0)