Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d474876
test(protect): add searchableJson PostgreSQL database integration tests
tobyhede Feb 9, 2026
b5170f8
test(protect): address code review feedback for searchableJson integr…
tobyhede Feb 9, 2026
c1f59be
fix(protect): remove double-wrapping of encrypted data in searchableJ…
tobyhede Feb 9, 2026
067700d
test(protect): strengthen searchableJson PG integration test assertions
tobyhede Feb 9, 2026
8dbd088
fix(protect): avoid non-deterministic assertions in searchableJson pg…
tobyhede Feb 9, 2026
f5d1785
fix(protect): use jsonb_path_query for selector queries in searchable…
tobyhede Feb 9, 2026
2e31638
fix(protect): use batch encryptQuery form for composite-literal retur…
tobyhede Feb 9, 2026
97f1bf7
fix(protect): handle returnType in single-value encryptQuery
tobyhede Feb 9, 2026
0190b02
fix(protect): disable prepared statements for pooled PG connections
tobyhede Feb 9, 2026
f5dd914
refactor(protect): extract formatEncryptedResult helper for returnTyp…
tobyhede Feb 9, 2026
071f7a7
test(protect): add single-value encryptQuery returnType unit tests
tobyhede Feb 9, 2026
3d593ff
docs(protect): add inline comment explaining prepare: false
tobyhede Feb 9, 2026
f951279
test(protect): add single-value returnType unit tests for searchableJson
tobyhede Feb 9, 2026
2d9d397
test(protect): add escaped-format, LockContext, and concurrent PG int…
tobyhede Feb 9, 2026
f25bf53
fix(protect): fix flaky escaped-containment test comparing non-determ…
tobyhede Feb 9, 2026
78fb1e6
test(protect): add decrypt-and-validate to 5 searchableJson PG tests
tobyhede Feb 9, 2026
7d196c6
test(protect): add <@, jsonb_path_query_first, jsonb_path_exists PG i…
tobyhede Feb 10, 2026
78737ba
test(protect): add jsonb_array_elements + jsonb_array_length PG integ…
tobyhede Feb 10, 2026
7a0af24
test(protect): add array containment, round-trip, and top-level array…
tobyhede Feb 10, 2026
fde3fc1
refactor(protect): unify SQL alias and parameterization in @> and <@ …
tobyhede Feb 10, 2026
28bb0dc
fix(protect): remove unsupported multi-element containment and top-le…
tobyhede Feb 10, 2026
c81a442
test(protect): add containment operand and protocol matrix PG integra…
tobyhede Feb 10, 2026
0d7e05d
test(protect): add -> field access operator PG integration tests
tobyhede Feb 10, 2026
76ba943
test(protect): add = equality comparison discovery PG integration tests
tobyhede Feb 10, 2026
4becfed
test(protect): add protocol backfill Simple variants for PG integrati…
tobyhede Feb 10, 2026
c8efe4a
fix(protect): remove unsupported and fix reversed operand PG integrat…
tobyhede Feb 10, 2026
3a70f5c
test(protect): close remaining searchable JSON PG integration test gaps
tobyhede Feb 11, 2026
b5fe37f
test(protect): add missing decrypt + row count assertions to searchab…
tobyhede Feb 11, 2026
0755729
test(protect): refactor searchable-json-pg integration tests for comp…
tobyhede Feb 11, 2026
9361fd2
test(protect): fix assertion gaps and complete helper adoption in sea…
tobyhede Feb 11, 2026
e0aee15
fix(protect): convert 6 unsupported PG operations to expect errors in…
tobyhede Feb 11, 2026
de87e0b
fix(protect): use correct EQL function and selector for array operati…
tobyhede Feb 11, 2026
e87c345
fix(protect): use jsonb_path_query_first and SELECT-clause pattern fo…
tobyhede Feb 11, 2026
67bbc72
fix(protect): use [@] selector notation for array operation tests
tobyhede Feb 12, 2026
5e032e5
refactor: remove unused variable
auxesis Feb 13, 2026
0b7931e
refactor: removed unused import
auxesis Feb 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
echo "CS_CLIENT_ACCESS_KEY=${{ secrets.CS_CLIENT_ACCESS_KEY }}" >> ./packages/protect/.env
echo "SUPABASE_URL=${{ secrets.SUPABASE_URL }}" >> ./packages/protect/.env
echo "SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }}" >> ./packages/protect/.env
echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" >> ./packages/protect/.env
echo "CS_ZEROKMS_HOST=https://ap-southeast-2.aws.zerokms.cipherstashmanaged.net" >> ./packages/protect/.env
echo "CS_CTS_HOST=https://ap-southeast-2.aws.cts.cipherstashmanaged.net" >> ./packages/protect/.env

