@vollcrypt/db-guard 0.1.2 → 0.9.1
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/LICENSE +16 -0
- package/LICENSE-COMMERCIAL.md +71 -0
- package/LICENSE-GPL +674 -0
- package/dist/blind-index.d.ts +1 -7
- package/dist/blind-index.js +3 -22
- package/dist/cli.js +3 -3
- package/dist/drivers.js +52 -16
- package/dist/drizzle.js +23 -22
- package/dist/index.d.ts +2 -6
- package/dist/index.js +4 -12
- package/dist/kms.d.ts +10 -0
- package/dist/kms.js +58 -2
- package/dist/mongoose.d.ts +1 -1
- package/dist/mongoose.js +33 -24
- package/dist/prisma.d.ts +4 -24
- package/dist/prisma.js +44 -176
- package/dist/provenance.json +37 -21
- package/dist/provenance.json.sig +2 -1
- package/dist/sbom.json +45 -21
- package/dist/sbom.json.sig +1 -2
- package/dist/security.d.ts +11 -3
- package/dist/security.js +223 -76
- package/dist/typeorm.d.ts +1 -1
- package/dist/typeorm.js +12 -11
- package/package.json +105 -50
package/dist/blind-index.js
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.computeBlindIndex =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Computes a hardened, frequency-resistant blind index for a database field.
|
|
7
|
-
*
|
|
8
|
-
* Uses HKDF-SHA256 to derive a unique column key from the root salt,
|
|
9
|
-
* preventing cross-column frequency analysis. Zeroizes intermediate keys immediately.
|
|
10
|
-
*/
|
|
11
|
-
function computeBlindIndex(value, rootSalt, columnName) {
|
|
12
|
-
if (value === null || value === undefined)
|
|
13
|
-
return value;
|
|
14
|
-
const plaintext = typeof value === 'string' ? value : JSON.stringify(value);
|
|
15
|
-
const columnNameBuf = Buffer.from(columnName, 'utf8');
|
|
16
|
-
// 1. Derive column-specific key using HKDF-SHA256
|
|
17
|
-
const derivedColumnKey = (0, security_1.deriveHkdf)(rootSalt, null, columnNameBuf, 32);
|
|
18
|
-
// 2. Compute the final blind index using the derived column key
|
|
19
|
-
const plaintextBuf = Buffer.from(plaintext, 'utf8');
|
|
20
|
-
const blindIndex = (0, security_1.deriveHkdf)(derivedColumnKey, null, plaintextBuf, 32);
|
|
21
|
-
// 3. RAM Security: Zeroize the derived key immediately (Anti-Core Dump)
|
|
22
|
-
derivedColumnKey.fill(0);
|
|
23
|
-
return blindIndex.toString('hex');
|
|
24
|
-
}
|
|
3
|
+
exports.computeBlindIndex = void 0;
|
|
4
|
+
var security_1 = require("./security");
|
|
5
|
+
Object.defineProperty(exports, "computeBlindIndex", { enumerable: true, get: function () { return security_1.computeBlindIndex; } });
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const
|
|
4
|
+
const security_1 = require("./security");
|
|
5
5
|
function printProgressBar(current, total) {
|
|
6
6
|
const percentage = Math.min(100, Math.floor((current / total) * 100));
|
|
7
7
|
const barLength = 40;
|
|
@@ -138,7 +138,7 @@ async function migratePostgres(url, table, column, idCol, key, version, chunkSiz
|
|
|
138
138
|
// 3. Encrypt and update each row
|
|
139
139
|
for (const row of batchRes.rows) {
|
|
140
140
|
const rawVal = row[column];
|
|
141
|
-
const encryptedVal = (0,
|
|
141
|
+
const encryptedVal = (0, security_1.encryptValue)(rawVal, key, version);
|
|
142
142
|
await client.query(`UPDATE "${table}" SET "${column}" = $1 WHERE "${idCol}" = $2`, [encryptedVal, row[idCol]]);
|
|
143
143
|
processed++;
|
|
144
144
|
printProgressBar(processed, total);
|
|
@@ -190,7 +190,7 @@ async function migrateMongo(url, collectionName, field, idCol, key, version, chu
|
|
|
190
190
|
}
|
|
191
191
|
for (const doc of batch) {
|
|
192
192
|
const rawVal = doc[field];
|
|
193
|
-
const encryptedVal = (0,
|
|
193
|
+
const encryptedVal = (0, security_1.encryptValue)(rawVal, key, version);
|
|
194
194
|
await collection.updateOne({ [idCol]: doc[idCol] }, { $set: { [field]: encryptedVal } });
|
|
195
195
|
processed++;
|
|
196
196
|
printProgressBar(processed, total);
|
package/dist/drivers.js
CHANGED
|
@@ -2,40 +2,52 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.wrapSqliteDatabase = wrapSqliteDatabase;
|
|
4
4
|
exports.wrapOracleConnection = wrapOracleConnection;
|
|
5
|
-
const prisma_js_1 = require("./prisma.js");
|
|
6
5
|
const security_js_1 = require("./security.js");
|
|
7
6
|
function getKeys(options) {
|
|
8
|
-
let keys;
|
|
7
|
+
let keys = {};
|
|
9
8
|
let activeVersion;
|
|
10
9
|
if (Buffer.isBuffer(options.key)) {
|
|
11
|
-
keys = { '1': options.key };
|
|
10
|
+
keys = { '1': Buffer.from(options.key) };
|
|
12
11
|
activeVersion = '1';
|
|
13
12
|
}
|
|
14
13
|
else {
|
|
15
|
-
|
|
14
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
15
|
+
keys[v] = Buffer.from(k);
|
|
16
|
+
}
|
|
16
17
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
17
18
|
}
|
|
18
19
|
return { keys, activeVersion };
|
|
19
20
|
}
|
|
21
|
+
function cleanIdentifier(identifier) {
|
|
22
|
+
if (!identifier)
|
|
23
|
+
return identifier;
|
|
24
|
+
let cleaned = identifier.trim();
|
|
25
|
+
if ((cleaned.startsWith('"') && cleaned.endsWith('"')) ||
|
|
26
|
+
(cleaned.startsWith('`') && cleaned.endsWith('`')) ||
|
|
27
|
+
(cleaned.startsWith('[') && cleaned.endsWith(']'))) {
|
|
28
|
+
cleaned = cleaned.slice(1, -1);
|
|
29
|
+
}
|
|
30
|
+
return cleaned.trim();
|
|
31
|
+
}
|
|
20
32
|
function getParamColumns(sql) {
|
|
21
33
|
const sqlClean = sql.replace(/\s+/g, ' ').trim();
|
|
22
34
|
// Match INSERT INTO table (col1, col2) ...
|
|
23
|
-
const insertMatch = sqlClean.match(/INSERT\s+INTO\s+(\
|
|
35
|
+
const insertMatch = sqlClean.match(/INSERT\s+INTO\s+([a-zA-Z0-9_"`[\]]+)\s*\(([^)]+)\)/i);
|
|
24
36
|
if (insertMatch) {
|
|
25
|
-
const table = insertMatch[1];
|
|
26
|
-
const columns = insertMatch[2].split(',').map(c => c
|
|
37
|
+
const table = cleanIdentifier(insertMatch[1]);
|
|
38
|
+
const columns = insertMatch[2].split(',').map(c => cleanIdentifier(c));
|
|
27
39
|
return { table, columns };
|
|
28
40
|
}
|
|
29
41
|
// Match UPDATE table SET col1 = ?, col2 = ? ...
|
|
30
|
-
const updateMatch = sqlClean.match(/UPDATE\s+(\
|
|
42
|
+
const updateMatch = sqlClean.match(/UPDATE\s+([a-zA-Z0-9_"`[\]]+)\s+SET\s+([\s\S]+?)(?:\s+WHERE|$)/i);
|
|
31
43
|
if (updateMatch) {
|
|
32
|
-
const table = updateMatch[1];
|
|
44
|
+
const table = cleanIdentifier(updateMatch[1]);
|
|
33
45
|
const setParts = updateMatch[2].split(',');
|
|
34
46
|
const columns = [];
|
|
35
47
|
for (const part of setParts) {
|
|
36
|
-
const match = part.match(/(\
|
|
48
|
+
const match = part.match(/([a-zA-Z0-9_"`[\]]+)\s*=/);
|
|
37
49
|
if (match) {
|
|
38
|
-
columns.push(match[1]);
|
|
50
|
+
columns.push(cleanIdentifier(match[1]));
|
|
39
51
|
}
|
|
40
52
|
}
|
|
41
53
|
return { table, columns };
|
|
@@ -53,7 +65,7 @@ function decryptRow(row, table, keys, options) {
|
|
|
53
65
|
const val = row[i];
|
|
54
66
|
if (typeof val === 'string' && val.startsWith('VOLLVALT:')) {
|
|
55
67
|
try {
|
|
56
|
-
cloned[i] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0,
|
|
68
|
+
cloned[i] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0, security_js_1.decryptValue)(v, keys), table, `column_${i}`, undefined, options);
|
|
57
69
|
}
|
|
58
70
|
catch {
|
|
59
71
|
// Keep original on failure
|
|
@@ -67,7 +79,7 @@ function decryptRow(row, table, keys, options) {
|
|
|
67
79
|
for (const [key, val] of Object.entries(row)) {
|
|
68
80
|
if (typeof val === 'string' && val.startsWith('VOLLVALT:')) {
|
|
69
81
|
try {
|
|
70
|
-
cloned[key] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0,
|
|
82
|
+
cloned[key] = (0, security_js_1.decryptWithSecurity)(val, (v) => (0, security_js_1.decryptValue)(v, keys), table, key, row.id || row._id, options);
|
|
71
83
|
}
|
|
72
84
|
catch {
|
|
73
85
|
// Keep original on failure
|
|
@@ -96,10 +108,34 @@ function wrapSqliteDatabase(db, options) {
|
|
|
96
108
|
const fieldsToEncrypt = options.entities[table] || [];
|
|
97
109
|
if (fieldsToEncrypt.length === 0)
|
|
98
110
|
return params;
|
|
111
|
+
// Case 1: single array parameter, e.g., stmt.run([val1, val2])
|
|
112
|
+
if (params.length === 1 && Array.isArray(params[0])) {
|
|
113
|
+
const arrayParams = params[0].map((param, index) => {
|
|
114
|
+
const colName = columns[index];
|
|
115
|
+
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
116
|
+
return (0, security_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
117
|
+
}
|
|
118
|
+
return param;
|
|
119
|
+
});
|
|
120
|
+
return [arrayParams];
|
|
121
|
+
}
|
|
122
|
+
// Case 2: single object parameter for named binds, e.g., stmt.run({ col1: val1 })
|
|
123
|
+
if (params.length === 1 && params[0] && typeof params[0] === 'object' && !Buffer.isBuffer(params[0])) {
|
|
124
|
+
const obj = { ...params[0] };
|
|
125
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
126
|
+
// Strip prefix character (@, :, $) if present
|
|
127
|
+
const cleanKey = key.replace(/^[@:$]/, '');
|
|
128
|
+
if (fieldsToEncrypt.includes(cleanKey)) {
|
|
129
|
+
obj[key] = (0, security_js_1.encryptValue)(val, activeKey, activeVersion);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [obj];
|
|
133
|
+
}
|
|
134
|
+
// Case 3: multiple positional parameters, e.g., stmt.run(val1, val2)
|
|
99
135
|
return params.map((param, index) => {
|
|
100
136
|
const colName = columns[index];
|
|
101
137
|
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
102
|
-
return (0,
|
|
138
|
+
return (0, security_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
103
139
|
}
|
|
104
140
|
return param;
|
|
105
141
|
});
|
|
@@ -155,7 +191,7 @@ function wrapOracleConnection(connection, options) {
|
|
|
155
191
|
processedBinds = bindParams.map((param, index) => {
|
|
156
192
|
const colName = columns[index];
|
|
157
193
|
if (colName && fieldsToEncrypt.includes(colName)) {
|
|
158
|
-
return (0,
|
|
194
|
+
return (0, security_js_1.encryptValue)(param, activeKey, activeVersion);
|
|
159
195
|
}
|
|
160
196
|
return param;
|
|
161
197
|
});
|
|
@@ -164,7 +200,7 @@ function wrapOracleConnection(connection, options) {
|
|
|
164
200
|
processedBinds = { ...bindParams };
|
|
165
201
|
for (const field of fieldsToEncrypt) {
|
|
166
202
|
if (processedBinds[field] !== undefined && processedBinds[field] !== null) {
|
|
167
|
-
processedBinds[field] = (0,
|
|
203
|
+
processedBinds[field] = (0, security_js_1.encryptValue)(processedBinds[field], activeKey, activeVersion);
|
|
168
204
|
}
|
|
169
205
|
}
|
|
170
206
|
}
|
package/dist/drizzle.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createDrizzleGuard = void 0;
|
|
4
|
-
const pg_core_1 = require("drizzle-orm/pg-core");
|
|
5
|
-
const mysql_core_1 = require("drizzle-orm/mysql-core");
|
|
6
|
-
const sqlite_core_1 = require("drizzle-orm/sqlite-core");
|
|
7
|
-
const prisma_1 = require("./prisma");
|
|
8
|
-
const blind_index_1 = require("./blind-index");
|
|
9
4
|
const security_1 = require("./security");
|
|
10
5
|
function getKeys(options) {
|
|
11
6
|
let keys;
|
|
12
7
|
let activeVersion;
|
|
13
8
|
if (Buffer.isBuffer(options.key)) {
|
|
14
|
-
keys = { '1': options.key };
|
|
9
|
+
keys = { '1': Buffer.from(options.key) };
|
|
15
10
|
activeVersion = '1';
|
|
16
11
|
}
|
|
17
12
|
else {
|
|
18
|
-
keys =
|
|
13
|
+
keys = {};
|
|
14
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
15
|
+
keys[v] = Buffer.from(k);
|
|
16
|
+
}
|
|
19
17
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
20
18
|
}
|
|
21
19
|
return { keys, activeVersion };
|
|
22
20
|
}
|
|
23
21
|
const createDrizzleGuard = (options) => {
|
|
22
|
+
const pgCustomType = require('drizzle-orm/pg-core').customType;
|
|
23
|
+
const mysqlCustomType = require('drizzle-orm/mysql-core').customType;
|
|
24
|
+
const sqliteCustomType = require('drizzle-orm/sqlite-core').customType;
|
|
24
25
|
const { keys, activeVersion } = getKeys(options);
|
|
25
26
|
const activeKey = keys[activeVersion];
|
|
26
27
|
if (!activeKey) {
|
|
@@ -29,49 +30,49 @@ const createDrizzleGuard = (options) => {
|
|
|
29
30
|
(0, security_1.registerKeysForZeroization)(keys);
|
|
30
31
|
const rootSalt = options.blindIndexes?.rootSalt;
|
|
31
32
|
return {
|
|
32
|
-
pgText: (name, columnPath) => (
|
|
33
|
+
pgText: (name, columnPath) => pgCustomType({
|
|
33
34
|
dataType() {
|
|
34
35
|
return 'text';
|
|
35
36
|
},
|
|
36
37
|
toDriver(value) {
|
|
37
|
-
return (0,
|
|
38
|
+
return (0, security_1.encryptValue)(value, activeKey, activeVersion);
|
|
38
39
|
},
|
|
39
40
|
fromDriver(value) {
|
|
40
41
|
const parts = columnPath?.split('.') || [name];
|
|
41
42
|
const mName = parts[0] || 'Model';
|
|
42
43
|
const fName = parts[1] || name;
|
|
43
|
-
return (0, security_1.decryptWithSecurity)(value, (val) => (0,
|
|
44
|
+
return (0, security_1.decryptWithSecurity)(value, (val) => (0, security_1.decryptValue)(val, keys), mName, fName, undefined, options);
|
|
44
45
|
}
|
|
45
46
|
})(name),
|
|
46
|
-
mysqlText: (name, columnPath) => (
|
|
47
|
+
mysqlText: (name, columnPath) => mysqlCustomType({
|
|
47
48
|
dataType() {
|
|
48
49
|
return 'text';
|
|
49
50
|
},
|
|
50
51
|
toDriver(value) {
|
|
51
|
-
return (0,
|
|
52
|
+
return (0, security_1.encryptValue)(value, activeKey, activeVersion);
|
|
52
53
|
},
|
|
53
54
|
fromDriver(value) {
|
|
54
55
|
const parts = columnPath?.split('.') || [name];
|
|
55
56
|
const mName = parts[0] || 'Model';
|
|
56
57
|
const fName = parts[1] || name;
|
|
57
|
-
return (0, security_1.decryptWithSecurity)(value, (val) => (0,
|
|
58
|
+
return (0, security_1.decryptWithSecurity)(value, (val) => (0, security_1.decryptValue)(val, keys), mName, fName, undefined, options);
|
|
58
59
|
}
|
|
59
60
|
})(name),
|
|
60
|
-
sqliteText: (name, columnPath) => (
|
|
61
|
+
sqliteText: (name, columnPath) => sqliteCustomType({
|
|
61
62
|
dataType() {
|
|
62
63
|
return 'text';
|
|
63
64
|
},
|
|
64
65
|
toDriver(value) {
|
|
65
|
-
return (0,
|
|
66
|
+
return (0, security_1.encryptValue)(value, activeKey, activeVersion);
|
|
66
67
|
},
|
|
67
68
|
fromDriver(value) {
|
|
68
69
|
const parts = columnPath?.split('.') || [name];
|
|
69
70
|
const mName = parts[0] || 'Model';
|
|
70
71
|
const fName = parts[1] || name;
|
|
71
|
-
return (0, security_1.decryptWithSecurity)(value, (val) => (0,
|
|
72
|
+
return (0, security_1.decryptWithSecurity)(value, (val) => (0, security_1.decryptValue)(val, keys), mName, fName, undefined, options);
|
|
72
73
|
}
|
|
73
74
|
})(name),
|
|
74
|
-
pgBlindIndex: (name, columnName) => (
|
|
75
|
+
pgBlindIndex: (name, columnName) => pgCustomType({
|
|
75
76
|
dataType() {
|
|
76
77
|
return 'text';
|
|
77
78
|
},
|
|
@@ -79,13 +80,13 @@ const createDrizzleGuard = (options) => {
|
|
|
79
80
|
if (!rootSalt) {
|
|
80
81
|
throw new Error('Blind index root salt is not configured in Drizzle guard options.');
|
|
81
82
|
}
|
|
82
|
-
return (0,
|
|
83
|
+
return (0, security_1.computeBlindIndex)(value, rootSalt, columnName);
|
|
83
84
|
},
|
|
84
85
|
fromDriver(value) {
|
|
85
86
|
return value;
|
|
86
87
|
}
|
|
87
88
|
})(name),
|
|
88
|
-
mysqlBlindIndex: (name, columnName) => (
|
|
89
|
+
mysqlBlindIndex: (name, columnName) => mysqlCustomType({
|
|
89
90
|
dataType() {
|
|
90
91
|
return 'text';
|
|
91
92
|
},
|
|
@@ -93,13 +94,13 @@ const createDrizzleGuard = (options) => {
|
|
|
93
94
|
if (!rootSalt) {
|
|
94
95
|
throw new Error('Blind index root salt is not configured in Drizzle guard options.');
|
|
95
96
|
}
|
|
96
|
-
return (0,
|
|
97
|
+
return (0, security_1.computeBlindIndex)(value, rootSalt, columnName);
|
|
97
98
|
},
|
|
98
99
|
fromDriver(value) {
|
|
99
100
|
return value;
|
|
100
101
|
}
|
|
101
102
|
})(name),
|
|
102
|
-
sqliteBlindIndex: (name, columnName) => (
|
|
103
|
+
sqliteBlindIndex: (name, columnName) => sqliteCustomType({
|
|
103
104
|
dataType() {
|
|
104
105
|
return 'text';
|
|
105
106
|
},
|
|
@@ -107,7 +108,7 @@ const createDrizzleGuard = (options) => {
|
|
|
107
108
|
if (!rootSalt) {
|
|
108
109
|
throw new Error('Blind index root salt is not configured in Drizzle guard options.');
|
|
109
110
|
}
|
|
110
|
-
return (0,
|
|
111
|
+
return (0, security_1.computeBlindIndex)(value, rootSalt, columnName);
|
|
111
112
|
},
|
|
112
113
|
fromDriver(value) {
|
|
113
114
|
return value;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
export { prismaDbGuard, PrismaDbGuardOptions, encryptValue, decryptValue, resolveKeys } from './prisma';
|
|
2
|
-
export { mongooseDbGuard, MongooseDbGuardOptions } from './mongoose';
|
|
3
|
-
export { createDrizzleGuard } from './drizzle';
|
|
4
|
-
export { createTypeOrmSubscriber } from './typeorm';
|
|
5
1
|
export { wrapSqliteDatabase, wrapOracleConnection, DbGuardDriverOptions } from './drivers';
|
|
6
|
-
export { KmsProvider, AwsKmsProvider, GcpKmsProvider, VaultKmsProvider, unwrapDekLocal, Pkcs11KmsProvider } from './kms';
|
|
2
|
+
export { KmsProvider, AwsKmsProvider, GcpKmsProvider, VaultKmsProvider, unwrapDekLocal, Pkcs11KmsProvider, resolveKeys, DbGuardKeysOptions } from './kms';
|
|
7
3
|
export { computeBlindIndex } from './blind-index';
|
|
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';
|
|
4
|
+
export { dbGuardContextStore, configureAuditLogger, decryptWithSecurity, checkRateLimit, checkPageSize, resetFailClosedStatusForTesting, resetAuditLoggerForTesting, getCachedKey, setCachedKey, resetSecureKeyCacheForTesting, configureBreakGlass, deactivateBreakGlass, isBreakGlassActive, getBreakGlassKey, activateBreakGlass, parseCiphertext, CRYPTO_ALGORITHMS, VERSION_ALGORITHMS, maskValue, encryptValue, decryptValue } from './security';
|
|
9
5
|
export { auditConfiguration, generateComplianceHtmlReport, ComplianceAuditInput, ComplianceScorecard } from './compliance';
|
package/dist/index.js
CHANGED
|
@@ -1,17 +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.wrapOracleConnection = exports.wrapSqliteDatabase =
|
|
4
|
-
var prisma_1 = require("./prisma");
|
|
5
|
-
Object.defineProperty(exports, "prismaDbGuard", { enumerable: true, get: function () { return prisma_1.prismaDbGuard; } });
|
|
6
|
-
Object.defineProperty(exports, "encryptValue", { enumerable: true, get: function () { return prisma_1.encryptValue; } });
|
|
7
|
-
Object.defineProperty(exports, "decryptValue", { enumerable: true, get: function () { return prisma_1.decryptValue; } });
|
|
8
|
-
Object.defineProperty(exports, "resolveKeys", { enumerable: true, get: function () { return prisma_1.resolveKeys; } });
|
|
9
|
-
var mongoose_1 = require("./mongoose");
|
|
10
|
-
Object.defineProperty(exports, "mongooseDbGuard", { enumerable: true, get: function () { return mongoose_1.mongooseDbGuard; } });
|
|
11
|
-
var drizzle_1 = require("./drizzle");
|
|
12
|
-
Object.defineProperty(exports, "createDrizzleGuard", { enumerable: true, get: function () { return drizzle_1.createDrizzleGuard; } });
|
|
13
|
-
var typeorm_1 = require("./typeorm");
|
|
14
|
-
Object.defineProperty(exports, "createTypeOrmSubscriber", { enumerable: true, get: function () { return typeorm_1.createTypeOrmSubscriber; } });
|
|
3
|
+
exports.generateComplianceHtmlReport = exports.auditConfiguration = exports.decryptValue = exports.encryptValue = 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.resolveKeys = exports.Pkcs11KmsProvider = exports.unwrapDekLocal = exports.VaultKmsProvider = exports.GcpKmsProvider = exports.AwsKmsProvider = exports.wrapOracleConnection = exports.wrapSqliteDatabase = void 0;
|
|
15
4
|
var drivers_1 = require("./drivers");
|
|
16
5
|
Object.defineProperty(exports, "wrapSqliteDatabase", { enumerable: true, get: function () { return drivers_1.wrapSqliteDatabase; } });
|
|
17
6
|
Object.defineProperty(exports, "wrapOracleConnection", { enumerable: true, get: function () { return drivers_1.wrapOracleConnection; } });
|
|
@@ -21,6 +10,7 @@ Object.defineProperty(exports, "GcpKmsProvider", { enumerable: true, get: functi
|
|
|
21
10
|
Object.defineProperty(exports, "VaultKmsProvider", { enumerable: true, get: function () { return kms_1.VaultKmsProvider; } });
|
|
22
11
|
Object.defineProperty(exports, "unwrapDekLocal", { enumerable: true, get: function () { return kms_1.unwrapDekLocal; } });
|
|
23
12
|
Object.defineProperty(exports, "Pkcs11KmsProvider", { enumerable: true, get: function () { return kms_1.Pkcs11KmsProvider; } });
|
|
13
|
+
Object.defineProperty(exports, "resolveKeys", { enumerable: true, get: function () { return kms_1.resolveKeys; } });
|
|
24
14
|
var blind_index_1 = require("./blind-index");
|
|
25
15
|
Object.defineProperty(exports, "computeBlindIndex", { enumerable: true, get: function () { return blind_index_1.computeBlindIndex; } });
|
|
26
16
|
var security_1 = require("./security");
|
|
@@ -43,6 +33,8 @@ Object.defineProperty(exports, "parseCiphertext", { enumerable: true, get: funct
|
|
|
43
33
|
Object.defineProperty(exports, "CRYPTO_ALGORITHMS", { enumerable: true, get: function () { return security_1.CRYPTO_ALGORITHMS; } });
|
|
44
34
|
Object.defineProperty(exports, "VERSION_ALGORITHMS", { enumerable: true, get: function () { return security_1.VERSION_ALGORITHMS; } });
|
|
45
35
|
Object.defineProperty(exports, "maskValue", { enumerable: true, get: function () { return security_1.maskValue; } });
|
|
36
|
+
Object.defineProperty(exports, "encryptValue", { enumerable: true, get: function () { return security_1.encryptValue; } });
|
|
37
|
+
Object.defineProperty(exports, "decryptValue", { enumerable: true, get: function () { return security_1.decryptValue; } });
|
|
46
38
|
var compliance_1 = require("./compliance");
|
|
47
39
|
Object.defineProperty(exports, "auditConfiguration", { enumerable: true, get: function () { return compliance_1.auditConfiguration; } });
|
|
48
40
|
Object.defineProperty(exports, "generateComplianceHtmlReport", { enumerable: true, get: function () { return compliance_1.generateComplianceHtmlReport; } });
|
package/dist/kms.d.ts
CHANGED
|
@@ -44,3 +44,13 @@ export declare class Pkcs11KmsProvider implements KmsProvider {
|
|
|
44
44
|
});
|
|
45
45
|
decrypt(ciphertext: Buffer): Promise<Buffer>;
|
|
46
46
|
}
|
|
47
|
+
export interface DbGuardKeysOptions {
|
|
48
|
+
key?: Buffer | Record<string, Buffer>;
|
|
49
|
+
kms?: {
|
|
50
|
+
provider: KmsProvider;
|
|
51
|
+
wrappedKey: Buffer | Record<string, Buffer>;
|
|
52
|
+
wrappedKek?: Buffer | Record<string, Buffer>;
|
|
53
|
+
activeKeyVersion?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export declare function resolveKeys(options: DbGuardKeysOptions): Promise<Record<string, Buffer>>;
|
package/dist/kms.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Pkcs11KmsProvider = exports.VaultKmsProvider = exports.GcpKmsProvider = exports.AwsKmsProvider = void 0;
|
|
4
4
|
exports.unwrapDekLocal = unwrapDekLocal;
|
|
5
|
+
exports.resolveKeys = resolveKeys;
|
|
5
6
|
const security_1 = require("./security");
|
|
6
7
|
class AwsKmsProvider {
|
|
7
8
|
config;
|
|
@@ -106,7 +107,14 @@ class Pkcs11KmsProvider {
|
|
|
106
107
|
const pkcs11js = require('pkcs11js');
|
|
107
108
|
const pkcs11 = new pkcs11js.PKCS11();
|
|
108
109
|
pkcs11.load(this.config.libraryPath);
|
|
109
|
-
|
|
110
|
+
try {
|
|
111
|
+
pkcs11.C_Initialize();
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
if (!e.message?.includes('CRYPTOKI_ALREADY_INITIALIZED') && e.code !== 0x00000191 && e.code !== 401) {
|
|
115
|
+
throw e;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
110
118
|
const slots = pkcs11.C_GetSlotList(true);
|
|
111
119
|
const slotIndex = this.config.slotId !== undefined ? this.config.slotId : 0;
|
|
112
120
|
if (!slots || slots.length <= slotIndex) {
|
|
@@ -143,7 +151,6 @@ class Pkcs11KmsProvider {
|
|
|
143
151
|
finally {
|
|
144
152
|
pkcs11.C_Logout(session);
|
|
145
153
|
pkcs11.C_CloseSession(session);
|
|
146
|
-
pkcs11.C_Finalize();
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
catch (err) {
|
|
@@ -152,3 +159,52 @@ class Pkcs11KmsProvider {
|
|
|
152
159
|
}
|
|
153
160
|
}
|
|
154
161
|
exports.Pkcs11KmsProvider = Pkcs11KmsProvider;
|
|
162
|
+
async function resolveKeys(options) {
|
|
163
|
+
let rawKeys = {};
|
|
164
|
+
if (options.key) {
|
|
165
|
+
if (Buffer.isBuffer(options.key)) {
|
|
166
|
+
rawKeys = { '1': options.key };
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
rawKeys = { ...options.key };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else if (options.kms) {
|
|
173
|
+
const { provider, wrappedKey, wrappedKek } = options.kms;
|
|
174
|
+
if (Buffer.isBuffer(wrappedKey)) {
|
|
175
|
+
if (wrappedKek && Buffer.isBuffer(wrappedKek)) {
|
|
176
|
+
const unwrappedKek = await provider.decrypt(wrappedKek);
|
|
177
|
+
const dek = unwrapDekLocal(wrappedKey, unwrappedKek);
|
|
178
|
+
unwrappedKek.fill(0); // RAM Security: zeroize KEK immediately
|
|
179
|
+
rawKeys = { '1': dek };
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const key = await provider.decrypt(wrappedKey);
|
|
183
|
+
rawKeys = { '1': key };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
for (const [ver, wrapped] of Object.entries(wrappedKey)) {
|
|
188
|
+
if (wrappedKek) {
|
|
189
|
+
const wKek = Buffer.isBuffer(wrappedKek) ? wrappedKek : wrappedKek[ver];
|
|
190
|
+
if (wKek) {
|
|
191
|
+
const unwrappedKek = await provider.decrypt(wKek);
|
|
192
|
+
const dek = unwrapDekLocal(wrapped, unwrappedKek);
|
|
193
|
+
unwrappedKek.fill(0); // RAM Security: zeroize KEK immediately
|
|
194
|
+
rawKeys[ver] = dek;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
rawKeys[ver] = await provider.decrypt(wrapped);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
rawKeys[ver] = await provider.decrypt(wrapped);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
throw new Error("Either 'key' or 'kms' configuration must be provided.");
|
|
208
|
+
}
|
|
209
|
+
return rawKeys;
|
|
210
|
+
}
|
package/dist/mongoose.d.ts
CHANGED
package/dist/mongoose.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mongooseDbGuard = mongooseDbGuard;
|
|
4
|
-
const
|
|
5
|
-
const blind_index_1 = require("./blind-index");
|
|
4
|
+
const kms_1 = require("./kms");
|
|
6
5
|
const security_1 = require("./security");
|
|
7
6
|
function mongooseDbGuard(schema, options) {
|
|
8
7
|
const { fields } = options;
|
|
9
|
-
let keys;
|
|
8
|
+
let keys = {};
|
|
10
9
|
let activeVersion;
|
|
11
10
|
if (Buffer.isBuffer(options.key)) {
|
|
12
|
-
keys = { '1': options.key };
|
|
11
|
+
keys = { '1': Buffer.from(options.key) };
|
|
13
12
|
activeVersion = '1';
|
|
14
13
|
}
|
|
15
14
|
else {
|
|
16
|
-
|
|
15
|
+
for (const [v, k] of Object.entries(options.key)) {
|
|
16
|
+
keys[v] = Buffer.from(k);
|
|
17
|
+
}
|
|
17
18
|
activeVersion = options.activeKeyVersion || Object.keys(keys)[0];
|
|
18
19
|
}
|
|
19
20
|
(0, security_1.registerKeysForZeroization)(keys);
|
|
@@ -28,11 +29,15 @@ function mongooseDbGuard(schema, options) {
|
|
|
28
29
|
return { keys: { '1': bgKey }, activeKey: bgKey, activeVersion: '1' };
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
if (
|
|
32
|
+
if (options.multiTenant && !tenantId) {
|
|
33
|
+
throw new Error("Vollcrypt Security: tenantId must be provided in multi-tenant mode.");
|
|
34
|
+
}
|
|
35
|
+
if (!options.multiTenant) {
|
|
32
36
|
return { keys, activeKey, activeVersion };
|
|
33
37
|
}
|
|
38
|
+
const tId = tenantId;
|
|
34
39
|
// Check Secure TTL Cache
|
|
35
|
-
const cachedActiveKey = (0, security_1.getCachedKey)(
|
|
40
|
+
const cachedActiveKey = (0, security_1.getCachedKey)(tId, activeVersion);
|
|
36
41
|
if (cachedActiveKey) {
|
|
37
42
|
return { keys: { [activeVersion]: cachedActiveKey }, activeKey: cachedActiveKey, activeVersion };
|
|
38
43
|
}
|
|
@@ -44,27 +49,31 @@ function mongooseDbGuard(schema, options) {
|
|
|
44
49
|
}
|
|
45
50
|
let tenantConfig;
|
|
46
51
|
if (multiTenant.tenants) {
|
|
47
|
-
tenantConfig = multiTenant.tenants[
|
|
52
|
+
tenantConfig = multiTenant.tenants[tId];
|
|
48
53
|
}
|
|
49
54
|
else if (multiTenant.getTenantConfig) {
|
|
50
|
-
tenantConfig = await multiTenant.getTenantConfig(
|
|
55
|
+
tenantConfig = await multiTenant.getTenantConfig(tId);
|
|
51
56
|
}
|
|
52
57
|
if (!tenantConfig) {
|
|
53
|
-
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${
|
|
58
|
+
throw new Error(`Vollcrypt Security: Configuration not found for tenantId "${tId}".`);
|
|
54
59
|
}
|
|
55
|
-
const
|
|
60
|
+
const resolvedTenantKeysRaw = await (0, kms_1.resolveKeys)({
|
|
56
61
|
...options,
|
|
57
62
|
key: tenantConfig.key,
|
|
58
63
|
kms: tenantConfig.kms
|
|
59
64
|
});
|
|
60
|
-
|
|
65
|
+
const resolvedTenantKeys = {};
|
|
66
|
+
for (const [v, k] of Object.entries(resolvedTenantKeysRaw)) {
|
|
67
|
+
resolvedTenantKeys[v] = Buffer.from(k);
|
|
68
|
+
}
|
|
69
|
+
(0, security_1.registerKeysForZeroization)(resolvedTenantKeys, tId);
|
|
61
70
|
const tActiveVersion = tenantConfig.kms?.activeKeyVersion || '1';
|
|
62
71
|
const tActiveKey = resolvedTenantKeys[tActiveVersion];
|
|
63
72
|
if (!tActiveKey) {
|
|
64
|
-
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${
|
|
73
|
+
throw new Error(`Vollcrypt Security: Active key version "${tActiveVersion}" not found for tenantId "${tId}".`);
|
|
65
74
|
}
|
|
66
75
|
for (const [ver, keyBuf] of Object.entries(resolvedTenantKeys)) {
|
|
67
|
-
(0, security_1.setCachedKey)(
|
|
76
|
+
(0, security_1.setCachedKey)(tId, ver, keyBuf);
|
|
68
77
|
}
|
|
69
78
|
return { keys: resolvedTenantKeys, activeKey: tActiveKey, activeVersion: tActiveVersion };
|
|
70
79
|
};
|
|
@@ -79,7 +88,7 @@ function mongooseDbGuard(schema, options) {
|
|
|
79
88
|
// 1. Encrypt fields
|
|
80
89
|
for (const field of fields) {
|
|
81
90
|
if (doc.isModified(field) && doc[field] !== undefined && doc[field] !== null) {
|
|
82
|
-
doc[field] = (0,
|
|
91
|
+
doc[field] = (0, security_1.encryptValue)(doc[field], resolved.activeKey, resolved.activeVersion);
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
// 2. Compute blind indexes
|
|
@@ -89,9 +98,9 @@ function mongooseDbGuard(schema, options) {
|
|
|
89
98
|
const bidxField = `${field}_bidx`;
|
|
90
99
|
// Decrypt temporary value if it was already encrypted in the previous step
|
|
91
100
|
const rawVal = doc.isModified(field) && doc[field].startsWith('VOLLVALT:')
|
|
92
|
-
? (0,
|
|
101
|
+
? (0, security_1.decryptValue)(doc[field], keys)
|
|
93
102
|
: doc[field];
|
|
94
|
-
doc[bidxField] = (0,
|
|
103
|
+
doc[bidxField] = (0, security_1.computeBlindIndex)(rawVal, options.blindIndexes.rootSalt, `${modelName}.${field}`);
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
}
|
|
@@ -128,9 +137,9 @@ function mongooseDbGuard(schema, options) {
|
|
|
128
137
|
if (obj[currentPart] !== undefined && obj[currentPart] !== null) {
|
|
129
138
|
if (options.blindIndexes && options.blindIndexes.fields.includes(fullPath) && options.blindIndexes.rootSalt) {
|
|
130
139
|
const bidxField = `${currentPart}_bidx`;
|
|
131
|
-
obj[bidxField] = (0,
|
|
140
|
+
obj[bidxField] = (0, security_1.computeBlindIndex)(obj[currentPart], options.blindIndexes.rootSalt, `${modelName}.${fullPath}`);
|
|
132
141
|
}
|
|
133
|
-
obj[currentPart] = (0,
|
|
142
|
+
obj[currentPart] = (0, security_1.encryptValue)(obj[currentPart], encKey, encVer);
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
else {
|
|
@@ -141,9 +150,9 @@ function mongooseDbGuard(schema, options) {
|
|
|
141
150
|
if (obj[dotNotatedPath] !== undefined && obj[dotNotatedPath] !== null) {
|
|
142
151
|
if (options.blindIndexes && options.blindIndexes.fields.includes(fullPath) && options.blindIndexes.rootSalt) {
|
|
143
152
|
const bidxField = `${dotNotatedPath}_bidx`;
|
|
144
|
-
obj[bidxField] = (0,
|
|
153
|
+
obj[bidxField] = (0, security_1.computeBlindIndex)(obj[dotNotatedPath], options.blindIndexes.rootSalt, `${modelName}.${fullPath}`);
|
|
145
154
|
}
|
|
146
|
-
obj[dotNotatedPath] = (0,
|
|
155
|
+
obj[dotNotatedPath] = (0, security_1.encryptValue)(obj[dotNotatedPath], encKey, encVer);
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
};
|
|
@@ -178,7 +187,7 @@ function mongooseDbGuard(schema, options) {
|
|
|
178
187
|
// 2. Process query criteria (rewrite exact match search queries on conditions)
|
|
179
188
|
const conditions = typeof query.getQuery === 'function' ? query.getQuery() : null;
|
|
180
189
|
if (conditions && options.blindIndexes && options.blindIndexes.rootSalt) {
|
|
181
|
-
(0,
|
|
190
|
+
(0, security_1.rewriteQueryWhere)(conditions, options.blindIndexes.fields, options.blindIndexes.rootSalt, modelName);
|
|
182
191
|
}
|
|
183
192
|
if (typeof next === 'function') {
|
|
184
193
|
next();
|
|
@@ -210,7 +219,7 @@ function mongooseDbGuard(schema, options) {
|
|
|
210
219
|
const modelName = options.blindIndexes?.modelName || query.model?.modelName || 'Model';
|
|
211
220
|
const conditions = typeof query.getQuery === 'function' ? query.getQuery() : null;
|
|
212
221
|
if (conditions && options.blindIndexes && options.blindIndexes.rootSalt) {
|
|
213
|
-
(0,
|
|
222
|
+
(0, security_1.rewriteQueryWhere)(conditions, options.blindIndexes.fields, options.blindIndexes.rootSalt, modelName);
|
|
214
223
|
}
|
|
215
224
|
next();
|
|
216
225
|
});
|
|
@@ -222,7 +231,7 @@ function mongooseDbGuard(schema, options) {
|
|
|
222
231
|
for (const field of fields) {
|
|
223
232
|
if (doc[field] !== undefined && doc[field] !== null) {
|
|
224
233
|
try {
|
|
225
|
-
doc[field] = (0, security_1.decryptWithSecurity)(doc[field], (val) => (0,
|
|
234
|
+
doc[field] = (0, security_1.decryptWithSecurity)(doc[field], (val) => (0, security_1.decryptValue)(val, decKeys), modelName, field, doc.id || doc._id, options);
|
|
226
235
|
}
|
|
227
236
|
catch (err) {
|
|
228
237
|
throw new Error(`Mongoose db-guard failed to decrypt field "${field}": ${err.message}`);
|