@cipherstash/stack 0.16.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +225 -0
- package/README.md +2 -2
- package/dist/{chunk-CY5B4LIF.js → chunk-36AA7IBJ.js} +303 -204
- package/dist/chunk-36AA7IBJ.js.map +1 -0
- package/dist/{chunk-MD6742R6.js → chunk-4AVL4VZD.js} +1 -1
- package/dist/chunk-4AVL4VZD.js.map +1 -0
- package/dist/{chunk-4RNBI3UH.js → chunk-BNO32ZMV.js} +1 -1
- package/dist/chunk-BNO32ZMV.js.map +1 -0
- package/dist/{chunk-6FD7OBGM.js → chunk-HQANMV7R.js} +3 -3
- package/dist/chunk-HQANMV7R.js.map +1 -0
- package/dist/chunk-IADZCZEA.js +23 -0
- package/dist/chunk-IADZCZEA.js.map +1 -0
- package/dist/chunk-IBSK6P33.js +209 -0
- package/dist/chunk-IBSK6P33.js.map +1 -0
- package/dist/chunk-JSG2AMDI.js +17 -0
- package/dist/chunk-JSG2AMDI.js.map +1 -0
- package/dist/chunk-MP3SSDNN.js +4439 -0
- package/dist/chunk-MP3SSDNN.js.map +1 -0
- package/dist/chunk-OFQ555AX.js +191 -0
- package/dist/chunk-OFQ555AX.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-U66S7VIF.js +435 -0
- package/dist/chunk-U66S7VIF.js.map +1 -0
- package/dist/{client-C_7TF8EL.d.cts → client-DSGHBN-g.d.cts} +209 -72
- package/dist/{client-BLbA6aFD.d.ts → client-DfCrlHXh.d.ts} +209 -72
- package/dist/client.cjs +4103 -43
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +5 -5
- package/dist/client.d.ts +5 -5
- package/dist/client.js +3 -1
- package/dist/drizzle/index.cjs +4139 -77
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +33 -33
- package/dist/drizzle/index.d.ts +33 -33
- package/dist/drizzle/index.js +39 -33
- package/dist/drizzle/index.js.map +1 -1
- package/dist/dynamodb/index.cjs +31 -14
- package/dist/dynamodb/index.cjs.map +1 -1
- package/dist/dynamodb/index.d.cts +12 -12
- package/dist/dynamodb/index.d.ts +12 -12
- package/dist/dynamodb/index.js +16 -12
- package/dist/dynamodb/index.js.map +1 -1
- package/dist/encryption/index.cjs +4611 -257
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +3 -3
- package/dist/encryption/index.d.ts +3 -3
- package/dist/encryption/index.js +35 -7
- package/dist/encryption/v3.cjs +7791 -0
- package/dist/encryption/v3.cjs.map +1 -0
- package/dist/encryption/v3.d.cts +100 -0
- package/dist/encryption/v3.d.ts +100 -0
- package/dist/encryption/v3.js +161 -0
- package/dist/encryption/v3.js.map +1 -0
- package/dist/eql/v3/index.cjs +612 -0
- package/dist/eql/v3/index.cjs.map +1 -0
- package/dist/eql/v3/index.d.cts +71 -0
- package/dist/eql/v3/index.d.ts +71 -0
- package/dist/eql/v3/index.js +89 -0
- package/dist/eql/v3/index.js.map +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/identity/index.cjs +55 -35
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +54 -30
- package/dist/identity/index.d.ts +54 -30
- package/dist/identity/index.js +9 -177
- package/dist/identity/index.js.map +1 -1
- package/dist/index.cjs +4625 -273
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -7
- package/dist/index.d.ts +18 -7
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/schema/index.cjs +4108 -44
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +3 -1
- package/dist/supabase/index.cjs +4500 -75
- package/dist/supabase/index.cjs.map +1 -1
- package/dist/supabase/index.d.cts +113 -34
- package/dist/supabase/index.d.ts +113 -34
- package/dist/supabase/index.js +350 -32
- package/dist/supabase/index.js.map +1 -1
- package/dist/table-CIH7jZ2h.d.ts +603 -0
- package/dist/table-DihEAlxG.d.cts +603 -0
- package/dist/{types-public-Dv-r6Ik0.d.ts → types-public-CpS5KjwX.d.cts} +209 -36
- package/dist/{types-public-Dv-r6Ik0.d.cts → types-public-CpS5KjwX.d.ts} +209 -36
- package/dist/types-public.cjs.map +1 -1
- package/dist/types-public.d.cts +2 -2
- package/dist/types-public.d.ts +2 -2
- package/dist/types-public.js +2 -1
- package/dist/wasm-inline.d.ts +1269 -0
- package/dist/wasm-inline.js +4594 -0
- package/dist/wasm-inline.js.map +1 -0
- package/package.json +63 -13
- package/dist/chunk-4RNBI3UH.js.map +0 -1
- package/dist/chunk-6FD7OBGM.js.map +0 -1
- package/dist/chunk-CY5B4LIF.js.map +0 -1
- package/dist/chunk-JDZ2QUUF.js +0 -381
- package/dist/chunk-JDZ2QUUF.js.map +0 -1
- package/dist/chunk-MD6742R6.js.map +0 -1
|
@@ -1,26 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveLockContext
|
|
3
|
+
} from "./chunk-OFQ555AX.js";
|
|
1
4
|
import {
|
|
2
5
|
formatEncryptedResult,
|
|
3
6
|
isEncryptedPayload,
|
|
4
7
|
toFfiKeysetIdentifier
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BNO32ZMV.js";
|
|
6
9
|
import {
|
|
7
10
|
buildEncryptConfig,
|
|
8
11
|
encryptConfigSchema
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MP3SSDNN.js";
|
|
10
13
|
import {
|
|
11
14
|
queryTypeToFfi,
|
|
12
15
|
queryTypeToQueryOp
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4AVL4VZD.js";
|
|
17
|
+
import {
|
|
18
|
+
withResult
|
|
19
|
+
} from "./chunk-JSG2AMDI.js";
|
|
14
20
|
import {
|
|
15
21
|
createRequestLogger,
|
|
16
22
|
logger
|
|
17
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-HQANMV7R.js";
|
|
18
24
|
import {
|
|
19
25
|
EncryptionErrorTypes
|
|
20
26
|
} from "./chunk-LBMC4D6D.js";
|
|
21
27
|
|
|
22
28
|
// src/encryption/index.ts
|
|
23
|
-
import { withResult as withResult11 } from "@byteslice/result";
|
|
24
29
|
import { newClient } from "@cipherstash/protect-ffi";
|
|
25
30
|
|
|
26
31
|
// ../../node_modules/.pnpm/uuid@14.0.0/node_modules/uuid/dist-node/regex.js
|
|
@@ -37,6 +42,11 @@ function isScalarQueryTermArray(value) {
|
|
|
37
42
|
return Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && value[0] !== null && "column" in value[0] && "table" in value[0];
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
// src/encryption/operations/batch-encrypt-query.ts
|
|
46
|
+
import {
|
|
47
|
+
encryptQueryBulk as ffiEncryptQueryBulk
|
|
48
|
+
} from "@cipherstash/protect-ffi";
|
|
49
|
+
|
|
40
50
|
// src/encryption/helpers/error-code.ts
|
|
41
51
|
import {
|
|
42
52
|
ProtectError as FfiProtectError
|
|
@@ -45,12 +55,6 @@ function getErrorCode(error) {
|
|
|
45
55
|
return error instanceof FfiProtectError ? error.code : void 0;
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
// src/encryption/operations/batch-encrypt-query.ts
|
|
49
|
-
import { withResult } from "@byteslice/result";
|
|
50
|
-
import {
|
|
51
|
-
encryptQueryBulk as ffiEncryptQueryBulk
|
|
52
|
-
} from "@cipherstash/protect-ffi";
|
|
53
|
-
|
|
54
58
|
// src/encryption/helpers/infer-index-type.ts
|
|
55
59
|
function inferIndexType(column) {
|
|
56
60
|
const config = column.build();
|
|
@@ -70,7 +74,7 @@ function inferQueryOpFromPlaintext(plaintext) {
|
|
|
70
74
|
if (typeof plaintext === "string") {
|
|
71
75
|
return "ste_vec_selector";
|
|
72
76
|
}
|
|
73
|
-
if (typeof plaintext === "object" || typeof plaintext === "number" || typeof plaintext === "boolean"
|
|
77
|
+
if (typeof plaintext === "object" || typeof plaintext === "number" || typeof plaintext === "boolean") {
|
|
74
78
|
return "ste_vec_term";
|
|
75
79
|
}
|
|
76
80
|
return "ste_vec_term";
|
|
@@ -90,9 +94,18 @@ function validateIndexType(column, indexType) {
|
|
|
90
94
|
);
|
|
91
95
|
}
|
|
92
96
|
}
|
|
97
|
+
function resolvesEqualityViaOre(column) {
|
|
98
|
+
if (!("getQueryCapabilities" in column)) return false;
|
|
99
|
+
if (!column.getQueryCapabilities().equality) return false;
|
|
100
|
+
const indexes = column.build().indexes ?? {};
|
|
101
|
+
return !indexes.unique && !!indexes.ore;
|
|
102
|
+
}
|
|
93
103
|
function resolveIndexType(column, queryType, plaintext) {
|
|
94
104
|
const indexType = queryType ? queryTypeToFfi[queryType] : inferIndexType(column);
|
|
95
105
|
if (queryType) {
|
|
106
|
+
if (queryType === "equality" && resolvesEqualityViaOre(column)) {
|
|
107
|
+
return { indexType: "ore" };
|
|
108
|
+
}
|
|
96
109
|
validateIndexType(column, indexType);
|
|
97
110
|
if (queryType === "searchableJson") {
|
|
98
111
|
if (plaintext === void 0 || plaintext === null) {
|
|
@@ -176,6 +189,15 @@ var EncryptionOperation = class {
|
|
|
176
189
|
};
|
|
177
190
|
|
|
178
191
|
// src/encryption/operations/batch-encrypt-query.ts
|
|
192
|
+
function filterNullTerms(terms) {
|
|
193
|
+
const nonNullTerms = [];
|
|
194
|
+
terms.forEach((term, index) => {
|
|
195
|
+
if (term.value !== null && term.value !== void 0) {
|
|
196
|
+
nonNullTerms.push({ term, originalIndex: index });
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return { nonNullTerms };
|
|
200
|
+
}
|
|
179
201
|
function buildQueryPayload(term, lockContext) {
|
|
180
202
|
assertValidNumericValue(term.value);
|
|
181
203
|
const { indexType, queryOp } = resolveIndexType(
|
|
@@ -196,10 +218,15 @@ function buildQueryPayload(term, lockContext) {
|
|
|
196
218
|
}
|
|
197
219
|
return payload;
|
|
198
220
|
}
|
|
199
|
-
function assembleResults(
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
function assembleResults(totalLength, encryptedValues, nonNullTerms) {
|
|
222
|
+
const results = new Array(totalLength).fill(null);
|
|
223
|
+
nonNullTerms.forEach(({ term, originalIndex }, i) => {
|
|
224
|
+
results[originalIndex] = formatEncryptedResult(
|
|
225
|
+
encryptedValues[i],
|
|
226
|
+
term.returnType
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
return results;
|
|
203
230
|
}
|
|
204
231
|
var BatchEncryptQueryOperation = class extends EncryptionOperation {
|
|
205
232
|
constructor(client, terms) {
|
|
@@ -207,6 +234,8 @@ var BatchEncryptQueryOperation = class extends EncryptionOperation {
|
|
|
207
234
|
this.client = client;
|
|
208
235
|
this.terms = terms;
|
|
209
236
|
}
|
|
237
|
+
client;
|
|
238
|
+
terms;
|
|
210
239
|
withLockContext(lockContext) {
|
|
211
240
|
return new BatchEncryptQueryOperationWithLockContext(
|
|
212
241
|
this.client,
|
|
@@ -226,18 +255,23 @@ var BatchEncryptQueryOperation = class extends EncryptionOperation {
|
|
|
226
255
|
log.emit();
|
|
227
256
|
return { data: [] };
|
|
228
257
|
}
|
|
258
|
+
const { nonNullTerms } = filterNullTerms(this.terms);
|
|
259
|
+
if (nonNullTerms.length === 0) {
|
|
260
|
+
log.emit();
|
|
261
|
+
return { data: this.terms.map(() => null) };
|
|
262
|
+
}
|
|
229
263
|
const result = await withResult(
|
|
230
264
|
async () => {
|
|
231
265
|
if (!this.client) throw noClientError();
|
|
232
266
|
const { metadata } = this.getAuditData();
|
|
233
|
-
const queries =
|
|
234
|
-
(term) => buildQueryPayload(term)
|
|
267
|
+
const queries = nonNullTerms.map(
|
|
268
|
+
({ term }) => buildQueryPayload(term)
|
|
235
269
|
);
|
|
236
270
|
const encrypted = await ffiEncryptQueryBulk(this.client, {
|
|
237
271
|
queries,
|
|
238
272
|
unverifiedContext: metadata
|
|
239
273
|
});
|
|
240
|
-
return assembleResults(this.terms, encrypted);
|
|
274
|
+
return assembleResults(this.terms.length, encrypted, nonNullTerms);
|
|
241
275
|
},
|
|
242
276
|
(error) => {
|
|
243
277
|
log.set({ errorCode: getErrorCode(error) ?? "unknown" });
|
|
@@ -260,6 +294,9 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
|
|
|
260
294
|
this.lockContext = lockContext;
|
|
261
295
|
this.auditMetadata = auditMetadata;
|
|
262
296
|
}
|
|
297
|
+
client;
|
|
298
|
+
terms;
|
|
299
|
+
lockContext;
|
|
263
300
|
async execute() {
|
|
264
301
|
const log = createRequestLogger();
|
|
265
302
|
log.set({
|
|
@@ -271,25 +308,24 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
|
|
|
271
308
|
log.emit();
|
|
272
309
|
return { data: [] };
|
|
273
310
|
}
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
311
|
+
const { nonNullTerms } = filterNullTerms(this.terms);
|
|
312
|
+
if (nonNullTerms.length === 0) {
|
|
276
313
|
log.emit();
|
|
277
|
-
return {
|
|
314
|
+
return { data: this.terms.map(() => null) };
|
|
278
315
|
}
|
|
279
|
-
const { ctsToken, context } = lockContextResult.data;
|
|
280
316
|
const result = await withResult(
|
|
281
317
|
async () => {
|
|
282
318
|
if (!this.client) throw noClientError();
|
|
319
|
+
const context = resolveLockContext(this.lockContext);
|
|
283
320
|
const { metadata } = this.getAuditData();
|
|
284
|
-
const queries =
|
|
285
|
-
(term) => buildQueryPayload(term, context)
|
|
321
|
+
const queries = nonNullTerms.map(
|
|
322
|
+
({ term }) => buildQueryPayload(term, context)
|
|
286
323
|
);
|
|
287
324
|
const encrypted = await ffiEncryptQueryBulk(this.client, {
|
|
288
325
|
queries,
|
|
289
|
-
serviceToken: ctsToken,
|
|
290
326
|
unverifiedContext: metadata
|
|
291
327
|
});
|
|
292
|
-
return assembleResults(this.terms, encrypted);
|
|
328
|
+
return assembleResults(this.terms.length, encrypted, nonNullTerms);
|
|
293
329
|
},
|
|
294
330
|
(error) => {
|
|
295
331
|
log.set({ errorCode: getErrorCode(error) ?? "unknown" });
|
|
@@ -306,30 +342,34 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
|
|
|
306
342
|
};
|
|
307
343
|
|
|
308
344
|
// src/encryption/operations/bulk-decrypt.ts
|
|
309
|
-
import { withResult as withResult2 } from "@byteslice/result";
|
|
310
345
|
import {
|
|
311
346
|
decryptBulkFallible
|
|
312
347
|
} from "@cipherstash/protect-ffi";
|
|
313
348
|
var createDecryptPayloads = (encryptedPayloads, lockContext) => {
|
|
314
|
-
return encryptedPayloads.map(({ id, data }) => ({
|
|
349
|
+
return encryptedPayloads.filter(({ data }) => data !== null).map(({ id, data }) => ({
|
|
315
350
|
id,
|
|
316
351
|
ciphertext: data,
|
|
317
352
|
...lockContext && { lockContext }
|
|
318
353
|
}));
|
|
319
354
|
};
|
|
355
|
+
var createNullResult = (encryptedPayloads) => encryptedPayloads.map(({ id }) => ({ id, data: null }));
|
|
320
356
|
var mapDecryptedDataToResult = (encryptedPayloads, decryptedData) => {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
357
|
+
const result = new Array(encryptedPayloads.length);
|
|
358
|
+
let decryptedIndex = 0;
|
|
359
|
+
for (let i = 0; i < encryptedPayloads.length; i++) {
|
|
360
|
+
if (encryptedPayloads[i].data === null) {
|
|
361
|
+
result[i] = { id: encryptedPayloads[i].id, data: null };
|
|
362
|
+
} else {
|
|
363
|
+
const decryptResult = decryptedData[decryptedIndex];
|
|
364
|
+
if ("error" in decryptResult) {
|
|
365
|
+
result[i] = { id: encryptedPayloads[i].id, error: decryptResult.error };
|
|
366
|
+
} else {
|
|
367
|
+
result[i] = { id: encryptedPayloads[i].id, data: decryptResult.data };
|
|
368
|
+
}
|
|
369
|
+
decryptedIndex++;
|
|
327
370
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
data: decryptResult.data
|
|
331
|
-
};
|
|
332
|
-
});
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
333
373
|
};
|
|
334
374
|
var BulkDecryptOperation = class extends EncryptionOperation {
|
|
335
375
|
client;
|
|
@@ -349,15 +389,18 @@ var BulkDecryptOperation = class extends EncryptionOperation {
|
|
|
349
389
|
count: this.encryptedPayloads?.length ?? 0,
|
|
350
390
|
lockContext: false
|
|
351
391
|
});
|
|
352
|
-
const result = await
|
|
392
|
+
const result = await withResult(
|
|
353
393
|
async () => {
|
|
354
394
|
if (!this.client) throw noClientError();
|
|
355
395
|
if (!this.encryptedPayloads || this.encryptedPayloads.length === 0)
|
|
356
396
|
return [];
|
|
357
|
-
const
|
|
397
|
+
const nonNullPayloads = createDecryptPayloads(this.encryptedPayloads);
|
|
398
|
+
if (nonNullPayloads.length === 0) {
|
|
399
|
+
return createNullResult(this.encryptedPayloads);
|
|
400
|
+
}
|
|
358
401
|
const { metadata } = this.getAuditData();
|
|
359
402
|
const decryptedData = await decryptBulkFallible(this.client, {
|
|
360
|
-
ciphertexts:
|
|
403
|
+
ciphertexts: nonNullPayloads,
|
|
361
404
|
unverifiedContext: metadata
|
|
362
405
|
});
|
|
363
406
|
return mapDecryptedDataToResult(this.encryptedPayloads, decryptedData);
|
|
@@ -401,22 +444,21 @@ var BulkDecryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
401
444
|
count: encryptedPayloads?.length ?? 0,
|
|
402
445
|
lockContext: true
|
|
403
446
|
});
|
|
404
|
-
const result = await
|
|
447
|
+
const result = await withResult(
|
|
405
448
|
async () => {
|
|
406
449
|
if (!client) throw noClientError();
|
|
407
450
|
if (!encryptedPayloads || encryptedPayloads.length === 0) return [];
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
411
|
-
}
|
|
412
|
-
const payloads = createDecryptPayloads(
|
|
451
|
+
const lockContext = resolveLockContext(this.lockContext);
|
|
452
|
+
const nonNullPayloads = createDecryptPayloads(
|
|
413
453
|
encryptedPayloads,
|
|
414
|
-
|
|
454
|
+
lockContext
|
|
415
455
|
);
|
|
456
|
+
if (nonNullPayloads.length === 0) {
|
|
457
|
+
return createNullResult(encryptedPayloads);
|
|
458
|
+
}
|
|
416
459
|
const { metadata } = this.getAuditData();
|
|
417
460
|
const decryptedData = await decryptBulkFallible(client, {
|
|
418
|
-
ciphertexts:
|
|
419
|
-
serviceToken: context.data.ctsToken,
|
|
461
|
+
ciphertexts: nonNullPayloads,
|
|
420
462
|
unverifiedContext: metadata
|
|
421
463
|
});
|
|
422
464
|
return mapDecryptedDataToResult(encryptedPayloads, decryptedData);
|
|
@@ -435,9 +477,6 @@ var BulkDecryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
435
477
|
}
|
|
436
478
|
};
|
|
437
479
|
|
|
438
|
-
// src/encryption/operations/bulk-decrypt-models.ts
|
|
439
|
-
import { withResult as withResult3 } from "@byteslice/result";
|
|
440
|
-
|
|
441
480
|
// src/encryption/helpers/model-helpers.ts
|
|
442
481
|
import {
|
|
443
482
|
decryptBulk,
|
|
@@ -519,6 +558,17 @@ function prepareFieldsForDecryption(model) {
|
|
|
519
558
|
processNestedFields(model);
|
|
520
559
|
return { otherFields, operationFields, keyMap, nullFields };
|
|
521
560
|
}
|
|
561
|
+
function resolveEncryptColumnMap(table) {
|
|
562
|
+
const keyMap = table.buildColumnKeyMap?.();
|
|
563
|
+
if (keyMap) {
|
|
564
|
+
return {
|
|
565
|
+
columnPaths: Object.keys(keyMap),
|
|
566
|
+
toColumnName: (path) => keyMap[path] ?? path
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
const columnPaths = Object.keys(table.build().columns);
|
|
570
|
+
return { columnPaths, toColumnName: (path) => path };
|
|
571
|
+
}
|
|
522
572
|
function prepareFieldsForEncryption(model, table) {
|
|
523
573
|
const otherFields = { ...model };
|
|
524
574
|
const operationFields = {};
|
|
@@ -554,7 +604,7 @@ function prepareFieldsForEncryption(model, table) {
|
|
|
554
604
|
}
|
|
555
605
|
}
|
|
556
606
|
};
|
|
557
|
-
const columnPaths =
|
|
607
|
+
const { columnPaths } = resolveEncryptColumnMap(table);
|
|
558
608
|
processNestedFields(model, "", columnPaths);
|
|
559
609
|
return { otherFields, operationFields, keyMap, nullFields };
|
|
560
610
|
}
|
|
@@ -593,12 +643,13 @@ async function encryptModelFields(model, table, client, auditData) {
|
|
|
593
643
|
throw new Error("Client not initialized");
|
|
594
644
|
}
|
|
595
645
|
const { otherFields, operationFields, keyMap, nullFields } = prepareFieldsForEncryption(model, table);
|
|
646
|
+
const { toColumnName } = resolveEncryptColumnMap(table);
|
|
596
647
|
const bulkEncryptPayload = Object.entries(operationFields).map(
|
|
597
648
|
([key, value]) => ({
|
|
598
649
|
id: key,
|
|
599
650
|
plaintext: value,
|
|
600
651
|
table: table.tableName,
|
|
601
|
-
column: key
|
|
652
|
+
column: toColumnName(key)
|
|
602
653
|
})
|
|
603
654
|
);
|
|
604
655
|
const encryptedData = await handleSingleModelBulkOperation(
|
|
@@ -632,14 +683,13 @@ async function decryptModelFieldsWithLockContext(model, client, lockContext, aud
|
|
|
632
683
|
([key, value]) => ({
|
|
633
684
|
id: key,
|
|
634
685
|
ciphertext: value,
|
|
635
|
-
lockContext
|
|
686
|
+
lockContext
|
|
636
687
|
})
|
|
637
688
|
);
|
|
638
689
|
const decryptedFields = await handleSingleModelBulkOperation(
|
|
639
690
|
bulkDecryptPayload,
|
|
640
691
|
(items) => decryptBulk(client, {
|
|
641
692
|
ciphertexts: items,
|
|
642
|
-
serviceToken: lockContext.ctsToken,
|
|
643
693
|
unverifiedContext: auditData?.metadata
|
|
644
694
|
}),
|
|
645
695
|
keyMap
|
|
@@ -663,20 +713,20 @@ async function encryptModelFieldsWithLockContext(model, table, client, lockConte
|
|
|
663
713
|
throw new Error("Lock context is not initialized");
|
|
664
714
|
}
|
|
665
715
|
const { otherFields, operationFields, keyMap, nullFields } = prepareFieldsForEncryption(model, table);
|
|
716
|
+
const { toColumnName } = resolveEncryptColumnMap(table);
|
|
666
717
|
const bulkEncryptPayload = Object.entries(operationFields).map(
|
|
667
718
|
([key, value]) => ({
|
|
668
719
|
id: key,
|
|
669
720
|
plaintext: value,
|
|
670
721
|
table: table.tableName,
|
|
671
|
-
column: key,
|
|
672
|
-
lockContext
|
|
722
|
+
column: toColumnName(key),
|
|
723
|
+
lockContext
|
|
673
724
|
})
|
|
674
725
|
);
|
|
675
726
|
const encryptedData = await handleSingleModelBulkOperation(
|
|
676
727
|
bulkEncryptPayload,
|
|
677
728
|
(items) => encryptBulk(client, {
|
|
678
729
|
plaintexts: items,
|
|
679
|
-
serviceToken: lockContext.ctsToken,
|
|
680
730
|
unverifiedContext: auditData?.metadata
|
|
681
731
|
}),
|
|
682
732
|
keyMap
|
|
@@ -733,7 +783,7 @@ function prepareBulkModelsForOperation(models, table) {
|
|
|
733
783
|
}
|
|
734
784
|
};
|
|
735
785
|
if (table) {
|
|
736
|
-
const columnPaths =
|
|
786
|
+
const { columnPaths } = resolveEncryptColumnMap(table);
|
|
737
787
|
processNestedFields(model, "", columnPaths);
|
|
738
788
|
} else {
|
|
739
789
|
const processEncryptedFields = (obj, prefix = "", columnPaths = []) => {
|
|
@@ -771,6 +821,15 @@ function prepareBulkModelsForOperation(models, table) {
|
|
|
771
821
|
}
|
|
772
822
|
return { otherFields, operationFields, keyMap, nullFields };
|
|
773
823
|
}
|
|
824
|
+
function fieldsForModelIndex(fields, modelIndex) {
|
|
825
|
+
const result = {};
|
|
826
|
+
for (const [id, value] of Object.entries(fields)) {
|
|
827
|
+
const sep = id.indexOf("-");
|
|
828
|
+
if (Number.parseInt(id.slice(0, sep), 10) !== modelIndex) continue;
|
|
829
|
+
result[id.slice(sep + 1)] = value;
|
|
830
|
+
}
|
|
831
|
+
return result;
|
|
832
|
+
}
|
|
774
833
|
async function bulkEncryptModels(models, table, client, auditData) {
|
|
775
834
|
if (!client) {
|
|
776
835
|
throw new Error("Client not initialized");
|
|
@@ -779,12 +838,13 @@ async function bulkEncryptModels(models, table, client, auditData) {
|
|
|
779
838
|
return [];
|
|
780
839
|
}
|
|
781
840
|
const { otherFields, operationFields, keyMap, nullFields } = prepareBulkModelsForOperation(models, table);
|
|
841
|
+
const { toColumnName } = resolveEncryptColumnMap(table);
|
|
782
842
|
const bulkEncryptPayload = operationFields.flatMap(
|
|
783
843
|
(fields, modelIndex) => Object.entries(fields).map(([key, value]) => ({
|
|
784
844
|
id: `${modelIndex}-${key}`,
|
|
785
845
|
plaintext: value,
|
|
786
846
|
table: table.tableName,
|
|
787
|
-
column: key
|
|
847
|
+
column: toColumnName(key)
|
|
788
848
|
}))
|
|
789
849
|
);
|
|
790
850
|
const encryptedData = await handleMultiModelBulkOperation(
|
|
@@ -801,15 +861,7 @@ async function bulkEncryptModels(models, table, client, auditData) {
|
|
|
801
861
|
const parts = key.split(".");
|
|
802
862
|
setNestedValue(result, parts, value);
|
|
803
863
|
}
|
|
804
|
-
const modelData =
|
|
805
|
-
Object.entries(encryptedData).filter(([key]) => {
|
|
806
|
-
const [idx] = key.split("-");
|
|
807
|
-
return Number.parseInt(idx) === modelIndex;
|
|
808
|
-
}).map(([key, value]) => {
|
|
809
|
-
const [_2, fieldKey] = key.split("-");
|
|
810
|
-
return [fieldKey, value];
|
|
811
|
-
})
|
|
812
|
-
);
|
|
864
|
+
const modelData = fieldsForModelIndex(encryptedData, modelIndex);
|
|
813
865
|
for (const [key, value] of Object.entries(modelData)) {
|
|
814
866
|
const parts = key.split(".");
|
|
815
867
|
setNestedValue(result, parts, value);
|
|
@@ -845,15 +897,7 @@ async function bulkDecryptModels(models, client, auditData) {
|
|
|
845
897
|
const parts = key.split(".");
|
|
846
898
|
setNestedValue(result, parts, value);
|
|
847
899
|
}
|
|
848
|
-
const modelData =
|
|
849
|
-
Object.entries(decryptedFields).filter(([key]) => {
|
|
850
|
-
const [idx] = key.split("-");
|
|
851
|
-
return Number.parseInt(idx) === modelIndex;
|
|
852
|
-
}).map(([key, value]) => {
|
|
853
|
-
const [_2, fieldKey] = key.split("-");
|
|
854
|
-
return [fieldKey, value];
|
|
855
|
-
})
|
|
856
|
-
);
|
|
900
|
+
const modelData = fieldsForModelIndex(decryptedFields, modelIndex);
|
|
857
901
|
for (const [key, value] of Object.entries(modelData)) {
|
|
858
902
|
const parts = key.split(".");
|
|
859
903
|
setNestedValue(result, parts, value);
|
|
@@ -873,14 +917,13 @@ async function bulkDecryptModelsWithLockContext(models, client, lockContext, aud
|
|
|
873
917
|
(fields, modelIndex) => Object.entries(fields).map(([key, value]) => ({
|
|
874
918
|
id: `${modelIndex}-${key}`,
|
|
875
919
|
ciphertext: value,
|
|
876
|
-
lockContext
|
|
920
|
+
lockContext
|
|
877
921
|
}))
|
|
878
922
|
);
|
|
879
923
|
const decryptedFields = await handleMultiModelBulkOperation(
|
|
880
924
|
bulkDecryptPayload,
|
|
881
925
|
(items) => decryptBulk(client, {
|
|
882
926
|
ciphertexts: items,
|
|
883
|
-
serviceToken: lockContext.ctsToken,
|
|
884
927
|
unverifiedContext: auditData?.metadata
|
|
885
928
|
}),
|
|
886
929
|
keyMap
|
|
@@ -891,15 +934,7 @@ async function bulkDecryptModelsWithLockContext(models, client, lockContext, aud
|
|
|
891
934
|
const parts = key.split(".");
|
|
892
935
|
setNestedValue(result, parts, value);
|
|
893
936
|
}
|
|
894
|
-
const modelData =
|
|
895
|
-
Object.entries(decryptedFields).filter(([key]) => {
|
|
896
|
-
const [idx] = key.split("-");
|
|
897
|
-
return Number.parseInt(idx) === modelIndex;
|
|
898
|
-
}).map(([key, value]) => {
|
|
899
|
-
const [_2, fieldKey] = key.split("-");
|
|
900
|
-
return [fieldKey, value];
|
|
901
|
-
})
|
|
902
|
-
);
|
|
937
|
+
const modelData = fieldsForModelIndex(decryptedFields, modelIndex);
|
|
903
938
|
for (const [key, value] of Object.entries(modelData)) {
|
|
904
939
|
const parts = key.split(".");
|
|
905
940
|
setNestedValue(result, parts, value);
|
|
@@ -915,20 +950,20 @@ async function bulkEncryptModelsWithLockContext(models, table, client, lockConte
|
|
|
915
950
|
throw new Error("Lock context is not initialized");
|
|
916
951
|
}
|
|
917
952
|
const { otherFields, operationFields, keyMap, nullFields } = prepareBulkModelsForOperation(models, table);
|
|
953
|
+
const { toColumnName } = resolveEncryptColumnMap(table);
|
|
918
954
|
const bulkEncryptPayload = operationFields.flatMap(
|
|
919
955
|
(fields, modelIndex) => Object.entries(fields).map(([key, value]) => ({
|
|
920
956
|
id: `${modelIndex}-${key}`,
|
|
921
957
|
plaintext: value,
|
|
922
958
|
table: table.tableName,
|
|
923
|
-
column: key,
|
|
924
|
-
lockContext
|
|
959
|
+
column: toColumnName(key),
|
|
960
|
+
lockContext
|
|
925
961
|
}))
|
|
926
962
|
);
|
|
927
963
|
const encryptedData = await handleMultiModelBulkOperation(
|
|
928
964
|
bulkEncryptPayload,
|
|
929
965
|
(items) => encryptBulk(client, {
|
|
930
966
|
plaintexts: items,
|
|
931
|
-
serviceToken: lockContext.ctsToken,
|
|
932
967
|
unverifiedContext: auditData?.metadata
|
|
933
968
|
}),
|
|
934
969
|
keyMap
|
|
@@ -939,15 +974,7 @@ async function bulkEncryptModelsWithLockContext(models, table, client, lockConte
|
|
|
939
974
|
const parts = key.split(".");
|
|
940
975
|
setNestedValue(result, parts, value);
|
|
941
976
|
}
|
|
942
|
-
const modelData =
|
|
943
|
-
Object.entries(encryptedData).filter(([key]) => {
|
|
944
|
-
const [idx] = key.split("-");
|
|
945
|
-
return Number.parseInt(idx) === modelIndex;
|
|
946
|
-
}).map(([key, value]) => {
|
|
947
|
-
const [_2, fieldKey] = key.split("-");
|
|
948
|
-
return [fieldKey, value];
|
|
949
|
-
})
|
|
950
|
-
);
|
|
977
|
+
const modelData = fieldsForModelIndex(encryptedData, modelIndex);
|
|
951
978
|
for (const [key, value] of Object.entries(modelData)) {
|
|
952
979
|
const parts = key.split(".");
|
|
953
980
|
setNestedValue(result, parts, value);
|
|
@@ -975,7 +1002,7 @@ var BulkDecryptModelsOperation = class extends EncryptionOperation {
|
|
|
975
1002
|
count: this.models.length,
|
|
976
1003
|
lockContext: false
|
|
977
1004
|
});
|
|
978
|
-
const result = await
|
|
1005
|
+
const result = await withResult(
|
|
979
1006
|
async () => {
|
|
980
1007
|
if (!this.client) {
|
|
981
1008
|
throw noClientError();
|
|
@@ -1022,20 +1049,17 @@ var BulkDecryptModelsOperationWithLockContext = class extends EncryptionOperatio
|
|
|
1022
1049
|
count: models.length,
|
|
1023
1050
|
lockContext: true
|
|
1024
1051
|
});
|
|
1025
|
-
const result = await
|
|
1052
|
+
const result = await withResult(
|
|
1026
1053
|
async () => {
|
|
1027
1054
|
if (!client) {
|
|
1028
1055
|
throw noClientError();
|
|
1029
1056
|
}
|
|
1030
|
-
const context =
|
|
1031
|
-
if (context.failure) {
|
|
1032
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1033
|
-
}
|
|
1057
|
+
const context = resolveLockContext(this.lockContext);
|
|
1034
1058
|
const auditData = this.getAuditData();
|
|
1035
1059
|
return await bulkDecryptModelsWithLockContext(
|
|
1036
1060
|
models,
|
|
1037
1061
|
client,
|
|
1038
|
-
context
|
|
1062
|
+
context,
|
|
1039
1063
|
auditData
|
|
1040
1064
|
);
|
|
1041
1065
|
},
|
|
@@ -1054,10 +1078,9 @@ var BulkDecryptModelsOperationWithLockContext = class extends EncryptionOperatio
|
|
|
1054
1078
|
};
|
|
1055
1079
|
|
|
1056
1080
|
// src/encryption/operations/bulk-encrypt.ts
|
|
1057
|
-
import { withResult as withResult4 } from "@byteslice/result";
|
|
1058
1081
|
import { encryptBulk as encryptBulk2 } from "@cipherstash/protect-ffi";
|
|
1059
1082
|
var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
|
|
1060
|
-
return plaintexts.map(({ id, plaintext }) => ({
|
|
1083
|
+
return plaintexts.filter(({ plaintext }) => plaintext !== null).map(({ id, plaintext }) => ({
|
|
1061
1084
|
id,
|
|
1062
1085
|
plaintext,
|
|
1063
1086
|
column: column.getName(),
|
|
@@ -1065,6 +1088,20 @@ var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
|
|
|
1065
1088
|
...lockContext && { lockContext }
|
|
1066
1089
|
}));
|
|
1067
1090
|
};
|
|
1091
|
+
var createNullResult2 = (plaintexts) => plaintexts.map(({ id }) => ({ id, data: null }));
|
|
1092
|
+
var mapEncryptedDataToResult = (plaintexts, encryptedData) => {
|
|
1093
|
+
const result = new Array(plaintexts.length);
|
|
1094
|
+
let encryptedIndex = 0;
|
|
1095
|
+
for (let i = 0; i < plaintexts.length; i++) {
|
|
1096
|
+
if (plaintexts[i].plaintext === null) {
|
|
1097
|
+
result[i] = { id: plaintexts[i].id, data: null };
|
|
1098
|
+
} else {
|
|
1099
|
+
result[i] = { id: plaintexts[i].id, data: encryptedData[encryptedIndex] };
|
|
1100
|
+
encryptedIndex++;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return result;
|
|
1104
|
+
};
|
|
1068
1105
|
var BulkEncryptOperation = class extends EncryptionOperation {
|
|
1069
1106
|
client;
|
|
1070
1107
|
plaintexts;
|
|
@@ -1089,7 +1126,7 @@ var BulkEncryptOperation = class extends EncryptionOperation {
|
|
|
1089
1126
|
count: this.plaintexts?.length ?? 0,
|
|
1090
1127
|
lockContext: false
|
|
1091
1128
|
});
|
|
1092
|
-
const result = await
|
|
1129
|
+
const result = await withResult(
|
|
1093
1130
|
async () => {
|
|
1094
1131
|
if (!this.client) {
|
|
1095
1132
|
throw noClientError();
|
|
@@ -1097,20 +1134,20 @@ var BulkEncryptOperation = class extends EncryptionOperation {
|
|
|
1097
1134
|
if (!this.plaintexts || this.plaintexts.length === 0) {
|
|
1098
1135
|
return [];
|
|
1099
1136
|
}
|
|
1100
|
-
const
|
|
1137
|
+
const nonNullPayloads = createEncryptPayloads(
|
|
1101
1138
|
this.plaintexts,
|
|
1102
1139
|
this.column,
|
|
1103
1140
|
this.table
|
|
1104
1141
|
);
|
|
1142
|
+
if (nonNullPayloads.length === 0) {
|
|
1143
|
+
return createNullResult2(this.plaintexts);
|
|
1144
|
+
}
|
|
1105
1145
|
const { metadata } = this.getAuditData();
|
|
1106
1146
|
const encryptedData = await encryptBulk2(this.client, {
|
|
1107
|
-
plaintexts:
|
|
1147
|
+
plaintexts: nonNullPayloads,
|
|
1108
1148
|
unverifiedContext: metadata
|
|
1109
1149
|
});
|
|
1110
|
-
return
|
|
1111
|
-
id: this.plaintexts[i].id,
|
|
1112
|
-
data
|
|
1113
|
-
}));
|
|
1150
|
+
return mapEncryptedDataToResult(this.plaintexts, encryptedData);
|
|
1114
1151
|
},
|
|
1115
1152
|
(error) => {
|
|
1116
1153
|
log.set({ errorCode: getErrorCode(error) ?? "unknown" });
|
|
@@ -1155,7 +1192,7 @@ var BulkEncryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1155
1192
|
count: plaintexts?.length ?? 0,
|
|
1156
1193
|
lockContext: true
|
|
1157
1194
|
});
|
|
1158
|
-
const result = await
|
|
1195
|
+
const result = await withResult(
|
|
1159
1196
|
async () => {
|
|
1160
1197
|
if (!client) {
|
|
1161
1198
|
throw noClientError();
|
|
@@ -1163,26 +1200,22 @@ var BulkEncryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1163
1200
|
if (!plaintexts || plaintexts.length === 0) {
|
|
1164
1201
|
return [];
|
|
1165
1202
|
}
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1169
|
-
}
|
|
1170
|
-
const payloads = createEncryptPayloads(
|
|
1203
|
+
const lockContext = resolveLockContext(this.lockContext);
|
|
1204
|
+
const nonNullPayloads = createEncryptPayloads(
|
|
1171
1205
|
plaintexts,
|
|
1172
1206
|
column,
|
|
1173
1207
|
table,
|
|
1174
|
-
|
|
1208
|
+
lockContext
|
|
1175
1209
|
);
|
|
1210
|
+
if (nonNullPayloads.length === 0) {
|
|
1211
|
+
return createNullResult2(plaintexts);
|
|
1212
|
+
}
|
|
1176
1213
|
const { metadata } = this.getAuditData();
|
|
1177
1214
|
const encryptedData = await encryptBulk2(client, {
|
|
1178
|
-
plaintexts:
|
|
1179
|
-
serviceToken: context.data.ctsToken,
|
|
1215
|
+
plaintexts: nonNullPayloads,
|
|
1180
1216
|
unverifiedContext: metadata
|
|
1181
1217
|
});
|
|
1182
|
-
return
|
|
1183
|
-
id: plaintexts[i].id,
|
|
1184
|
-
data
|
|
1185
|
-
}));
|
|
1218
|
+
return mapEncryptedDataToResult(plaintexts, encryptedData);
|
|
1186
1219
|
},
|
|
1187
1220
|
(error) => {
|
|
1188
1221
|
log.set({ errorCode: getErrorCode(error) ?? "unknown" });
|
|
@@ -1199,7 +1232,6 @@ var BulkEncryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1199
1232
|
};
|
|
1200
1233
|
|
|
1201
1234
|
// src/encryption/operations/bulk-encrypt-models.ts
|
|
1202
|
-
import { withResult as withResult5 } from "@byteslice/result";
|
|
1203
1235
|
var BulkEncryptModelsOperation = class extends EncryptionOperation {
|
|
1204
1236
|
client;
|
|
1205
1237
|
models;
|
|
@@ -1221,7 +1253,7 @@ var BulkEncryptModelsOperation = class extends EncryptionOperation {
|
|
|
1221
1253
|
count: this.models.length,
|
|
1222
1254
|
lockContext: false
|
|
1223
1255
|
});
|
|
1224
|
-
const result = await
|
|
1256
|
+
const result = await withResult(
|
|
1225
1257
|
async () => {
|
|
1226
1258
|
if (!this.client) {
|
|
1227
1259
|
throw noClientError();
|
|
@@ -1275,21 +1307,18 @@ var BulkEncryptModelsOperationWithLockContext = class extends EncryptionOperatio
|
|
|
1275
1307
|
count: models.length,
|
|
1276
1308
|
lockContext: true
|
|
1277
1309
|
});
|
|
1278
|
-
const result = await
|
|
1310
|
+
const result = await withResult(
|
|
1279
1311
|
async () => {
|
|
1280
1312
|
if (!client) {
|
|
1281
1313
|
throw noClientError();
|
|
1282
1314
|
}
|
|
1283
|
-
const context =
|
|
1284
|
-
if (context.failure) {
|
|
1285
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1286
|
-
}
|
|
1315
|
+
const context = resolveLockContext(this.lockContext);
|
|
1287
1316
|
const auditData = this.getAuditData();
|
|
1288
1317
|
return await bulkEncryptModelsWithLockContext(
|
|
1289
1318
|
models,
|
|
1290
1319
|
table,
|
|
1291
1320
|
client,
|
|
1292
|
-
context
|
|
1321
|
+
context,
|
|
1293
1322
|
auditData
|
|
1294
1323
|
);
|
|
1295
1324
|
},
|
|
@@ -1308,12 +1337,15 @@ var BulkEncryptModelsOperationWithLockContext = class extends EncryptionOperatio
|
|
|
1308
1337
|
};
|
|
1309
1338
|
|
|
1310
1339
|
// src/encryption/operations/decrypt.ts
|
|
1311
|
-
import { withResult as withResult6 } from "@byteslice/result";
|
|
1312
1340
|
import {
|
|
1313
1341
|
decrypt as ffiDecrypt
|
|
1314
1342
|
} from "@cipherstash/protect-ffi";
|
|
1315
1343
|
var DecryptOperation = class extends EncryptionOperation {
|
|
1316
1344
|
client;
|
|
1345
|
+
// Internally widened to allow null so the runtime guard below can
|
|
1346
|
+
// short-circuit on legacy / manually-NULLed rows. The public
|
|
1347
|
+
// `Encryption.decrypt()` signature still rejects null at the type
|
|
1348
|
+
// layer; this is defense in depth for direct construction.
|
|
1317
1349
|
encryptedData;
|
|
1318
1350
|
constructor(client, encryptedData) {
|
|
1319
1351
|
super();
|
|
@@ -1329,11 +1361,14 @@ var DecryptOperation = class extends EncryptionOperation {
|
|
|
1329
1361
|
op: "decrypt",
|
|
1330
1362
|
lockContext: false
|
|
1331
1363
|
});
|
|
1332
|
-
const result = await
|
|
1364
|
+
const result = await withResult(
|
|
1333
1365
|
async () => {
|
|
1334
1366
|
if (!this.client) {
|
|
1335
1367
|
throw noClientError();
|
|
1336
1368
|
}
|
|
1369
|
+
if (this.encryptedData === null) {
|
|
1370
|
+
return null;
|
|
1371
|
+
}
|
|
1337
1372
|
const { metadata } = this.getAuditData();
|
|
1338
1373
|
return await ffiDecrypt(this.client, {
|
|
1339
1374
|
ciphertext: this.encryptedData,
|
|
@@ -1378,22 +1413,21 @@ var DecryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1378
1413
|
op: "decrypt",
|
|
1379
1414
|
lockContext: true
|
|
1380
1415
|
});
|
|
1381
|
-
const result = await
|
|
1416
|
+
const result = await withResult(
|
|
1382
1417
|
async () => {
|
|
1383
1418
|
const { client, encryptedData } = this.operation.getOperation();
|
|
1384
1419
|
if (!client) {
|
|
1385
1420
|
throw noClientError();
|
|
1386
1421
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
if (context.failure) {
|
|
1390
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1422
|
+
if (encryptedData === null) {
|
|
1423
|
+
return null;
|
|
1391
1424
|
}
|
|
1425
|
+
const { metadata } = this.getAuditData();
|
|
1426
|
+
const lockContext = resolveLockContext(this.lockContext);
|
|
1392
1427
|
return await ffiDecrypt(client, {
|
|
1393
1428
|
ciphertext: encryptedData,
|
|
1394
1429
|
unverifiedContext: metadata,
|
|
1395
|
-
lockContext
|
|
1396
|
-
serviceToken: context.data.ctsToken
|
|
1430
|
+
lockContext
|
|
1397
1431
|
});
|
|
1398
1432
|
},
|
|
1399
1433
|
(error) => {
|
|
@@ -1411,7 +1445,6 @@ var DecryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1411
1445
|
};
|
|
1412
1446
|
|
|
1413
1447
|
// src/encryption/operations/decrypt-model.ts
|
|
1414
|
-
import { withResult as withResult7 } from "@byteslice/result";
|
|
1415
1448
|
var DecryptModelOperation = class extends EncryptionOperation {
|
|
1416
1449
|
client;
|
|
1417
1450
|
model;
|
|
@@ -1429,7 +1462,7 @@ var DecryptModelOperation = class extends EncryptionOperation {
|
|
|
1429
1462
|
op: "decryptModel",
|
|
1430
1463
|
lockContext: false
|
|
1431
1464
|
});
|
|
1432
|
-
const result = await
|
|
1465
|
+
const result = await withResult(
|
|
1433
1466
|
async () => {
|
|
1434
1467
|
if (!this.client) {
|
|
1435
1468
|
throw noClientError();
|
|
@@ -1474,21 +1507,18 @@ var DecryptModelOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1474
1507
|
op: "decryptModel",
|
|
1475
1508
|
lockContext: true
|
|
1476
1509
|
});
|
|
1477
|
-
const result = await
|
|
1510
|
+
const result = await withResult(
|
|
1478
1511
|
async () => {
|
|
1479
1512
|
const { client, model } = this.operation.getOperation();
|
|
1480
1513
|
if (!client) {
|
|
1481
1514
|
throw noClientError();
|
|
1482
1515
|
}
|
|
1483
|
-
const context =
|
|
1484
|
-
if (context.failure) {
|
|
1485
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1486
|
-
}
|
|
1516
|
+
const context = resolveLockContext(this.lockContext);
|
|
1487
1517
|
const auditData = this.getAuditData();
|
|
1488
1518
|
return await decryptModelFieldsWithLockContext(
|
|
1489
1519
|
model,
|
|
1490
1520
|
client,
|
|
1491
|
-
context
|
|
1521
|
+
context,
|
|
1492
1522
|
auditData
|
|
1493
1523
|
);
|
|
1494
1524
|
},
|
|
@@ -1507,12 +1537,15 @@ var DecryptModelOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1507
1537
|
};
|
|
1508
1538
|
|
|
1509
1539
|
// src/encryption/operations/encrypt.ts
|
|
1510
|
-
import { withResult as withResult8 } from "@byteslice/result";
|
|
1511
1540
|
import {
|
|
1512
1541
|
encrypt as ffiEncrypt
|
|
1513
1542
|
} from "@cipherstash/protect-ffi";
|
|
1514
1543
|
var EncryptOperation = class extends EncryptionOperation {
|
|
1515
1544
|
client;
|
|
1545
|
+
// Internally widened to allow null so the runtime guard below can
|
|
1546
|
+
// short-circuit. The public `Encryption.encrypt()` signature still
|
|
1547
|
+
// rejects null at the type layer; this is defense in depth for callers
|
|
1548
|
+
// that reach this class through casts or dynamic field walking.
|
|
1516
1549
|
plaintext;
|
|
1517
1550
|
column;
|
|
1518
1551
|
table;
|
|
@@ -1534,19 +1567,20 @@ var EncryptOperation = class extends EncryptionOperation {
|
|
|
1534
1567
|
column: this.column.getName(),
|
|
1535
1568
|
lockContext: false
|
|
1536
1569
|
});
|
|
1537
|
-
const result = await
|
|
1570
|
+
const result = await withResult(
|
|
1538
1571
|
async () => {
|
|
1539
1572
|
if (!this.client) {
|
|
1540
1573
|
throw noClientError();
|
|
1541
1574
|
}
|
|
1542
|
-
if (
|
|
1543
|
-
|
|
1544
|
-
}
|
|
1545
|
-
if (typeof this.plaintext === "number" && !Number.isFinite(this.plaintext)) {
|
|
1546
|
-
throw new Error("[encryption]: Cannot encrypt Infinity value");
|
|
1575
|
+
if (this.plaintext === null) {
|
|
1576
|
+
return null;
|
|
1547
1577
|
}
|
|
1578
|
+
assertValidNumericValue(this.plaintext);
|
|
1548
1579
|
const { metadata } = this.getAuditData();
|
|
1549
1580
|
return await ffiEncrypt(this.client, {
|
|
1581
|
+
// `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
|
|
1582
|
+
// `toJSON` at the boundary); cast until the upstream `JsPlaintext` input
|
|
1583
|
+
// union is corrected to include it.
|
|
1550
1584
|
plaintext: this.plaintext,
|
|
1551
1585
|
column: this.column.getName(),
|
|
1552
1586
|
table: this.table.tableName,
|
|
@@ -1595,22 +1629,22 @@ var EncryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1595
1629
|
column: column.getName(),
|
|
1596
1630
|
lockContext: true
|
|
1597
1631
|
});
|
|
1598
|
-
const result = await
|
|
1632
|
+
const result = await withResult(
|
|
1599
1633
|
async () => {
|
|
1600
1634
|
if (!client) {
|
|
1601
1635
|
throw noClientError();
|
|
1602
1636
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
if (context.failure) {
|
|
1606
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1637
|
+
if (plaintext === null) {
|
|
1638
|
+
return null;
|
|
1607
1639
|
}
|
|
1640
|
+
assertValidNumericValue(plaintext);
|
|
1641
|
+
const { metadata } = this.getAuditData();
|
|
1642
|
+
const lockContext = resolveLockContext(this.lockContext);
|
|
1608
1643
|
return await ffiEncrypt(client, {
|
|
1609
1644
|
plaintext,
|
|
1610
1645
|
column: column.getName(),
|
|
1611
1646
|
table: table.tableName,
|
|
1612
|
-
lockContext
|
|
1613
|
-
serviceToken: context.data.ctsToken,
|
|
1647
|
+
lockContext,
|
|
1614
1648
|
unverifiedContext: metadata
|
|
1615
1649
|
});
|
|
1616
1650
|
},
|
|
@@ -1629,7 +1663,6 @@ var EncryptOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1629
1663
|
};
|
|
1630
1664
|
|
|
1631
1665
|
// src/encryption/operations/encrypt-model.ts
|
|
1632
|
-
import { withResult as withResult9 } from "@byteslice/result";
|
|
1633
1666
|
var EncryptModelOperation = class extends EncryptionOperation {
|
|
1634
1667
|
client;
|
|
1635
1668
|
model;
|
|
@@ -1650,7 +1683,7 @@ var EncryptModelOperation = class extends EncryptionOperation {
|
|
|
1650
1683
|
table: this.table.tableName,
|
|
1651
1684
|
lockContext: false
|
|
1652
1685
|
});
|
|
1653
|
-
const result = await
|
|
1686
|
+
const result = await withResult(
|
|
1654
1687
|
async () => {
|
|
1655
1688
|
if (!this.client) {
|
|
1656
1689
|
throw noClientError();
|
|
@@ -1703,21 +1736,18 @@ var EncryptModelOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1703
1736
|
table: table.tableName,
|
|
1704
1737
|
lockContext: true
|
|
1705
1738
|
});
|
|
1706
|
-
const result = await
|
|
1739
|
+
const result = await withResult(
|
|
1707
1740
|
async () => {
|
|
1708
1741
|
if (!client) {
|
|
1709
1742
|
throw noClientError();
|
|
1710
1743
|
}
|
|
1711
|
-
const context =
|
|
1712
|
-
if (context.failure) {
|
|
1713
|
-
throw new Error(`[encryption]: ${context.failure.message}`);
|
|
1714
|
-
}
|
|
1744
|
+
const context = resolveLockContext(this.lockContext);
|
|
1715
1745
|
const auditData = this.getAuditData();
|
|
1716
1746
|
return await encryptModelFieldsWithLockContext(
|
|
1717
1747
|
model,
|
|
1718
1748
|
table,
|
|
1719
1749
|
client,
|
|
1720
|
-
context
|
|
1750
|
+
context,
|
|
1721
1751
|
auditData
|
|
1722
1752
|
);
|
|
1723
1753
|
},
|
|
@@ -1736,7 +1766,6 @@ var EncryptModelOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1736
1766
|
};
|
|
1737
1767
|
|
|
1738
1768
|
// src/encryption/operations/encrypt-query.ts
|
|
1739
|
-
import { withResult as withResult10 } from "@byteslice/result";
|
|
1740
1769
|
import {
|
|
1741
1770
|
encryptQuery as ffiEncryptQuery
|
|
1742
1771
|
} from "@cipherstash/protect-ffi";
|
|
@@ -1747,6 +1776,9 @@ var EncryptQueryOperation = class extends EncryptionOperation {
|
|
|
1747
1776
|
this.plaintext = plaintext;
|
|
1748
1777
|
this.opts = opts;
|
|
1749
1778
|
}
|
|
1779
|
+
client;
|
|
1780
|
+
plaintext;
|
|
1781
|
+
opts;
|
|
1750
1782
|
withLockContext(lockContext) {
|
|
1751
1783
|
return new EncryptQueryOperationWithLockContext(
|
|
1752
1784
|
this.client,
|
|
@@ -1765,27 +1797,35 @@ var EncryptQueryOperation = class extends EncryptionOperation {
|
|
|
1765
1797
|
queryType: this.opts.queryType,
|
|
1766
1798
|
lockContext: false
|
|
1767
1799
|
});
|
|
1768
|
-
|
|
1800
|
+
if (this.plaintext === null || this.plaintext === void 0) {
|
|
1801
|
+
log.emit();
|
|
1802
|
+
return { data: null };
|
|
1803
|
+
}
|
|
1804
|
+
const plaintext = this.plaintext;
|
|
1805
|
+
const validationError = validateNumericValue(plaintext);
|
|
1769
1806
|
if (validationError?.failure) {
|
|
1770
1807
|
log.emit();
|
|
1771
1808
|
return { failure: validationError.failure };
|
|
1772
1809
|
}
|
|
1773
|
-
const result = await
|
|
1810
|
+
const result = await withResult(
|
|
1774
1811
|
async () => {
|
|
1775
1812
|
if (!this.client) throw noClientError();
|
|
1776
1813
|
const { metadata } = this.getAuditData();
|
|
1777
1814
|
const { indexType, queryOp } = resolveIndexType(
|
|
1778
1815
|
this.opts.column,
|
|
1779
1816
|
this.opts.queryType,
|
|
1780
|
-
|
|
1817
|
+
plaintext
|
|
1781
1818
|
);
|
|
1782
1819
|
assertValueIndexCompatibility(
|
|
1783
|
-
|
|
1820
|
+
plaintext,
|
|
1784
1821
|
indexType,
|
|
1785
1822
|
this.opts.column.getName()
|
|
1786
1823
|
);
|
|
1787
1824
|
const encrypted = await ffiEncryptQuery(this.client, {
|
|
1788
|
-
|
|
1825
|
+
// `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
|
|
1826
|
+
// `toJSON` at the boundary); cast until the upstream input union is
|
|
1827
|
+
// corrected to include it.
|
|
1828
|
+
plaintext,
|
|
1789
1829
|
column: this.opts.column.getName(),
|
|
1790
1830
|
table: this.opts.table.tableName,
|
|
1791
1831
|
indexType,
|
|
@@ -1819,6 +1859,10 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1819
1859
|
this.lockContext = lockContext;
|
|
1820
1860
|
this.auditMetadata = auditMetadata;
|
|
1821
1861
|
}
|
|
1862
|
+
client;
|
|
1863
|
+
plaintext;
|
|
1864
|
+
opts;
|
|
1865
|
+
lockContext;
|
|
1822
1866
|
async execute() {
|
|
1823
1867
|
const log = createRequestLogger();
|
|
1824
1868
|
log.set({
|
|
@@ -1828,39 +1872,41 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1828
1872
|
queryType: this.opts.queryType,
|
|
1829
1873
|
lockContext: true
|
|
1830
1874
|
});
|
|
1831
|
-
|
|
1832
|
-
if (validationError?.failure) {
|
|
1875
|
+
if (this.plaintext === null || this.plaintext === void 0) {
|
|
1833
1876
|
log.emit();
|
|
1834
|
-
return {
|
|
1877
|
+
return { data: null };
|
|
1835
1878
|
}
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1879
|
+
const plaintext = this.plaintext;
|
|
1880
|
+
const validationError = validateNumericValue(plaintext);
|
|
1881
|
+
if (validationError?.failure) {
|
|
1838
1882
|
log.emit();
|
|
1839
|
-
return { failure:
|
|
1883
|
+
return { failure: validationError.failure };
|
|
1840
1884
|
}
|
|
1841
|
-
const
|
|
1842
|
-
const result = await withResult10(
|
|
1885
|
+
const result = await withResult(
|
|
1843
1886
|
async () => {
|
|
1844
1887
|
if (!this.client) throw noClientError();
|
|
1888
|
+
const context = resolveLockContext(this.lockContext);
|
|
1845
1889
|
const { metadata } = this.getAuditData();
|
|
1846
1890
|
const { indexType, queryOp } = resolveIndexType(
|
|
1847
1891
|
this.opts.column,
|
|
1848
1892
|
this.opts.queryType,
|
|
1849
|
-
|
|
1893
|
+
plaintext
|
|
1850
1894
|
);
|
|
1851
1895
|
assertValueIndexCompatibility(
|
|
1852
|
-
|
|
1896
|
+
plaintext,
|
|
1853
1897
|
indexType,
|
|
1854
1898
|
this.opts.column.getName()
|
|
1855
1899
|
);
|
|
1856
1900
|
const encrypted = await ffiEncryptQuery(this.client, {
|
|
1857
|
-
|
|
1901
|
+
// `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
|
|
1902
|
+
// `toJSON` at the boundary); cast until the upstream input union is
|
|
1903
|
+
// corrected to include it.
|
|
1904
|
+
plaintext,
|
|
1858
1905
|
column: this.opts.column.getName(),
|
|
1859
1906
|
table: this.opts.table.tableName,
|
|
1860
1907
|
indexType,
|
|
1861
1908
|
queryOp,
|
|
1862
1909
|
lockContext: context,
|
|
1863
|
-
serviceToken: ctsToken,
|
|
1864
1910
|
unverifiedContext: metadata
|
|
1865
1911
|
});
|
|
1866
1912
|
return formatEncryptedResult(encrypted, this.opts.returnType);
|
|
@@ -1883,6 +1929,20 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
|
|
|
1883
1929
|
var noClientError = () => new Error(
|
|
1884
1930
|
"The Encryption client has not been initialized. Please call init() before using the client."
|
|
1885
1931
|
);
|
|
1932
|
+
function resolveEqlVersion(schemas, explicit) {
|
|
1933
|
+
const v3Count = schemas.filter(
|
|
1934
|
+
(schema) => typeof schema.buildColumnKeyMap === "function"
|
|
1935
|
+
).length;
|
|
1936
|
+
if (v3Count > 0 && v3Count < schemas.length) {
|
|
1937
|
+
throw new Error(
|
|
1938
|
+
"[encryption]: cannot mix EQL v2 and EQL v3 tables in one client \u2014 one client emits exactly one wire format. Create separate clients for the v2 and v3 schemas."
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
if (explicit !== void 0) {
|
|
1942
|
+
return explicit;
|
|
1943
|
+
}
|
|
1944
|
+
return v3Count === 0 ? void 0 : 3;
|
|
1945
|
+
}
|
|
1886
1946
|
var EncryptionClient = class {
|
|
1887
1947
|
client;
|
|
1888
1948
|
encryptConfig;
|
|
@@ -1893,7 +1953,7 @@ var EncryptionClient = class {
|
|
|
1893
1953
|
* @returns A promise that resolves to a {@link Result} containing the initialized EncryptionClient or an {@link EncryptionError}.
|
|
1894
1954
|
**/
|
|
1895
1955
|
async init(config) {
|
|
1896
|
-
return await
|
|
1956
|
+
return await withResult(
|
|
1897
1957
|
async () => {
|
|
1898
1958
|
const validated = encryptConfigSchema.parse(
|
|
1899
1959
|
config.encryptConfig
|
|
@@ -1912,7 +1972,9 @@ var EncryptionClient = class {
|
|
|
1912
1972
|
clientId: config.clientId,
|
|
1913
1973
|
clientKey: config.clientKey,
|
|
1914
1974
|
keyset: toFfiKeysetIdentifier(config.keyset)
|
|
1915
|
-
}
|
|
1975
|
+
},
|
|
1976
|
+
strategy: config.authStrategy,
|
|
1977
|
+
eqlVersion: config.eqlVersion
|
|
1916
1978
|
});
|
|
1917
1979
|
this.encryptConfig = validated;
|
|
1918
1980
|
logger.debug("Successfully initialized the Encryption client.");
|
|
@@ -2003,7 +2065,7 @@ var EncryptionClient = class {
|
|
|
2003
2065
|
return new EncryptOperation(this.client, plaintext, opts);
|
|
2004
2066
|
}
|
|
2005
2067
|
encryptQuery(plaintextOrTerms, opts) {
|
|
2006
|
-
if (isScalarQueryTermArray(plaintextOrTerms)) {
|
|
2068
|
+
if (!opts && isScalarQueryTermArray(plaintextOrTerms)) {
|
|
2007
2069
|
return new BatchEncryptQueryOperation(this.client, plaintextOrTerms);
|
|
2008
2070
|
}
|
|
2009
2071
|
if (Array.isArray(plaintextOrTerms) && plaintextOrTerms.length === 0 && !opts) {
|
|
@@ -2050,6 +2112,13 @@ var EncryptionClient = class {
|
|
|
2050
2112
|
* .withLockContext(lockContext)
|
|
2051
2113
|
* ```
|
|
2052
2114
|
*
|
|
2115
|
+
* @remarks
|
|
2116
|
+
* The public input type rejects null, but at runtime `decrypt` will
|
|
2117
|
+
* short-circuit and return null when given a null ciphertext
|
|
2118
|
+
* (defense in depth for legacy / manually-NULLed DB rows reached via
|
|
2119
|
+
* casts or dynamic field walking). The narrow return type holds for
|
|
2120
|
+
* any caller that respects the input contract.
|
|
2121
|
+
*
|
|
2053
2122
|
* @see {@link LockContext}
|
|
2054
2123
|
* @see {@link DecryptOperation}
|
|
2055
2124
|
*/
|
|
@@ -2304,6 +2373,17 @@ var EncryptionClient = class {
|
|
|
2304
2373
|
return this.encryptConfig;
|
|
2305
2374
|
}
|
|
2306
2375
|
};
|
|
2376
|
+
var warnedStrategyDeprecated = false;
|
|
2377
|
+
function warnStrategyDeprecated() {
|
|
2378
|
+
if (warnedStrategyDeprecated) return;
|
|
2379
|
+
warnedStrategyDeprecated = true;
|
|
2380
|
+
console.warn(
|
|
2381
|
+
"[encryption]: `config.strategy` is deprecated and will be removed in a future release \u2014 use `config.authStrategy` instead."
|
|
2382
|
+
);
|
|
2383
|
+
}
|
|
2384
|
+
function __resetStrategyDeprecationWarningForTests() {
|
|
2385
|
+
warnedStrategyDeprecated = false;
|
|
2386
|
+
}
|
|
2307
2387
|
var Encryption = async (config) => {
|
|
2308
2388
|
const { schemas, config: clientConfig } = config;
|
|
2309
2389
|
if (!schemas.length) {
|
|
@@ -2316,11 +2396,18 @@ var Encryption = async (config) => {
|
|
|
2316
2396
|
"[encryption]: Invalid UUID provided for keyset id. Must be a valid UUID."
|
|
2317
2397
|
);
|
|
2318
2398
|
}
|
|
2399
|
+
if (clientConfig?.strategy) {
|
|
2400
|
+
warnStrategyDeprecated();
|
|
2401
|
+
}
|
|
2402
|
+
const authStrategy = clientConfig?.authStrategy ?? clientConfig?.strategy;
|
|
2319
2403
|
const client = new EncryptionClient();
|
|
2320
2404
|
const encryptConfig = buildEncryptConfig(...schemas);
|
|
2405
|
+
const eqlVersion = resolveEqlVersion(schemas, clientConfig?.eqlVersion);
|
|
2321
2406
|
const result = await client.init({
|
|
2322
2407
|
encryptConfig,
|
|
2323
|
-
...clientConfig
|
|
2408
|
+
...clientConfig,
|
|
2409
|
+
authStrategy,
|
|
2410
|
+
eqlVersion
|
|
2324
2411
|
});
|
|
2325
2412
|
if (result.failure) {
|
|
2326
2413
|
throw new Error(`[encryption]: ${result.failure.message}`);
|
|
@@ -2329,8 +2416,20 @@ var Encryption = async (config) => {
|
|
|
2329
2416
|
};
|
|
2330
2417
|
|
|
2331
2418
|
export {
|
|
2419
|
+
BatchEncryptQueryOperation,
|
|
2420
|
+
BulkDecryptOperation,
|
|
2421
|
+
BulkDecryptModelsOperation,
|
|
2422
|
+
BulkEncryptOperation,
|
|
2423
|
+
BulkEncryptModelsOperation,
|
|
2424
|
+
DecryptOperation,
|
|
2425
|
+
DecryptModelOperation,
|
|
2426
|
+
EncryptOperation,
|
|
2427
|
+
EncryptModelOperation,
|
|
2428
|
+
EncryptQueryOperation,
|
|
2332
2429
|
noClientError,
|
|
2430
|
+
resolveEqlVersion,
|
|
2333
2431
|
EncryptionClient,
|
|
2432
|
+
__resetStrategyDeprecationWarningForTests,
|
|
2334
2433
|
Encryption
|
|
2335
2434
|
};
|
|
2336
|
-
//# sourceMappingURL=chunk-
|
|
2435
|
+
//# sourceMappingURL=chunk-36AA7IBJ.js.map
|