Expand Down
73 changes: 73 additions & 0 deletions packages/protect/__tests__/encrypt-query-searchable-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,79 @@ describe('searchableJson with returnType formatting', () => {
// Format: "(\"json\")" - outer quotes with escaped inner quotes
expect(data[0]).toMatch(/^"\(.*\)"$/)
}, 30000)

describe('single-value returnType', () => {
it('returns composite-literal for selector', async () => {
const result = await protectClient.encryptQuery('$.user.email', {
column: jsonbSchema.metadata,
table: jsonbSchema,
queryType: 'searchableJson',
returnType: 'composite-literal',
})

const data = unwrapResult(result)
expect(typeof data).toBe('string')
expect(data).toMatch(/^\(".*"\)$/)
}, 30000)

it('returns composite-literal for term', async () => {
const result = await protectClient.encryptQuery({ role: 'admin' }, {
column: jsonbSchema.metadata,
table: jsonbSchema,
queryType: 'searchableJson',
returnType: 'composite-literal',
})

const data = unwrapResult(result)
expect(typeof data).toBe('string')
expect(data).toMatch(/^\(".*"\)$/)
}, 30000)

it('returns escaped-composite-literal for selector', async () => {
const result = await protectClient.encryptQuery('$.user.email', {
column: jsonbSchema.metadata,
table: jsonbSchema,
queryType: 'searchableJson',
returnType: 'escaped-composite-literal',
})

const data = unwrapResult(result)
expect(typeof data).toBe('string')
expect(data as string).toMatch(/^"\(.*\)"$/)
// JSON.parse should yield the composite-literal format
const parsed = JSON.parse(data as string)
expect(parsed).toMatch(/^\(.*\)$/)
}, 30000)

it('returns escaped-composite-literal for term', async () => {
const result = await protectClient.encryptQuery({ role: 'admin' }, {
column: jsonbSchema.metadata,
table: jsonbSchema,
queryType: 'searchableJson',
returnType: 'escaped-composite-literal',
})

const data = unwrapResult(result)
expect(typeof data).toBe('string')
expect(data as string).toMatch(/^"\(.*\)"$/)
const parsed = JSON.parse(data as string)
expect(parsed).toMatch(/^\(.*\)$/)
}, 30000)

it('returns Encrypted object when returnType is omitted', async () => {
const result = await protectClient.encryptQuery('$.user.email', {
column: jsonbSchema.metadata,
table: jsonbSchema,
queryType: 'searchableJson',
})

const data = unwrapResult(result) as any
expect(typeof data).toBe('object')
expect(data).toHaveProperty('i')
expect(data.i).toHaveProperty('t')
expect(data.i).toHaveProperty('c')
}, 30000)
})
})

describe('searchableJson with LockContext', () => {
Expand Down
78 changes: 78 additions & 0 deletions packages/protect/__tests__/encrypt-query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,84 @@ describe('encryptQuery', () => {
}, 30000)
})

describe('single-value returnType formatting', () => {
it('returns Encrypted by default (no returnType)', async () => {
const result = await protectClient.encryptQuery('test@example.com', {
column: users.email,
table: users,
queryType: 'equality',
})

const data = unwrapResult(result)

expect(data).toMatchObject({
i: { t: 'users', c: 'email' },
v: 2,
})
expect(typeof data).toBe('object')
}, 30000)

it('returns composite-literal format when specified', async () => {
const result = await protectClient.encryptQuery('test@example.com', {
column: users.email,
table: users,
queryType: 'equality',
returnType: 'composite-literal',
})

const data = unwrapResult(result)

expect(typeof data).toBe('string')
// Format: ("json")
expect(data).toMatch(/^\(".*"\)$/)
}, 30000)

it('returns escaped-composite-literal format when specified', async () => {
const result = await protectClient.encryptQuery('test@example.com', {
column: users.email,
table: users,
queryType: 'equality',
returnType: 'escaped-composite-literal',
})

const data = unwrapResult(result)

expect(typeof data).toBe('string')
// Format: "(\"json\")" - outer quotes with escaped inner quotes
expect(data).toMatch(/^"\(.*\)"$/)
}, 30000)

it('returns eql format when explicitly specified', async () => {
const result = await protectClient.encryptQuery('test@example.com', {
column: users.email,
table: users,
queryType: 'equality',
returnType: 'eql',
})

const data = unwrapResult(result)

expect(data).toMatchObject({
i: { t: 'users', c: 'email' },
v: 2,
})
expect(typeof data).toBe('object')
}, 30000)

it('handles null value with composite-literal returnType', async () => {
const result = await protectClient.encryptQuery(null, {
column: users.email,
table: users,
queryType: 'equality',
returnType: 'composite-literal',
})

const data = unwrapResult(result)

expect(data).toBeNull()
}, 30000)
})

describe('LockContext support', () => {
it('single query with LockContext calls getLockContext', async () => {
const mockLockContext = createMockLockContext()
Expand Down
Loading