@vollcrypt/db-guard 0.1.1 → 0.9.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/dist/blind-index.js +10 -6
- package/dist/drivers.d.ts +15 -0
- package/dist/drivers.js +218 -0
- package/dist/drizzle.js +5 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/kms.js +8 -2
- package/dist/mongoose.js +22 -12
- package/dist/prisma.js +35 -11
- package/dist/provenance.json +42 -14
- package/dist/provenance.json.sig +1 -2
- package/dist/sbom.json +46 -14
- package/dist/sbom.json.sig +0 -0
- package/dist/security.d.ts +3 -3
- package/dist/security.js +99 -76
- package/dist/typeorm.js +5 -2
- package/package.json +80 -50
package/dist/blind-index.js
CHANGED
|
@@ -15,10 +15,14 @@ function computeBlindIndex(value, rootSalt, columnName) {
|
|
|
15
15
|
const columnNameBuf = Buffer.from(columnName, 'utf8');
|
|
16
16
|
// 1. Derive column-specific key using HKDF-SHA256
|
|
17
17
|
const derivedColumnKey = (0, security_1.deriveHkdf)(rootSalt, null, columnNameBuf, 32);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
try {
|
|
19
|
+
// 2. Compute the final blind index using the derived column key
|
|
20
|
+
const plaintextBuf = Buffer.from(plaintext, 'utf8');
|
|
21
|
+
const blindIndex = (0, security_1.deriveHkdf)(derivedColumnKey, null, plaintextBuf, 32);
|
|
22
|
+
return blindIndex.toString('hex');
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
// 3. RAM Security: Zeroize the derived key immediately (Anti-Core Dump)
|
|
26
|
+
derivedColumnKey.fill(0);
|
|
27
|
+
}
|
|
24
28
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RateLimiterOptions } from './security.js';
|
|
2
|
+
export interface DbGuardDriverOptions {
|
|
3
|
+
key: Buffer | Record<string, Buffer>;
|
|
4
|
+
activeKeyVersion?: string;
|
|
5
|
+
entities: Record<string, string[]>;
|
|
6
|
+
cryptoRbac?: {
|
|
7
|
+
roles: Record<string, {
|
|
8
|
+
decrypt: string[];
|
|
9
|
+
mask?: Record<string, 'credit_card' | 'email' | 'tc_no' | ((v: any) => any) | string>;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
rateLimiter?: RateLimiterOptions;
|
|
13
|
+
}
|
|
14
|
+
export declare function wrapSqliteDatabase(db: any, options: DbGuardDriverOptions): any;
|
|
15
|
+
export declare function wrapOracleConnection(connection: any, options: DbGuardDriverOptions): any;
|
package/dist/drivers.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapSqliteDatabase = wrapSqliteDatabase;
|
|
4
|
+
exports.wrapOracleConnection = wrapOracleConnection;
|
|
5
|
+
const prisma_js_1 = require("./prisma.js");
|
|
6
|
+
const security_js_1 = require("./security.js");
|
|
7
|
+
function getKeys(options) {
|
|
8
|
+
let keys = {};
|
|
9
|
+
let activeVersion;
|
|
10
|
+
if (Buffer.isBuffer(options.key)) {
|
|
11
|
+
keys = { '1': Buffer.from(options.key) };
|
|
12
|
+
activeVersion = '1';
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
16
|
+
keys[v] = Buffer.from(k);
|
|
17
|
+
}
|
|
18
|
+
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
19
|
+
}
|
|
20
|
+
return { keys, activeVersion };
|
|
21
|
+
}
|
|
22
|
+
function cleanIdentifier(identifier) {
|
|
23
|
+
if (!identifier)
|
|
24
|
+
return identifier;
|
|
25
|
+
let cleaned = identifier.trim();
|
|
26
|
+
if ((cleaned.startsWith('"') && cleaned.endsWith('"')) ||
|
|
27
|
+
(cleaned.startsWith('`') && cleaned.endsWith('`')) ||
|
|
28
|
+
(cleaned.startsWith('[') && cleaned.endsWith(']'))) {
|
|
29
|
+
cleaned = cleaned.slice(1, -1);
|
|
30
|
+
}
|
|
31
|
+
return cleaned.trim();
|
|
32
|
+
}
|
|
33
|
+
function getParamColumns(sql) {
|
|
34
|
+
const sqlClean = sql.replace(/\s+/g, ' ').trim();
|
|
35
|
+
// Match INSERT INTO table (col1, col2) ...
|
|
36
|
+
const insertMatch = sqlClean.match(/INSERT\s+INTO\s+([a-zA-Z0-9_"`[\]]+)\s*\(([^)]+)\)/i);
|
|
37
|
+
if (insertMatch) {
|
|
38
|
+
const table = cleanIdentifier(insertMatch[1]);
|
|
39
|
+
const columns = insertMatch[2].split(',').map(c => cleanIdentifier(c));
|
|
40
|
+
return { table, columns };
|
|
41
|
+
}
|
|
42
|
+
// Match UPDATE table SET col1 = ?, col2 = ? ...
|
|
43
|
+
const updateMatch = sqlClean.match(/UPDATE\s+([a-zA-Z0-9_"`[\]]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE|$)/i);
|
|
44
|
+
if (updateMatch) {
|
|
45
|
+
const table = cleanIdentifier(updateMatch[1]);
|
|
46
|
+
const setParts = updateMatch[2].split(',');
|
|
47
|
+
const columns = [];
|
|
48
|
+
for (const part of setParts) {
|
|
49
|
+
const match = part.match(/([a-zA-Z0-9_"`[\]]+)\s*=/);
|
|
50
|
+
if (match) {
|
|
51
|
+
columns.push(cleanIdentifier(match[1]));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { table, columns };
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function decryptRow(row, table, keys, options) {
|
|
59
|
+
if (!row)
|
|
60
|
+
return row;
|
|
61
|
+
if (typeof row === 'object') {
|
|
62
|
+
const cloned = Array.isArray(row) ? [...row] : { ...row };
|
|
63
|
+
if (Array.isArray(row)) {
|
|
64
|
+
// Array format (index-based)
|
|
65
|
+
for (let i = 0; i < row.length; i++) {
|
|
66
|
+
const val = row[i];
|
|
67
|
+
if (typeof val === 'string' && val.startsWith('VOLLVALT:')) {
|
|
68
|
+
try {
|
|
69
|
+
cloned[i] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0, prisma_js_1.decryptValue)(v, keys), table, `column_${i}`, undefined, options);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Keep original on failure
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Object format (key-value)
|
|
79
|
+
const fields = options.entities[table] || [];
|
|
80
|
+
for (const [key, val] of Object.entries(row)) {
|
|
81
|
+
if (typeof val === 'string' && val.startsWith('VOLLVALT:')) {
|
|
82
|
+
try {
|
|
83
|
+
cloned[key] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0, prisma_js_1.decryptValue)(v, keys), table, key, row.id || row._id, options);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Keep original on failure
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return cloned;
|
|
92
|
+
}
|
|
93
|
+
return row;
|
|
94
|
+
}
|
|
95
|
+
function wrapSqliteDatabase(db, options) {
|
|
96
|
+
const { keys, activeVersion } = getKeys(options);
|
|
97
|
+
const activeKey = keys[activeVersion];
|
|
98
|
+
(0, security_js_1.registerKeysForZeroization)(keys);
|
|
99
|
+
const originalPrepare = db.prepare;
|
|
100
|
+
db.prepare = function (sql, ...args) {
|
|
101
|
+
const statement = originalPrepare.call(this, sql, ...args);
|
|
102
|
+
const parsed = getParamColumns(sql);
|
|
103
|
+
// Helper to encrypt query input parameters
|
|
104
|
+
const encryptParams = (params) => {
|
|
105
|
+
if (!parsed)
|
|
106
|
+
return params;
|
|
107
|
+
const table = parsed.table;
|
|
108
|
+
const columns = parsed.columns;
|
|
109
|
+
const fieldsToEncrypt = options.entities[table] || [];
|
|
110
|
+
if (fieldsToEncrypt.length === 0)
|
|
111
|
+
return params;
|
|
112
|
+
// Case 1: single array parameter, e.g., stmt.run([val1, val2])
|
|
113
|
+
if (params.length === 1 && Array.isArray(params[0])) {
|
|
114
|
+
const arrayParams = params[0].map((param, index) => {
|
|
115
|
+
const colName = columns[index];
|
|
116
|
+
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
117
|
+
return (0, prisma_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
118
|
+
}
|
|
119
|
+
return param;
|
|
120
|
+
});
|
|
121
|
+
return [arrayParams];
|
|
122
|
+
}
|
|
123
|
+
// Case 2: single object parameter for named binds, e.g., stmt.run({ col1: val1 })
|
|
124
|
+
if (params.length === 1 && params[0] && typeof params[0] === 'object' && !Buffer.isBuffer(params[0])) {
|
|
125
|
+
const obj = { ...params[0] };
|
|
126
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
127
|
+
// Strip prefix character (@, :, $) if present
|
|
128
|
+
const cleanKey = key.replace(/^[@:$]/, '');
|
|
129
|
+
if (fieldsToEncrypt.includes(cleanKey)) {
|
|
130
|
+
obj[key] = (0, prisma_js_1.encryptValue)(val, activeKey, activeVersion);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return [obj];
|
|
134
|
+
}
|
|
135
|
+
// Case 3: multiple positional parameters, e.g., stmt.run(val1, val2)
|
|
136
|
+
return params.map((param, index) => {
|
|
137
|
+
const colName = columns[index];
|
|
138
|
+
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
139
|
+
return (0, prisma_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
140
|
+
}
|
|
141
|
+
return param;
|
|
142
|
+
});
|
|
143
|
+
};
|
|
144
|
+
const wrapStatementMethod = (originalMethod) => {
|
|
145
|
+
return function (...params) {
|
|
146
|
+
const processedParams = encryptParams(params);
|
|
147
|
+
const result = originalMethod.apply(statement, processedParams);
|
|
148
|
+
if (parsed) {
|
|
149
|
+
const table = parsed.table;
|
|
150
|
+
if (Array.isArray(result)) {
|
|
151
|
+
return result.map(row => decryptRow(row, table, keys, options));
|
|
152
|
+
}
|
|
153
|
+
else if (result) {
|
|
154
|
+
return decryptRow(result, table, keys, options);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// If query SQL parsing was skipped (e.g. SELECT *), decrypt rows generically
|
|
159
|
+
// using first table configured in options as fallback
|
|
160
|
+
const defaultTable = Object.keys(options.entities)[0] || 'Model';
|
|
161
|
+
if (Array.isArray(result)) {
|
|
162
|
+
return result.map(row => decryptRow(row, defaultTable, keys, options));
|
|
163
|
+
}
|
|
164
|
+
else if (result) {
|
|
165
|
+
return decryptRow(result, defaultTable, keys, options);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
statement.run = wrapStatementMethod(statement.run);
|
|
172
|
+
statement.get = wrapStatementMethod(statement.get);
|
|
173
|
+
statement.all = wrapStatementMethod(statement.all);
|
|
174
|
+
return statement;
|
|
175
|
+
};
|
|
176
|
+
return db;
|
|
177
|
+
}
|
|
178
|
+
function wrapOracleConnection(connection, options) {
|
|
179
|
+
const { keys, activeVersion } = getKeys(options);
|
|
180
|
+
const activeKey = keys[activeVersion];
|
|
181
|
+
(0, security_js_1.registerKeysForZeroization)(keys);
|
|
182
|
+
const originalExecute = connection.execute;
|
|
183
|
+
connection.execute = async function (sql, bindParams = {}, execOptions = {}, ...args) {
|
|
184
|
+
const parsed = getParamColumns(sql);
|
|
185
|
+
let processedBinds = bindParams;
|
|
186
|
+
if (parsed) {
|
|
187
|
+
const table = parsed.table;
|
|
188
|
+
const columns = parsed.columns;
|
|
189
|
+
const fieldsToEncrypt = options.entities[table] || [];
|
|
190
|
+
if (fieldsToEncrypt.length > 0) {
|
|
191
|
+
if (Array.isArray(bindParams)) {
|
|
192
|
+
processedBinds = bindParams.map((param, index) => {
|
|
193
|
+
const colName = columns[index];
|
|
194
|
+
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
195
|
+
return (0, prisma_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
196
|
+
}
|
|
197
|
+
return param;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else if (bindParams && typeof bindParams === 'object') {
|
|
201
|
+
processedBinds = { ...bindParams };
|
|
202
|
+
for (const field of fieldsToEncrypt) {
|
|
203
|
+
if (processedBinds[field] !== undefined && processedBinds[field] !== null) {
|
|
204
|
+
processedBinds[field] = (0, prisma_js_1.encryptValue)(processedBinds[field], activeKey, activeVersion);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const result = await originalExecute.call(this, sql, processedBinds, execOptions, ...args);
|
|
211
|
+
if (result && result.rows) {
|
|
212
|
+
const targetTable = parsed ? parsed.table : (Object.keys(options.entities)[0] || 'Model');
|
|
213
|
+
result.rows = result.rows.map((row) => decryptRow(row, targetTable, keys, options));
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
};
|
|
217
|
+
return connection;
|
|
218
|
+
}
|
package/dist/drizzle.js
CHANGED
|
@@ -11,11 +11,14 @@ function getKeys(options) {
|
|
|
11
11
|
let keys;
|
|
12
12
|
let activeVersion;
|
|
13
13
|
if (Buffer.isBuffer(options.key)) {
|
|
14
|
-
keys = { '1': options.key };
|
|
14
|
+
keys = { '1': Buffer.from(options.key) };
|
|
15
15
|
activeVersion = '1';
|
|
16
16
|
}
|
|
17
17
|
else {
|
|
18
|
-
keys =
|
|
18
|
+
keys = {};
|
|
19
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
20
|
+
keys[v] = Buffer.from(k);
|
|
21
|
+
}
|
|
19
22
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
20
23
|
}
|
|
21
24
|
return { keys, activeVersion };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { prismaDbGuard, PrismaDbGuardOptions, encryptValue, decryptValue, resolv
|
|
|
2
2
|
export { mongooseDbGuard, MongooseDbGuardOptions } from './mongoose';
|
|
3
3
|
export { createDrizzleGuard } from './drizzle';
|
|
4
4
|
export { createTypeOrmSubscriber } from './typeorm';
|
|
5
|
+
export { wrapSqliteDatabase, wrapOracleConnection, DbGuardDriverOptions } from './drivers';
|
|
5
6
|
export { KmsProvider, AwsKmsProvider, GcpKmsProvider, VaultKmsProvider, unwrapDekLocal, Pkcs11KmsProvider } from './kms';
|
|
6
7
|
export { computeBlindIndex } from './blind-index';
|
|
7
8
|
export { dbGuardContextStore, configureAuditLogger, decryptWithSecurity, checkRateLimit, checkPageSize, resetFailClosedStatusForTesting, resetAuditLoggerForTesting, getCachedKey, setCachedKey, resetSecureKeyCacheForTesting, configureBreakGlass, deactivateBreakGlass, isBreakGlassActive, getBreakGlassKey, activateBreakGlass, parseCiphertext, CRYPTO_ALGORITHMS, VERSION_ALGORITHMS, maskValue } from './security';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateComplianceHtmlReport = exports.auditConfiguration = exports.maskValue = exports.VERSION_ALGORITHMS = exports.CRYPTO_ALGORITHMS = exports.parseCiphertext = exports.activateBreakGlass = exports.getBreakGlassKey = exports.isBreakGlassActive = exports.deactivateBreakGlass = exports.configureBreakGlass = exports.resetSecureKeyCacheForTesting = exports.setCachedKey = exports.getCachedKey = exports.resetAuditLoggerForTesting = exports.resetFailClosedStatusForTesting = exports.checkPageSize = exports.checkRateLimit = exports.decryptWithSecurity = exports.configureAuditLogger = exports.dbGuardContextStore = exports.computeBlindIndex = exports.Pkcs11KmsProvider = exports.unwrapDekLocal = exports.VaultKmsProvider = exports.GcpKmsProvider = exports.AwsKmsProvider = exports.createTypeOrmSubscriber = exports.createDrizzleGuard = exports.mongooseDbGuard = exports.resolveKeys = exports.decryptValue = exports.encryptValue = exports.prismaDbGuard = void 0;
|
|
3
|
+
exports.generateComplianceHtmlReport = exports.auditConfiguration = exports.maskValue = exports.VERSION_ALGORITHMS = exports.CRYPTO_ALGORITHMS = exports.parseCiphertext = exports.activateBreakGlass = exports.getBreakGlassKey = exports.isBreakGlassActive = exports.deactivateBreakGlass = exports.configureBreakGlass = exports.resetSecureKeyCacheForTesting = exports.setCachedKey = exports.getCachedKey = exports.resetAuditLoggerForTesting = exports.resetFailClosedStatusForTesting = exports.checkPageSize = exports.checkRateLimit = exports.decryptWithSecurity = exports.configureAuditLogger = exports.dbGuardContextStore = exports.computeBlindIndex = exports.Pkcs11KmsProvider = exports.unwrapDekLocal = exports.VaultKmsProvider = exports.GcpKmsProvider = exports.AwsKmsProvider = exports.wrapOracleConnection = exports.wrapSqliteDatabase = exports.createTypeOrmSubscriber = exports.createDrizzleGuard = exports.mongooseDbGuard = exports.resolveKeys = exports.decryptValue = exports.encryptValue = exports.prismaDbGuard = void 0;
|
|
4
4
|
var prisma_1 = require("./prisma");
|
|
5
5
|
Object.defineProperty(exports, "prismaDbGuard", { enumerable: true, get: function () { return prisma_1.prismaDbGuard; } });
|
|
6
6
|
Object.defineProperty(exports, "encryptValue", { enumerable: true, get: function () { return prisma_1.encryptValue; } });
|
|
@@ -12,6 +12,9 @@ var drizzle_1 = require("./drizzle");
|
|
|
12
12
|
Object.defineProperty(exports, "createDrizzleGuard", { enumerable: true, get: function () { return drizzle_1.createDrizzleGuard; } });
|
|
13
13
|
var typeorm_1 = require("./typeorm");
|
|
14
14
|
Object.defineProperty(exports, "createTypeOrmSubscriber", { enumerable: true, get: function () { return typeorm_1.createTypeOrmSubscriber; } });
|
|
15
|
+
var drivers_1 = require("./drivers");
|
|
16
|
+
Object.defineProperty(exports, "wrapSqliteDatabase", { enumerable: true, get: function () { return drivers_1.wrapSqliteDatabase; } });
|
|
17
|
+
Object.defineProperty(exports, "wrapOracleConnection", { enumerable: true, get: function () { return drivers_1.wrapOracleConnection; } });
|
|
15
18
|
var kms_1 = require("./kms");
|
|
16
19
|
Object.defineProperty(exports, "AwsKmsProvider", { enumerable: true, get: function () { return kms_1.AwsKmsProvider; } });
|
|
17
20
|
Object.defineProperty(exports, "GcpKmsProvider", { enumerable: true, get: function () { return kms_1.GcpKmsProvider; } });
|
package/dist/kms.js
CHANGED
|
@@ -106,7 +106,14 @@ class Pkcs11KmsProvider {
|
|
|
106
106
|
const pkcs11js = require('pkcs11js');
|
|
107
107
|
const pkcs11 = new pkcs11js.PKCS11();
|
|
108
108
|
pkcs11.load(this.config.libraryPath);
|
|
109
|
-
|
|
109
|
+
try {
|
|
110
|
+
pkcs11.C_Initialize();
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (!e.message?.includes('CRYPTOKI_ALREADY_INITIALIZED') && e.code !== 0x00000191 && e.code !== 401) {
|
|
114
|
+
throw e;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
110
117
|
const slots = pkcs11.C_GetSlotList(true);
|
|
111
118
|
const slotIndex = this.config.slotId !== undefined ? this.config.slotId : 0;
|
|
112
119
|
if (!slots || slots.length <= slotIndex) {
|
|
@@ -143,7 +150,6 @@ class Pkcs11KmsProvider {
|
|
|
143
150
|
finally {
|
|
144
151
|
pkcs11.C_Logout(session);
|
|
145
152
|
pkcs11.C_CloseSession(session);
|
|
146
|
-
pkcs11.C_Finalize();
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
catch (err) {
|
package/dist/mongoose.js
CHANGED
|
@@ -6,14 +6,16 @@ const blind_index_1 = require("./blind-index");
|
|
|
6
6
|
const security_1 = require("./security");
|
|
7
7
|
function mongooseDbGuard(schema, options) {
|
|
8
8
|
const { fields } = options;
|
|
9
|
-
let keys;
|
|
9
|
+
let keys = {};
|
|
10
10
|
let activeVersion;
|
|
11
11
|
if (Buffer.isBuffer(options.key)) {
|
|
12
|
-
keys = { '1': options.key };
|
|
12
|
+
keys = { '1': Buffer.from(options.key) };
|
|
13
13
|
activeVersion = '1';
|
|
14
14
|
}
|
|
15
15
|
else {
|
|
16
|
-
|
|
16
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
17
|
+
keys[v] = Buffer.from(k);
|
|
18
|
+
}
|
|
17
19
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
18
20
|
}
|
|
19
21
|
(0, security_1.registerKeysForZeroization)(keys);
|
|
@@ -28,11 +30,15 @@ function mongooseDbGuard(schema, options) {
|
|
|
28
30
|
return { keys: { '1': bgKey }, activeKey: bgKey, activeVersion: '1' };
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
|
-
if (
|
|
33
|
+
if (options.multiTenant && !tenantId) {
|
|
34
|
+
throw new Error("Vollcrypt Security: tenantId must be provided in multi-tenant mode.");
|
|
35
|
+
}
|
|
36
|
+
if (!options.multiTenant) {
|
|
32
37
|
return { keys, activeKey, activeVersion };
|
|
33
38
|
}
|
|
39
|
+
const tId = tenantId;
|
|
34
40
|
// Check Secure TTL Cache
|
|
35
|
-
const cachedActiveKey = (0, security_1.getCachedKey)(
|
|
41
|
+
const cachedActiveKey = (0, security_1.getCachedKey)(tId, activeVersion);
|
|
36
42
|
if (cachedActiveKey) {
|
|
37
43
|
return { keys: { [activeVersion]: cachedActiveKey }, activeKey: cachedActiveKey, activeVersion };
|
|
38
44
|
}
|
|
@@ -44,27 +50,31 @@ function mongooseDbGuard(schema, options) {
|
|
|
44
50
|
}
|
|
45
51
|
let tenantConfig;
|
|
46
52
|
if (multiTenant.tenants) {
|
|
47
|
-
tenantConfig = multiTenant.tenants[
|
|
53
|
+
tenantConfig = multiTenant.tenants[tId];
|
|
48
54
|
}
|
|
49
55
|
else if (multiTenant.getTenantConfig) {
|
|
50
|
-
tenantConfig = await multiTenant.getTenantConfig(
|
|
56
|
+
tenantConfig = await multiTenant.getTenantConfig(tId);
|
|
51
57
|
}
|
|
52
58
|
if (!tenantConfig) {
|
|
53
|
-
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${
|
|
59
|
+
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${tId}".`);
|
|
54
60
|
}
|
|
55
|
-
const
|
|
61
|
+
const resolvedTenantKeysRaw = await (0, prisma_1.resolveKeys)({
|
|
56
62
|
...options,
|
|
57
63
|
key: tenantConfig.key,
|
|
58
64
|
kms: tenantConfig.kms
|
|
59
65
|
});
|
|
60
|
-
|
|
66
|
+
const resolvedTenantKeys = {};
|
|
67
|
+
for (const [v, k] of Object.entries(resolvedTenantKeysRaw)) {
|
|
68
|
+
resolvedTenantKeys[v] = Buffer.from(k);
|
|
69
|
+
}
|
|
70
|
+
(0, security_1.registerKeysForZeroization)(resolvedTenantKeys, tId);
|
|
61
71
|
const tActiveVersion = tenantConfig.kms?.activeKeyVersion || '1';
|
|
62
72
|
const tActiveKey = resolvedTenantKeys[tActiveVersion];
|
|
63
73
|
if (!tActiveKey) {
|
|
64
|
-
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${
|
|
74
|
+
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${tId}".`);
|
|
65
75
|
}
|
|
66
76
|
for (const [ver, keyBuf] of Object.entries(resolvedTenantKeys)) {
|
|
67
|
-
(0, security_1.setCachedKey)(
|
|
77
|
+
(0, security_1.setCachedKey)(tId, ver, keyBuf);
|
|
68
78
|
}
|
|
69
79
|
return { keys: resolvedTenantKeys, activeKey: tActiveKey, activeVersion: tActiveVersion };
|
|
70
80
|
};
|
package/dist/prisma.js
CHANGED
|
@@ -65,6 +65,11 @@ async function resolveKeys(options) {
|
|
|
65
65
|
function encryptValue(val, key, version) {
|
|
66
66
|
if (val === null || val === undefined)
|
|
67
67
|
return val;
|
|
68
|
+
const context = security_1.dbGuardContextStore.getStore();
|
|
69
|
+
const tId = context?.tenantId || 'global';
|
|
70
|
+
if (key.every(b => b === 0) || (0, security_1.getFailClosedStatus)(tId)) {
|
|
71
|
+
throw new Error(`Vollcrypt Security: Fail-Closed mode is active for tenant "${tId}". Encryption blocked.`);
|
|
72
|
+
}
|
|
68
73
|
const plaintext = typeof val === 'string' ? val : JSON.stringify(val);
|
|
69
74
|
const plaintextBuf = Buffer.from(plaintext, 'utf8');
|
|
70
75
|
const encrypted = security_1.CRYPTO_ALGORITHMS['1'].encrypt(plaintextBuf, key);
|
|
@@ -85,6 +90,9 @@ function decryptValue(stored, keys) {
|
|
|
85
90
|
if (!key) {
|
|
86
91
|
throw new Error(`Decryption key version "${version}" not found in registered keys`);
|
|
87
92
|
}
|
|
93
|
+
if (key.every(b => b === 0)) {
|
|
94
|
+
throw new Error(`Vollcrypt Security: Decryption blocked. Key version "${version}" is zeroized due to a Fail-Closed event.`);
|
|
95
|
+
}
|
|
88
96
|
try {
|
|
89
97
|
const encryptedBuf = Buffer.from(base64Data, 'base64');
|
|
90
98
|
const decryptor = security_1.CRYPTO_ALGORITHMS[algoId];
|
|
@@ -169,10 +177,13 @@ const prismaDbGuard = (options, resolvedKeys) => {
|
|
|
169
177
|
if (!keys) {
|
|
170
178
|
if (options.key) {
|
|
171
179
|
if (Buffer.isBuffer(options.key)) {
|
|
172
|
-
keys = { '1': options.key };
|
|
180
|
+
keys = { '1': Buffer.from(options.key) };
|
|
173
181
|
}
|
|
174
182
|
else {
|
|
175
|
-
keys = {
|
|
183
|
+
keys = {};
|
|
184
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
185
|
+
keys[v] = Buffer.from(k);
|
|
186
|
+
}
|
|
176
187
|
}
|
|
177
188
|
}
|
|
178
189
|
else if (options.kms || options.multiTenant) {
|
|
@@ -183,6 +194,11 @@ const prismaDbGuard = (options, resolvedKeys) => {
|
|
|
183
194
|
}
|
|
184
195
|
}
|
|
185
196
|
if (keys) {
|
|
197
|
+
const clonedKeys = {};
|
|
198
|
+
for (const [v, k] of Object.entries(keys)) {
|
|
199
|
+
clonedKeys[v] = Buffer.from(k);
|
|
200
|
+
}
|
|
201
|
+
keys = clonedKeys;
|
|
186
202
|
(0, security_1.registerKeysForZeroization)(keys);
|
|
187
203
|
}
|
|
188
204
|
const activeVersion = options.kms?.activeKeyVersion || '1';
|
|
@@ -194,41 +210,49 @@ const prismaDbGuard = (options, resolvedKeys) => {
|
|
|
194
210
|
return { keys: { '1': bgKey }, activeKey: bgKey, activeVersion: '1' };
|
|
195
211
|
}
|
|
196
212
|
}
|
|
197
|
-
if (
|
|
213
|
+
if (options.multiTenant && !tenantId) {
|
|
214
|
+
throw new Error("Vollcrypt Security: tenantId must be provided in multi-tenant mode.");
|
|
215
|
+
}
|
|
216
|
+
if (!options.multiTenant) {
|
|
198
217
|
if (!keys || !activeKey) {
|
|
199
218
|
throw new Error("Vollcrypt Security: Global keys are not resolved.");
|
|
200
219
|
}
|
|
201
220
|
return { keys, activeKey, activeVersion };
|
|
202
221
|
}
|
|
222
|
+
const tId = tenantId;
|
|
203
223
|
// Check Secure TTL Cache
|
|
204
|
-
const cachedActiveKey = (0, security_1.getCachedKey)(
|
|
224
|
+
const cachedActiveKey = (0, security_1.getCachedKey)(tId, activeVersion);
|
|
205
225
|
if (cachedActiveKey) {
|
|
206
226
|
return { keys: { [activeVersion]: cachedActiveKey }, activeKey: cachedActiveKey, activeVersion };
|
|
207
227
|
}
|
|
208
228
|
// Cache miss: resolve configuration
|
|
209
229
|
let tenantConfig;
|
|
210
230
|
if (options.multiTenant.tenants) {
|
|
211
|
-
tenantConfig = options.multiTenant.tenants[
|
|
231
|
+
tenantConfig = options.multiTenant.tenants[tId];
|
|
212
232
|
}
|
|
213
233
|
else if (options.multiTenant.getTenantConfig) {
|
|
214
|
-
tenantConfig = await options.multiTenant.getTenantConfig(
|
|
234
|
+
tenantConfig = await options.multiTenant.getTenantConfig(tId);
|
|
215
235
|
}
|
|
216
236
|
if (!tenantConfig) {
|
|
217
|
-
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${
|
|
237
|
+
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${tId}".`);
|
|
218
238
|
}
|
|
219
|
-
const
|
|
239
|
+
const resolvedTenantKeysRaw = await resolveKeys({
|
|
220
240
|
...options,
|
|
221
241
|
key: tenantConfig.key,
|
|
222
242
|
kms: tenantConfig.kms
|
|
223
243
|
});
|
|
224
|
-
|
|
244
|
+
const resolvedTenantKeys = {};
|
|
245
|
+
for (const [v, k] of Object.entries(resolvedTenantKeysRaw)) {
|
|
246
|
+
resolvedTenantKeys[v] = Buffer.from(k);
|
|
247
|
+
}
|
|
248
|
+
(0, security_1.registerKeysForZeroization)(resolvedTenantKeys, tId);
|
|
225
249
|
const tActiveVersion = tenantConfig.kms?.activeKeyVersion || '1';
|
|
226
250
|
const tActiveKey = resolvedTenantKeys[tActiveVersion];
|
|
227
251
|
if (!tActiveKey) {
|
|
228
|
-
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${
|
|
252
|
+
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${tId}".`);
|
|
229
253
|
}
|
|
230
254
|
for (const [ver, keyBuf] of Object.entries(resolvedTenantKeys)) {
|
|
231
|
-
(0, security_1.setCachedKey)(
|
|
255
|
+
(0, security_1.setCachedKey)(tId, ver, keyBuf);
|
|
232
256
|
}
|
|
233
257
|
return { keys: resolvedTenantKeys, activeKey: tActiveKey, activeVersion: tActiveVersion };
|
|
234
258
|
};
|
package/dist/provenance.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "dist/blind-index.js",
|
|
12
12
|
"digest": {
|
|
13
|
-
"sha256": "
|
|
13
|
+
"sha256": "83364c9d2a86d43f775f15537ee10bdfb8e06c852adceb5b3c7358bcfc412907"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
{
|
|
@@ -37,6 +37,18 @@
|
|
|
37
37
|
"sha256": "0b660e3eafcc27a78d5c97c7d34837a25c68295660a3d1c7da0ae8e0a676bfb6"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
+
{
|
|
41
|
+
"name": "dist/drivers.d.ts",
|
|
42
|
+
"digest": {
|
|
43
|
+
"sha256": "1363416c7c4ce5549331a13c43f89c8cba2865b26cac8e699b4504c48c29b049"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "dist/drivers.js",
|
|
48
|
+
"digest": {
|
|
49
|
+
"sha256": "4b418fae086ee440bd1c9fe99328c86923ab83467ecfb70fd9bb4ebee324e7f6"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
40
52
|
{
|
|
41
53
|
"name": "dist/drizzle.d.ts",
|
|
42
54
|
"digest": {
|
|
@@ -46,19 +58,19 @@
|
|
|
46
58
|
{
|
|
47
59
|
"name": "dist/drizzle.js",
|
|
48
60
|
"digest": {
|
|
49
|
-
"sha256": "
|
|
61
|
+
"sha256": "c7b3210b0cca48f2b45c5b789cea13f55f14fc2c620e155d15987b9b8e434f4a"
|
|
50
62
|
}
|
|
51
63
|
},
|
|
52
64
|
{
|
|
53
65
|
"name": "dist/index.d.ts",
|
|
54
66
|
"digest": {
|
|
55
|
-
"sha256": "
|
|
67
|
+
"sha256": "a4864c4ea0bb9e9cc9bc1f58cdbc31863e5543a572e6049ee900b53b9fecb003"
|
|
56
68
|
}
|
|
57
69
|
},
|
|
58
70
|
{
|
|
59
71
|
"name": "dist/index.js",
|
|
60
72
|
"digest": {
|
|
61
|
-
"sha256": "
|
|
73
|
+
"sha256": "496292f1edd16bd779f6e8712291c4b90e5b3f404d71b54f5c369c6736c26814"
|
|
62
74
|
}
|
|
63
75
|
},
|
|
64
76
|
{
|
|
@@ -70,7 +82,7 @@
|
|
|
70
82
|
{
|
|
71
83
|
"name": "dist/kms.js",
|
|
72
84
|
"digest": {
|
|
73
|
-
"sha256": "
|
|
85
|
+
"sha256": "5c748722412b465c01cebd70e2242836f8f0c10f4303189548061f88d67eb4e4"
|
|
74
86
|
}
|
|
75
87
|
},
|
|
76
88
|
{
|
|
@@ -82,7 +94,7 @@
|
|
|
82
94
|
{
|
|
83
95
|
"name": "dist/mongoose.js",
|
|
84
96
|
"digest": {
|
|
85
|
-
"sha256": "
|
|
97
|
+
"sha256": "a2ff33065a1c52707bdc3b7b1b49e79c5878e7edc547e219779426d3376a8f5b"
|
|
86
98
|
}
|
|
87
99
|
},
|
|
88
100
|
{
|
|
@@ -94,19 +106,19 @@
|
|
|
94
106
|
{
|
|
95
107
|
"name": "dist/prisma.js",
|
|
96
108
|
"digest": {
|
|
97
|
-
"sha256": "
|
|
109
|
+
"sha256": "dc5d221dfc6e2f790fd13abc1408e98e4d8a3a1f61ee876cc691963d5517395a"
|
|
98
110
|
}
|
|
99
111
|
},
|
|
100
112
|
{
|
|
101
113
|
"name": "dist/security.d.ts",
|
|
102
114
|
"digest": {
|
|
103
|
-
"sha256": "
|
|
115
|
+
"sha256": "6fed9ab5d0e7d940fd8e0e65f3e0a6be6581fbeddb82a6ad9706397efa4ec2b2"
|
|
104
116
|
}
|
|
105
117
|
},
|
|
106
118
|
{
|
|
107
119
|
"name": "dist/security.js",
|
|
108
120
|
"digest": {
|
|
109
|
-
"sha256": "
|
|
121
|
+
"sha256": "a7323f84eaa90838dcaba18f82cba8c48d6fdb708715786cae1c60bdaeb0050d"
|
|
110
122
|
}
|
|
111
123
|
},
|
|
112
124
|
{
|
|
@@ -118,7 +130,7 @@
|
|
|
118
130
|
{
|
|
119
131
|
"name": "dist/typeorm.js",
|
|
120
132
|
"digest": {
|
|
121
|
-
"sha256": "
|
|
133
|
+
"sha256": "d88d0aa0eff62675e732c5fd2f7c7885d601d5234729687aecbd5b1282e6a7bd"
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
],
|
|
@@ -129,7 +141,7 @@
|
|
|
129
141
|
"externalParameters": {
|
|
130
142
|
"packageJson": {
|
|
131
143
|
"name": "@vollcrypt/db-guard",
|
|
132
|
-
"version": "0.
|
|
144
|
+
"version": "0.9.0",
|
|
133
145
|
"scripts": {
|
|
134
146
|
"build": "tsc && node scripts/generate-sbom.js",
|
|
135
147
|
"test": "node --import tsx --test tests/*.test.ts"
|
|
@@ -158,6 +170,22 @@
|
|
|
158
170
|
"uri": "pkg:npm/typeorm@0.3.0",
|
|
159
171
|
"digest": {}
|
|
160
172
|
},
|
|
173
|
+
{
|
|
174
|
+
"uri": "pkg:npm/@aws-sdk/client-kms@3.0.0",
|
|
175
|
+
"digest": {}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"uri": "pkg:npm/@google-cloud/kms@3.0.0",
|
|
179
|
+
"digest": {}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"uri": "pkg:npm/node-vault@0.9.0",
|
|
183
|
+
"digest": {}
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"uri": "pkg:npm/pkcs11js@1.0.0",
|
|
187
|
+
"digest": {}
|
|
188
|
+
},
|
|
161
189
|
{
|
|
162
190
|
"uri": "pkg:rust/aes-gcm",
|
|
163
191
|
"digest": {}
|
|
@@ -213,9 +241,9 @@
|
|
|
213
241
|
"id": "https://vollcrypt.dev/builders/local-hermetic-env"
|
|
214
242
|
},
|
|
215
243
|
"metadata": {
|
|
216
|
-
"invocationId": "
|
|
217
|
-
"startedOn": "2026-
|
|
218
|
-
"finishedOn": "2026-
|
|
244
|
+
"invocationId": "394b345f273d241cc656eb0efdf34f57",
|
|
245
|
+
"startedOn": "2026-07-08T14:31:16.765Z",
|
|
246
|
+
"finishedOn": "2026-07-08T14:31:16.767Z"
|
|
219
247
|
}
|
|
220
248
|
}
|
|
221
249
|
}
|
package/dist/provenance.json.sig
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
�����������bܤ�/W����>6
|
|
1
|
+
&�/�!/�[5_\4|�%�u����C>t}���Ɋ��kV[�3SI!�#� K�%#�cP��
|
package/dist/sbom.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:dc37a041-235d-478b-85b1-b33d761679d6",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2026-
|
|
7
|
+
"timestamp": "2026-07-08T14:31:16.765Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "Vollcrypt",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"component": {
|
|
16
|
-
"bomRef": "pkg:npm/@vollcrypt/db-guard@0.
|
|
16
|
+
"bomRef": "pkg:npm/@vollcrypt/db-guard@0.9.0",
|
|
17
17
|
"type": "library",
|
|
18
18
|
"name": "@vollcrypt/db-guard",
|
|
19
|
-
"version": "0.
|
|
19
|
+
"version": "0.9.0",
|
|
20
20
|
"description": "Database field-level encryption integrations for Vollcrypt (Prisma, Mongoose, Drizzle, TypeORM)",
|
|
21
21
|
"hashes": [
|
|
22
22
|
{
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"alg": "SHA-256",
|
|
28
|
-
"content": "
|
|
28
|
+
"content": "83364c9d2a86d43f775f15537ee10bdfb8e06c852adceb5b3c7358bcfc412907"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"alg": "SHA-256",
|
|
@@ -43,21 +43,29 @@
|
|
|
43
43
|
"alg": "SHA-256",
|
|
44
44
|
"content": "0b660e3eafcc27a78d5c97c7d34837a25c68295660a3d1c7da0ae8e0a676bfb6"
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
"alg": "SHA-256",
|
|
48
|
+
"content": "1363416c7c4ce5549331a13c43f89c8cba2865b26cac8e699b4504c48c29b049"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"alg": "SHA-256",
|
|
52
|
+
"content": "4b418fae086ee440bd1c9fe99328c86923ab83467ecfb70fd9bb4ebee324e7f6"
|
|
53
|
+
},
|
|
46
54
|
{
|
|
47
55
|
"alg": "SHA-256",
|
|
48
56
|
"content": "cb43fec66c7da1e84b96a97e8a18b0d08606535c64236f66ff950250d0f23292"
|
|
49
57
|
},
|
|
50
58
|
{
|
|
51
59
|
"alg": "SHA-256",
|
|
52
|
-
"content": "
|
|
60
|
+
"content": "c7b3210b0cca48f2b45c5b789cea13f55f14fc2c620e155d15987b9b8e434f4a"
|
|
53
61
|
},
|
|
54
62
|
{
|
|
55
63
|
"alg": "SHA-256",
|
|
56
|
-
"content": "
|
|
64
|
+
"content": "a4864c4ea0bb9e9cc9bc1f58cdbc31863e5543a572e6049ee900b53b9fecb003"
|
|
57
65
|
},
|
|
58
66
|
{
|
|
59
67
|
"alg": "SHA-256",
|
|
60
|
-
"content": "
|
|
68
|
+
"content": "496292f1edd16bd779f6e8712291c4b90e5b3f404d71b54f5c369c6736c26814"
|
|
61
69
|
},
|
|
62
70
|
{
|
|
63
71
|
"alg": "SHA-256",
|
|
@@ -65,7 +73,7 @@
|
|
|
65
73
|
},
|
|
66
74
|
{
|
|
67
75
|
"alg": "SHA-256",
|
|
68
|
-
"content": "
|
|
76
|
+
"content": "5c748722412b465c01cebd70e2242836f8f0c10f4303189548061f88d67eb4e4"
|
|
69
77
|
},
|
|
70
78
|
{
|
|
71
79
|
"alg": "SHA-256",
|
|
@@ -73,7 +81,7 @@
|
|
|
73
81
|
},
|
|
74
82
|
{
|
|
75
83
|
"alg": "SHA-256",
|
|
76
|
-
"content": "
|
|
84
|
+
"content": "a2ff33065a1c52707bdc3b7b1b49e79c5878e7edc547e219779426d3376a8f5b"
|
|
77
85
|
},
|
|
78
86
|
{
|
|
79
87
|
"alg": "SHA-256",
|
|
@@ -81,15 +89,15 @@
|
|
|
81
89
|
},
|
|
82
90
|
{
|
|
83
91
|
"alg": "SHA-256",
|
|
84
|
-
"content": "
|
|
92
|
+
"content": "dc5d221dfc6e2f790fd13abc1408e98e4d8a3a1f61ee876cc691963d5517395a"
|
|
85
93
|
},
|
|
86
94
|
{
|
|
87
95
|
"alg": "SHA-256",
|
|
88
|
-
"content": "
|
|
96
|
+
"content": "6fed9ab5d0e7d940fd8e0e65f3e0a6be6581fbeddb82a6ad9706397efa4ec2b2"
|
|
89
97
|
},
|
|
90
98
|
{
|
|
91
99
|
"alg": "SHA-256",
|
|
92
|
-
"content": "
|
|
100
|
+
"content": "a7323f84eaa90838dcaba18f82cba8c48d6fdb708715786cae1c60bdaeb0050d"
|
|
93
101
|
},
|
|
94
102
|
{
|
|
95
103
|
"alg": "SHA-256",
|
|
@@ -97,7 +105,7 @@
|
|
|
97
105
|
},
|
|
98
106
|
{
|
|
99
107
|
"alg": "SHA-256",
|
|
100
|
-
"content": "
|
|
108
|
+
"content": "d88d0aa0eff62675e732c5fd2f7c7885d601d5234729687aecbd5b1282e6a7bd"
|
|
101
109
|
}
|
|
102
110
|
]
|
|
103
111
|
},
|
|
@@ -133,6 +141,30 @@
|
|
|
133
141
|
"version": ">=0.3.0",
|
|
134
142
|
"purl": "pkg:npm/typeorm@0.3.0"
|
|
135
143
|
},
|
|
144
|
+
{
|
|
145
|
+
"type": "library",
|
|
146
|
+
"name": "@aws-sdk/client-kms",
|
|
147
|
+
"version": ">=3.0.0",
|
|
148
|
+
"purl": "pkg:npm/@aws-sdk/client-kms@3.0.0"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"type": "library",
|
|
152
|
+
"name": "@google-cloud/kms",
|
|
153
|
+
"version": ">=3.0.0",
|
|
154
|
+
"purl": "pkg:npm/@google-cloud/kms@3.0.0"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"type": "library",
|
|
158
|
+
"name": "node-vault",
|
|
159
|
+
"version": ">=0.9.0",
|
|
160
|
+
"purl": "pkg:npm/node-vault@0.9.0"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"type": "library",
|
|
164
|
+
"name": "pkcs11js",
|
|
165
|
+
"version": ">=1.0.0",
|
|
166
|
+
"purl": "pkg:npm/pkcs11js@1.0.0"
|
|
167
|
+
},
|
|
136
168
|
{
|
|
137
169
|
"type": "library",
|
|
138
170
|
"name": "rust-crate:aes-gcm",
|
package/dist/sbom.json.sig
CHANGED
|
Binary file
|
package/dist/security.d.ts
CHANGED
|
@@ -46,11 +46,11 @@ export declare function activateBreakGlass(signatures: {
|
|
|
46
46
|
signature: string;
|
|
47
47
|
timestamp: number;
|
|
48
48
|
}[], emergencyBackupKey: Buffer): void;
|
|
49
|
-
export declare function registerKeysForZeroization(keys: Record<string, Buffer
|
|
50
|
-
export declare function triggerFailClosed(onFailClosedCallback?: () => void): void;
|
|
49
|
+
export declare function registerKeysForZeroization(keys: Record<string, Buffer>, tenantId?: string): void;
|
|
50
|
+
export declare function triggerFailClosed(onFailClosedCallback?: () => void, tenantId?: string): void;
|
|
51
51
|
export declare function checkRateLimit(options?: RateLimiterOptions): void;
|
|
52
52
|
export declare function checkPageSize(count: number, options?: RateLimiterOptions): 'ok' | 'warn' | 'bypass' | 'error';
|
|
53
|
-
export declare function getFailClosedStatus(): boolean;
|
|
53
|
+
export declare function getFailClosedStatus(tenantId?: string): boolean;
|
|
54
54
|
export declare function resetFailClosedStatusForTesting(): void;
|
|
55
55
|
export interface AuditLogEntry {
|
|
56
56
|
timestamp: string;
|
package/dist/security.js
CHANGED
|
@@ -209,15 +209,14 @@ function maskValue(val, rule) {
|
|
|
209
209
|
return '***';
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
-
let decryptCount = 0;
|
|
213
|
-
let windowStart = Date.now();
|
|
214
|
-
let isFailClosed = false;
|
|
215
|
-
const globalKeysToZeroize = [];
|
|
216
212
|
// Ephemeral Master Key generated randomly on startup
|
|
217
213
|
let ephemeralMasterKey = crypto.randomBytes(32);
|
|
214
|
+
const tenantFailClosed = new Map();
|
|
215
|
+
const tenantKeys = new Map();
|
|
216
|
+
const tenantRateLimitStates = new Map();
|
|
218
217
|
const secureKeyCache = new Map();
|
|
219
218
|
function getCachedKey(tenantId, version) {
|
|
220
|
-
const cacheKey =
|
|
219
|
+
const cacheKey = JSON.stringify([tenantId || 'global', version]);
|
|
221
220
|
const entry = secureKeyCache.get(cacheKey);
|
|
222
221
|
if (!entry)
|
|
223
222
|
return undefined;
|
|
@@ -234,7 +233,7 @@ function getCachedKey(tenantId, version) {
|
|
|
234
233
|
}
|
|
235
234
|
}
|
|
236
235
|
function setCachedKey(tenantId, version, plaintextKey, ttlMs = 120000) {
|
|
237
|
-
const cacheKey =
|
|
236
|
+
const cacheKey = JSON.stringify([tenantId || 'global', version]);
|
|
238
237
|
const existing = secureKeyCache.get(cacheKey);
|
|
239
238
|
if (existing) {
|
|
240
239
|
existing.wrappedKey.fill(0);
|
|
@@ -327,27 +326,58 @@ function activateBreakGlass(signatures, emergencyBackupKey) {
|
|
|
327
326
|
isBreakGlassActiveFlag = true;
|
|
328
327
|
logDecryption('SYSTEM', 'BREAK_GLASS_ACTIVATED', undefined);
|
|
329
328
|
}
|
|
330
|
-
function registerKeysForZeroization(keys) {
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
function registerKeysForZeroization(keys, tenantId) {
|
|
330
|
+
const tId = tenantId || 'global';
|
|
331
|
+
let list = tenantKeys.get(tId);
|
|
332
|
+
if (!list) {
|
|
333
|
+
list = [];
|
|
334
|
+
tenantKeys.set(tId, list);
|
|
335
|
+
}
|
|
336
|
+
if (!list.includes(keys)) {
|
|
337
|
+
list.push(keys);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function triggerFailClosed(onFailClosedCallback, tenantId) {
|
|
341
|
+
const tId = tenantId || exports.dbGuardContextStore.getStore()?.tenantId || 'global';
|
|
342
|
+
tenantFailClosed.set(tId, true);
|
|
343
|
+
// Zeroize all registered keys immediately in memory for this tenant
|
|
344
|
+
const list = tenantKeys.get(tId);
|
|
345
|
+
if (list && list.length > 0) {
|
|
346
|
+
for (const keyMap of list) {
|
|
347
|
+
for (const key of Object.values(keyMap)) {
|
|
348
|
+
key.fill(0);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
333
351
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
352
|
+
else {
|
|
353
|
+
// Fallback to global keys if no tenant-specific keys are registered
|
|
354
|
+
const globalList = tenantKeys.get('global');
|
|
355
|
+
if (globalList) {
|
|
356
|
+
for (const keyMap of globalList) {
|
|
357
|
+
for (const key of Object.values(keyMap)) {
|
|
358
|
+
key.fill(0);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
341
361
|
}
|
|
342
362
|
}
|
|
343
363
|
// Zeroize cache and ephemeral master key
|
|
344
|
-
for (const entry of secureKeyCache.
|
|
345
|
-
|
|
364
|
+
for (const [cacheKey, entry] of secureKeyCache.entries()) {
|
|
365
|
+
try {
|
|
366
|
+
const parsed = JSON.parse(cacheKey);
|
|
367
|
+
if (Array.isArray(parsed) && parsed[0] === tId) {
|
|
368
|
+
entry.wrappedKey.fill(0);
|
|
369
|
+
secureKeyCache.delete(cacheKey);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// fallback
|
|
374
|
+
}
|
|
346
375
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
376
|
+
if (tId === 'global') {
|
|
377
|
+
ephemeralMasterKey.fill(0);
|
|
378
|
+
if (breakGlassEmergencyKey) {
|
|
379
|
+
breakGlassEmergencyKey.fill(0);
|
|
380
|
+
}
|
|
351
381
|
}
|
|
352
382
|
if (onFailClosedCallback) {
|
|
353
383
|
try {
|
|
@@ -357,59 +387,45 @@ function triggerFailClosed(onFailClosedCallback) {
|
|
|
357
387
|
// prevent user callback crash from blocking zeroization
|
|
358
388
|
}
|
|
359
389
|
}
|
|
360
|
-
throw new Error(
|
|
390
|
+
throw new Error(`Vollcrypt Security: Decryption rate limit exceeded. Fail-Closed mode triggered for tenant "${tId}". Keys zeroized.`);
|
|
361
391
|
}
|
|
362
392
|
function checkRateLimit(options) {
|
|
363
|
-
if (isFailClosed) {
|
|
364
|
-
throw new Error('Vollcrypt Security: Fail-Closed mode is active. Decryption blocked.');
|
|
365
|
-
}
|
|
366
393
|
const context = exports.dbGuardContextStore.getStore();
|
|
394
|
+
const tId = context?.tenantId || 'global';
|
|
395
|
+
if (tenantFailClosed.get(tId)) {
|
|
396
|
+
throw new Error(`Vollcrypt Security: Fail-Closed mode is active for tenant "${tId}". Decryption blocked.`);
|
|
397
|
+
}
|
|
367
398
|
if (context?.bypassRateLimit) {
|
|
368
399
|
return; // Rate limit check bypassed for this request context
|
|
369
400
|
}
|
|
370
401
|
const limit = context?.maxDecryptionsPerSecond || options?.maxDecryptionsPerSecond || 500;
|
|
371
402
|
const mode = context?.rateLimiterMode || options?.mode || 'fail_closed';
|
|
372
403
|
const now = Date.now();
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
404
|
+
let state = tenantRateLimitStates.get(tId);
|
|
405
|
+
if (!state) {
|
|
406
|
+
state = { decryptCount: 0, windowStart: now };
|
|
407
|
+
tenantRateLimitStates.set(tId, state);
|
|
408
|
+
}
|
|
409
|
+
if (now - state.windowStart > 1000) {
|
|
410
|
+
state.decryptCount = 0;
|
|
411
|
+
state.windowStart = now;
|
|
412
|
+
}
|
|
413
|
+
state.decryptCount++;
|
|
414
|
+
if (state.decryptCount > limit) {
|
|
415
|
+
if (mode === 'fail_closed') {
|
|
416
|
+
triggerFailClosed(options?.onFailClosed, tId);
|
|
377
417
|
}
|
|
378
|
-
if (
|
|
379
|
-
|
|
380
|
-
context.windowStart = now;
|
|
381
|
-
}
|
|
382
|
-
context.decryptCount++;
|
|
383
|
-
if (context.decryptCount > limit) {
|
|
384
|
-
if (mode === 'fail_closed') {
|
|
385
|
-
triggerFailClosed(options?.onFailClosed);
|
|
386
|
-
}
|
|
387
|
-
else if (mode === 'warn') {
|
|
388
|
-
console.warn(`Vollcrypt Warning: Decryption rate limit exceeded. ${context.decryptCount} decryptions in the current window (limit: ${limit}).`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
if (now - windowStart > 1000) {
|
|
394
|
-
decryptCount = 0;
|
|
395
|
-
windowStart = now;
|
|
396
|
-
}
|
|
397
|
-
decryptCount++;
|
|
398
|
-
if (decryptCount > limit) {
|
|
399
|
-
if (mode === 'fail_closed') {
|
|
400
|
-
triggerFailClosed(options?.onFailClosed);
|
|
401
|
-
}
|
|
402
|
-
else if (mode === 'warn') {
|
|
403
|
-
console.warn(`Vollcrypt Warning: Decryption rate limit exceeded. ${decryptCount} decryptions in the current window (limit: ${limit}).`);
|
|
404
|
-
}
|
|
418
|
+
else if (mode === 'warn') {
|
|
419
|
+
console.warn(`Vollcrypt Warning: Decryption rate limit exceeded for tenant "${tId}". ${state.decryptCount} decryptions in the current window (limit: ${limit}).`);
|
|
405
420
|
}
|
|
406
421
|
}
|
|
407
422
|
}
|
|
408
423
|
function checkPageSize(count, options) {
|
|
409
|
-
if (isFailClosed) {
|
|
410
|
-
throw new Error('Vollcrypt Security: Fail-Closed mode is active. Decryption blocked.');
|
|
411
|
-
}
|
|
412
424
|
const context = exports.dbGuardContextStore.getStore();
|
|
425
|
+
const tId = context?.tenantId || 'global';
|
|
426
|
+
if (tenantFailClosed.get(tId)) {
|
|
427
|
+
throw new Error(`Vollcrypt Security: Fail-Closed mode is active for tenant "${tId}". Decryption blocked.`);
|
|
428
|
+
}
|
|
413
429
|
const maxPageSize = context?.maxPageSize !== undefined
|
|
414
430
|
? context.maxPageSize
|
|
415
431
|
: (options?.maxPageSize !== undefined ? options.maxPageSize : 250);
|
|
@@ -430,17 +446,19 @@ function checkPageSize(count, options) {
|
|
|
430
446
|
}
|
|
431
447
|
return 'ok';
|
|
432
448
|
}
|
|
433
|
-
function getFailClosedStatus() {
|
|
434
|
-
|
|
449
|
+
function getFailClosedStatus(tenantId) {
|
|
450
|
+
const tId = tenantId || exports.dbGuardContextStore.getStore()?.tenantId || 'global';
|
|
451
|
+
return tenantFailClosed.get(tId) || false;
|
|
435
452
|
}
|
|
436
453
|
function resetFailClosedStatusForTesting() {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
454
|
+
tenantFailClosed.clear();
|
|
455
|
+
tenantRateLimitStates.clear();
|
|
456
|
+
tenantKeys.clear();
|
|
440
457
|
}
|
|
441
458
|
let lastLogHash = '0'.repeat(64);
|
|
442
459
|
let auditLogPath;
|
|
443
460
|
let onAuditLogCallback;
|
|
461
|
+
let auditWriteQueue = Promise.resolve();
|
|
444
462
|
function configureAuditLogger(options) {
|
|
445
463
|
if (options?.path) {
|
|
446
464
|
auditLogPath = options.path;
|
|
@@ -470,6 +488,7 @@ function resetAuditLoggerForTesting() {
|
|
|
470
488
|
lastLogHash = '0'.repeat(64);
|
|
471
489
|
auditLogPath = undefined;
|
|
472
490
|
onAuditLogCallback = undefined;
|
|
491
|
+
auditWriteQueue = Promise.resolve();
|
|
473
492
|
}
|
|
474
493
|
function logDecryption(model, field, recordId) {
|
|
475
494
|
const context = exports.dbGuardContextStore.getStore();
|
|
@@ -497,12 +516,11 @@ function logDecryption(model, field, recordId) {
|
|
|
497
516
|
}
|
|
498
517
|
}
|
|
499
518
|
if (auditLogPath) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
519
|
+
const line = JSON.stringify(fullEntry) + '\n';
|
|
520
|
+
const currentPath = auditLogPath;
|
|
521
|
+
auditWriteQueue = auditWriteQueue.then(() => {
|
|
522
|
+
return fs.promises.appendFile(currentPath, line, 'utf8').catch(() => { });
|
|
523
|
+
});
|
|
506
524
|
}
|
|
507
525
|
}
|
|
508
526
|
function decryptWithSecurity(stored, decryptRawFn, modelName, fieldName, recordId, options) {
|
|
@@ -543,7 +561,8 @@ function decryptWithSecurity(stored, decryptRawFn, modelName, fieldName, recordI
|
|
|
543
561
|
return result;
|
|
544
562
|
}
|
|
545
563
|
exports.VERSION_ALGORITHMS = {
|
|
546
|
-
'1': '1'
|
|
564
|
+
'1': '1',
|
|
565
|
+
'2': '1'
|
|
547
566
|
};
|
|
548
567
|
exports.CRYPTO_ALGORITHMS = {
|
|
549
568
|
'1': {
|
|
@@ -557,12 +576,16 @@ function parseCiphertext(stored) {
|
|
|
557
576
|
const content = stored.slice('VOLLVALT:'.length);
|
|
558
577
|
if (content.startsWith('v')) {
|
|
559
578
|
const colon = content.indexOf(':');
|
|
560
|
-
if (colon === -1)
|
|
561
|
-
|
|
579
|
+
if (colon === -1) {
|
|
580
|
+
throw new Error("Vollcrypt Security: Malformed ciphertext format.");
|
|
581
|
+
}
|
|
562
582
|
const versionPart = content.slice(1, colon);
|
|
563
583
|
const base64Part = content.slice(colon + 1);
|
|
564
|
-
const algoId = exports.VERSION_ALGORITHMS[versionPart]
|
|
584
|
+
const algoId = exports.VERSION_ALGORITHMS[versionPart];
|
|
585
|
+
if (!algoId) {
|
|
586
|
+
throw new Error(`Vollcrypt Security: Deprecated or unsupported encryption version "v${versionPart}".`);
|
|
587
|
+
}
|
|
565
588
|
return { algoId, version: versionPart, base64Data: base64Part };
|
|
566
589
|
}
|
|
567
|
-
|
|
590
|
+
throw new Error("Vollcrypt Security: Legacy unversioned ciphertexts are deprecated and unsupported.");
|
|
568
591
|
}
|
package/dist/typeorm.js
CHANGED
|
@@ -43,11 +43,14 @@ function getKeys(options) {
|
|
|
43
43
|
let keys;
|
|
44
44
|
let activeVersion;
|
|
45
45
|
if (Buffer.isBuffer(options.key)) {
|
|
46
|
-
keys = { '1': options.key };
|
|
46
|
+
keys = { '1': Buffer.from(options.key) };
|
|
47
47
|
activeVersion = '1';
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
|
-
keys =
|
|
50
|
+
keys = {};
|
|
51
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
52
|
+
keys[v] = Buffer.from(k);
|
|
53
|
+
}
|
|
51
54
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
52
55
|
}
|
|
53
56
|
return { keys, activeVersion };
|
package/package.json
CHANGED
|
@@ -1,50 +1,80 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@vollcrypt/db-guard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Database field-level encryption integrations for Vollcrypt (Prisma, Mongoose, Drizzle, TypeORM)",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"bin": {
|
|
8
|
-
"vollcrypt-db-guard": "dist/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"README.md",
|
|
13
|
-
"compliance-config.json"
|
|
14
|
-
],
|
|
15
|
-
"publishConfig": {
|
|
16
|
-
"access": "public"
|
|
17
|
-
},
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/BeratVural/vollcrypt.git",
|
|
21
|
-
"directory": "db-guard/node"
|
|
22
|
-
},
|
|
23
|
-
"author": "Berat Vural <berat.vural.tr@gmail.com>",
|
|
24
|
-
"license": "GPL-3.0-only OR LicenseRef-Commercial",
|
|
25
|
-
"bugs": {
|
|
26
|
-
"url": "https://github.com/BeratVural/vollcrypt/issues"
|
|
27
|
-
},
|
|
28
|
-
"homepage": "https://github.com/BeratVural/vollcrypt/tree/main/db-guard/node#readme",
|
|
29
|
-
"scripts": {
|
|
30
|
-
"build": "tsc && node scripts/generate-sbom.js",
|
|
31
|
-
"test": "node --import tsx --test tests/*.test.ts"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {},
|
|
34
|
-
"peerDependencies": {
|
|
35
|
-
"@prisma/client": ">=4.7.0",
|
|
36
|
-
"mongoose": ">=6.0.0",
|
|
37
|
-
"drizzle-orm": ">=0.28.0",
|
|
38
|
-
"typeorm": ">=0.3.0"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@vollcrypt/db-guard",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Database field-level encryption integrations for Vollcrypt (Prisma, Mongoose, Drizzle, TypeORM)",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"vollcrypt-db-guard": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"compliance-config.json"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/BeratVural/vollcrypt.git",
|
|
21
|
+
"directory": "db-guard/node"
|
|
22
|
+
},
|
|
23
|
+
"author": "Berat Vural <berat.vural.tr@gmail.com>",
|
|
24
|
+
"license": "GPL-3.0-only OR LicenseRef-Commercial",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/BeratVural/vollcrypt/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/BeratVural/vollcrypt/tree/main/db-guard/node#readme",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc && node scripts/generate-sbom.js",
|
|
31
|
+
"test": "node --import tsx --test tests/*.test.ts"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@prisma/client": ">=4.7.0",
|
|
36
|
+
"mongoose": ">=6.0.0",
|
|
37
|
+
"drizzle-orm": ">=0.28.0",
|
|
38
|
+
"typeorm": ">=0.3.0",
|
|
39
|
+
"@aws-sdk/client-kms": ">=3.0.0",
|
|
40
|
+
"@google-cloud/kms": ">=3.0.0",
|
|
41
|
+
"node-vault": ">=0.9.0",
|
|
42
|
+
"pkcs11js": ">=1.0.0"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"@prisma/client": {
|
|
46
|
+
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"mongoose": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"drizzle-orm": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"typeorm": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@aws-sdk/client-kms": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@google-cloud/kms": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"node-vault": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"pkcs11js": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@prisma/client": "^5.0.0",
|
|
72
|
+
"@types/node": "^20.11.0",
|
|
73
|
+
"mongoose": "^8.0.0",
|
|
74
|
+
"prisma": "^5.0.0",
|
|
75
|
+
"drizzle-orm": "^0.30.0",
|
|
76
|
+
"typeorm": "^0.3.20",
|
|
77
|
+
"tsx": "^4.7.0",
|
|
78
|
+
"typescript": "^5.5.2"
|
|
79
|
+
}
|
|
80
|
+
}
|