@faizahmed/secret-keystore 1.1.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/LICENSE +21 -0
- package/README.md +1203 -0
- package/SECURITY.md +505 -0
- package/bin/cli.js +969 -0
- package/package.json +77 -0
- package/src/attestation/attestation-client.js +146 -0
- package/src/attestation/attestation-manager.js +339 -0
- package/src/attestation/cms-unwrap.js +166 -0
- package/src/attestation/index.js +66 -0
- package/src/attestation/key-pair.js +129 -0
- package/src/config.js +130 -0
- package/src/content-operations.js +494 -0
- package/src/errors.js +372 -0
- package/src/index.d.ts +641 -0
- package/src/index.js +438 -0
- package/src/keystore.js +678 -0
- package/src/kms.js +858 -0
- package/src/object-operations.js +232 -0
- package/src/options.js +541 -0
- package/src/path-matcher.js +319 -0
- package/src/rotate.js +92 -0
- package/src/yaml-utils.js +265 -0
package/src/options.js
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @faizahmed/secret-keystore - Options Architecture
|
|
3
|
+
*
|
|
4
|
+
* Layered/namespaced options structure for all library functions.
|
|
5
|
+
* This module provides type definitions, defaults, and validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { ValidationError, VALIDATION_ERROR_CODES } = require('./errors');
|
|
9
|
+
|
|
10
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reserved keys that should NEVER be encrypted.
|
|
16
|
+
* These are required for the encryption/decryption process itself.
|
|
17
|
+
*/
|
|
18
|
+
const RESERVED_KEYS = [
|
|
19
|
+
'KMS_KEY_ID',
|
|
20
|
+
'AWS_REGION',
|
|
21
|
+
'AWS_ACCESS_KEY_ID',
|
|
22
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
23
|
+
'AWS_SESSION_TOKEN'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
27
|
+
// DEFAULT VALUES
|
|
28
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
29
|
+
|
|
30
|
+
const DEFAULT_AWS_OPTIONS = {
|
|
31
|
+
credentials: null, // Uses IAM role by default
|
|
32
|
+
region: null // Uses AWS_REGION env var or SDK default
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const DEFAULT_ATTESTATION_OPTIONS = {
|
|
36
|
+
enabled: false,
|
|
37
|
+
required: false,
|
|
38
|
+
fallbackToStandard: true,
|
|
39
|
+
// Full attestation mode (default) - library handles key pair generation + CMS unwrap
|
|
40
|
+
endpoint: null, // Attestation endpoint URL (default: localhost:50123)
|
|
41
|
+
timeout: 10000, // Attestation request timeout in ms
|
|
42
|
+
userData: '', // User data to include in attestation document
|
|
43
|
+
// Legacy mode - pre-generated document + private key
|
|
44
|
+
document: null, // Buffer | (() => Buffer) | (() => Promise<Buffer>) | string (base64)
|
|
45
|
+
privateKey: null, // PEM private key for CMS unwrap (required with document in legacy mode)
|
|
46
|
+
// KMS options
|
|
47
|
+
encryptionContext: null // KMS encryption context
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const DEFAULT_COMMON_OPTIONS = {
|
|
51
|
+
aws: { ...DEFAULT_AWS_OPTIONS },
|
|
52
|
+
attestation: { ...DEFAULT_ATTESTATION_OPTIONS },
|
|
53
|
+
logger: null, // Uses console if not provided
|
|
54
|
+
logLevel: 'info' // 'debug' | 'info' | 'warn' | 'error' | 'silent'
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const DEFAULT_ENCRYPT_OPTIONS = {
|
|
58
|
+
...DEFAULT_COMMON_OPTIONS,
|
|
59
|
+
output: {
|
|
60
|
+
format: 'prefixed' // 'base64' | 'buffer' | 'prefixed' (ENC[...])
|
|
61
|
+
},
|
|
62
|
+
skip: {
|
|
63
|
+
empty: true,
|
|
64
|
+
alreadyEncrypted: true
|
|
65
|
+
},
|
|
66
|
+
continueOnError: false
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const DEFAULT_DECRYPT_OPTIONS = {
|
|
70
|
+
...DEFAULT_COMMON_OPTIONS,
|
|
71
|
+
input: {
|
|
72
|
+
format: 'auto' // 'auto' | 'base64' | 'buffer' | 'prefixed'
|
|
73
|
+
},
|
|
74
|
+
skip: {
|
|
75
|
+
unencrypted: true
|
|
76
|
+
},
|
|
77
|
+
validation: {
|
|
78
|
+
format: true,
|
|
79
|
+
kmsKeyMatch: true
|
|
80
|
+
},
|
|
81
|
+
continueOnError: false
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const DEFAULT_PATH_SELECTION_OPTIONS = {
|
|
85
|
+
paths: null, // Explicit dot-notation paths
|
|
86
|
+
patterns: null, // Glob patterns using ** for any-depth matching
|
|
87
|
+
exclude: {
|
|
88
|
+
paths: null,
|
|
89
|
+
patterns: null
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const DEFAULT_CONTENT_OPTIONS = {
|
|
94
|
+
preserve: {
|
|
95
|
+
comments: true,
|
|
96
|
+
formatting: true,
|
|
97
|
+
anchors: true // YAML only
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const DEFAULT_KEYSTORE_OPTIONS = {
|
|
102
|
+
...DEFAULT_DECRYPT_OPTIONS,
|
|
103
|
+
...DEFAULT_PATH_SELECTION_OPTIONS,
|
|
104
|
+
security: {
|
|
105
|
+
inMemoryEncryption: true,
|
|
106
|
+
secureWipe: true
|
|
107
|
+
},
|
|
108
|
+
access: {
|
|
109
|
+
ttl: null, // Secret expiry (ms), null = never
|
|
110
|
+
autoRefresh: true,
|
|
111
|
+
accessLimit: null, // Max access count per key
|
|
112
|
+
clearOnAccess: false
|
|
113
|
+
},
|
|
114
|
+
validation: {
|
|
115
|
+
noProcessEnvLeak: true,
|
|
116
|
+
throwOnMissingKey: false
|
|
117
|
+
},
|
|
118
|
+
retry: {
|
|
119
|
+
attempts: 3,
|
|
120
|
+
delay: 1000, // ms
|
|
121
|
+
backoff: 'exponential' // 'linear' | 'exponential'
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
126
|
+
// HELPER FUNCTIONS
|
|
127
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Deep merge two objects
|
|
131
|
+
* @param {Object} target - Target object
|
|
132
|
+
* @param {Object} source - Source object
|
|
133
|
+
* @returns {Object}
|
|
134
|
+
*/
|
|
135
|
+
function deepMerge(target, source) {
|
|
136
|
+
const result = { ...target };
|
|
137
|
+
|
|
138
|
+
for (const key of Object.keys(source)) {
|
|
139
|
+
if (source[key] === undefined) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
source[key] !== null &&
|
|
145
|
+
typeof source[key] === 'object' &&
|
|
146
|
+
!Array.isArray(source[key]) &&
|
|
147
|
+
!Buffer.isBuffer(source[key]) &&
|
|
148
|
+
typeof source[key] !== 'function'
|
|
149
|
+
) {
|
|
150
|
+
result[key] = deepMerge(target[key] || {}, source[key]);
|
|
151
|
+
} else {
|
|
152
|
+
result[key] = source[key];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Create a logger with the specified level
|
|
161
|
+
* @param {Object} baseLogger - Base logger (console or custom)
|
|
162
|
+
* @param {string} logLevel - Log level
|
|
163
|
+
* @returns {Object}
|
|
164
|
+
*/
|
|
165
|
+
function createLogger(baseLogger, logLevel = 'info') {
|
|
166
|
+
const logger = baseLogger || console;
|
|
167
|
+
const levels = ['debug', 'info', 'warn', 'error', 'silent'];
|
|
168
|
+
const levelIndex = levels.indexOf(logLevel);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
debug: (...args) => levelIndex <= 0 && logger.debug?.(...args),
|
|
172
|
+
info: (...args) => levelIndex <= 1 && logger.info?.(...args),
|
|
173
|
+
warn: (...args) => levelIndex <= 2 && logger.warn?.(...args),
|
|
174
|
+
error: (...args) => levelIndex <= 3 && logger.error?.(...args)
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
179
|
+
// VALIDATION FUNCTIONS
|
|
180
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validate that kmsKeyId is provided
|
|
184
|
+
* @param {string} kmsKeyId - KMS key ID
|
|
185
|
+
* @throws {ValidationError}
|
|
186
|
+
*/
|
|
187
|
+
function validateKmsKeyId(kmsKeyId) {
|
|
188
|
+
if (!kmsKeyId || typeof kmsKeyId !== 'string') {
|
|
189
|
+
throw new ValidationError(
|
|
190
|
+
'kmsKeyId is required and must be a non-empty string',
|
|
191
|
+
VALIDATION_ERROR_CODES.KMS_KEY_REQUIRED,
|
|
192
|
+
'kmsKeyId'
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Basic format validation
|
|
197
|
+
const isArn = kmsKeyId.startsWith('arn:aws:kms:');
|
|
198
|
+
const isAlias = kmsKeyId.startsWith('alias/');
|
|
199
|
+
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(kmsKeyId);
|
|
200
|
+
|
|
201
|
+
if (!isArn && !isAlias && !isUuid) {
|
|
202
|
+
throw new ValidationError(
|
|
203
|
+
'kmsKeyId must be an ARN (arn:aws:kms:...), alias (alias/...), or UUID',
|
|
204
|
+
VALIDATION_ERROR_CODES.INVALID_VALUE,
|
|
205
|
+
'kmsKeyId'
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Validate AWS options
|
|
212
|
+
* @param {Object} aws - AWS options
|
|
213
|
+
* @throws {ValidationError}
|
|
214
|
+
*/
|
|
215
|
+
function validateAwsOptions(aws) {
|
|
216
|
+
if (!aws) return;
|
|
217
|
+
|
|
218
|
+
if (aws.credentials) {
|
|
219
|
+
const { accessKeyId, secretAccessKey } = aws.credentials;
|
|
220
|
+
if (accessKeyId && !secretAccessKey) {
|
|
221
|
+
throw new ValidationError(
|
|
222
|
+
'secretAccessKey is required when accessKeyId is provided',
|
|
223
|
+
VALIDATION_ERROR_CODES.REQUIRED_FIELD,
|
|
224
|
+
'aws.credentials.secretAccessKey'
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (!accessKeyId && secretAccessKey) {
|
|
228
|
+
throw new ValidationError(
|
|
229
|
+
'accessKeyId is required when secretAccessKey is provided',
|
|
230
|
+
VALIDATION_ERROR_CODES.REQUIRED_FIELD,
|
|
231
|
+
'aws.credentials.accessKeyId'
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (aws.region && typeof aws.region !== 'string') {
|
|
237
|
+
throw new ValidationError(
|
|
238
|
+
'aws.region must be a string',
|
|
239
|
+
VALIDATION_ERROR_CODES.INVALID_TYPE,
|
|
240
|
+
'aws.region'
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Validate attestation document format (legacy mode)
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
function validateAttestationDocument(attestation) {
|
|
250
|
+
const isBuffer = Buffer.isBuffer(attestation.document);
|
|
251
|
+
const isFunction = typeof attestation.document === 'function';
|
|
252
|
+
const isString = typeof attestation.document === 'string';
|
|
253
|
+
|
|
254
|
+
if (!isBuffer && !isFunction && !isString) {
|
|
255
|
+
throw new ValidationError(
|
|
256
|
+
'attestation.document must be a Buffer, string (base64), or function',
|
|
257
|
+
VALIDATION_ERROR_CODES.INVALID_TYPE,
|
|
258
|
+
'attestation.document'
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// In legacy mode, privateKey is required with document
|
|
263
|
+
if (!attestation.privateKey) {
|
|
264
|
+
throw new ValidationError(
|
|
265
|
+
'attestation.privateKey is required when attestation.document is provided',
|
|
266
|
+
VALIDATION_ERROR_CODES.REQUIRED_FIELD,
|
|
267
|
+
'attestation.privateKey'
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Validate private key format
|
|
274
|
+
* @private
|
|
275
|
+
*/
|
|
276
|
+
function validatePrivateKeyFormat(privateKey) {
|
|
277
|
+
const isValidPem =
|
|
278
|
+
typeof privateKey === 'string' && privateKey.includes('-----BEGIN PRIVATE KEY-----');
|
|
279
|
+
|
|
280
|
+
if (!isValidPem) {
|
|
281
|
+
throw new ValidationError(
|
|
282
|
+
'attestation.privateKey must be a PEM-encoded PKCS#8 private key',
|
|
283
|
+
VALIDATION_ERROR_CODES.INVALID_TYPE,
|
|
284
|
+
'attestation.privateKey'
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Validate attestation options
|
|
291
|
+
* @param {Object} attestation - Attestation options
|
|
292
|
+
* @throws {ValidationError}
|
|
293
|
+
*/
|
|
294
|
+
function validateAttestationOptions(attestation) {
|
|
295
|
+
if (!attestation) return;
|
|
296
|
+
|
|
297
|
+
if (attestation.required && !attestation.enabled) {
|
|
298
|
+
throw new ValidationError(
|
|
299
|
+
'attestation.enabled must be true when attestation.required is true',
|
|
300
|
+
VALIDATION_ERROR_CODES.INVALID_OPTIONS,
|
|
301
|
+
'attestation'
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Validate legacy mode (document + privateKey)
|
|
306
|
+
if (attestation.document) {
|
|
307
|
+
validateAttestationDocument(attestation);
|
|
308
|
+
validatePrivateKeyFormat(attestation.privateKey);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Validate endpoint URL
|
|
312
|
+
if (attestation.endpoint) {
|
|
313
|
+
try {
|
|
314
|
+
new URL(attestation.endpoint);
|
|
315
|
+
} catch {
|
|
316
|
+
throw new ValidationError(
|
|
317
|
+
'attestation.endpoint must be a valid URL',
|
|
318
|
+
VALIDATION_ERROR_CODES.INVALID_VALUE,
|
|
319
|
+
'attestation.endpoint'
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Validate timeout
|
|
325
|
+
const hasTimeout = attestation.timeout !== undefined && attestation.timeout !== null;
|
|
326
|
+
const isValidTimeout = typeof attestation.timeout === 'number' && attestation.timeout >= 0;
|
|
327
|
+
|
|
328
|
+
if (hasTimeout && !isValidTimeout) {
|
|
329
|
+
throw new ValidationError(
|
|
330
|
+
'attestation.timeout must be a positive number',
|
|
331
|
+
VALIDATION_ERROR_CODES.INVALID_VALUE,
|
|
332
|
+
'attestation.timeout'
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Validate path selection options
|
|
339
|
+
* @param {Object} options - Path selection options
|
|
340
|
+
* @throws {ValidationError}
|
|
341
|
+
*/
|
|
342
|
+
function validatePathSelectionOptions(options) {
|
|
343
|
+
if (options.paths && !Array.isArray(options.paths)) {
|
|
344
|
+
throw new ValidationError(
|
|
345
|
+
'paths must be an array of strings',
|
|
346
|
+
VALIDATION_ERROR_CODES.INVALID_TYPE,
|
|
347
|
+
'paths'
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (options.patterns && !Array.isArray(options.patterns)) {
|
|
352
|
+
throw new ValidationError(
|
|
353
|
+
'patterns must be an array of strings',
|
|
354
|
+
VALIDATION_ERROR_CODES.INVALID_TYPE,
|
|
355
|
+
'patterns'
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Validate patterns only use ** (not single *)
|
|
360
|
+
if (options.patterns) {
|
|
361
|
+
for (const pattern of options.patterns) {
|
|
362
|
+
// Check for single * that's not part of **
|
|
363
|
+
if (/(?<!\*)\*(?!\*)/.test(pattern)) {
|
|
364
|
+
throw new ValidationError(
|
|
365
|
+
`Invalid pattern "${pattern}": only ** (any-depth) is supported, not * (single-level)`,
|
|
366
|
+
VALIDATION_ERROR_CODES.INVALID_VALUE,
|
|
367
|
+
'patterns'
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Validate common options
|
|
376
|
+
* @param {Object} options - Options to validate
|
|
377
|
+
* @throws {ValidationError}
|
|
378
|
+
*/
|
|
379
|
+
function validateCommonOptions(options) {
|
|
380
|
+
if (!options) return;
|
|
381
|
+
|
|
382
|
+
validateAwsOptions(options.aws);
|
|
383
|
+
validateAttestationOptions(options.attestation);
|
|
384
|
+
|
|
385
|
+
if (
|
|
386
|
+
options.logLevel &&
|
|
387
|
+
!['debug', 'info', 'warn', 'error', 'silent'].includes(options.logLevel)
|
|
388
|
+
) {
|
|
389
|
+
throw new ValidationError(
|
|
390
|
+
'logLevel must be one of: debug, info, warn, error, silent',
|
|
391
|
+
VALIDATION_ERROR_CODES.INVALID_VALUE,
|
|
392
|
+
'logLevel'
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
398
|
+
// OPTIONS BUILDERS
|
|
399
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Build common options with defaults
|
|
403
|
+
* @param {Object} options - User provided options
|
|
404
|
+
* @returns {Object}
|
|
405
|
+
*/
|
|
406
|
+
function buildCommonOptions(options = {}) {
|
|
407
|
+
const merged = deepMerge(DEFAULT_COMMON_OPTIONS, options);
|
|
408
|
+
merged.logger = createLogger(options.logger, merged.logLevel);
|
|
409
|
+
validateCommonOptions(merged);
|
|
410
|
+
return merged;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Build encrypt options with defaults
|
|
415
|
+
* @param {Object} options - User provided options
|
|
416
|
+
* @returns {Object}
|
|
417
|
+
*/
|
|
418
|
+
function buildEncryptOptions(options = {}) {
|
|
419
|
+
const merged = deepMerge(DEFAULT_ENCRYPT_OPTIONS, options);
|
|
420
|
+
merged.logger = createLogger(options.logger, merged.logLevel);
|
|
421
|
+
validateCommonOptions(merged);
|
|
422
|
+
return merged;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Build decrypt options with defaults
|
|
427
|
+
* @param {Object} options - User provided options
|
|
428
|
+
* @returns {Object}
|
|
429
|
+
*/
|
|
430
|
+
function buildDecryptOptions(options = {}) {
|
|
431
|
+
const merged = deepMerge(DEFAULT_DECRYPT_OPTIONS, options);
|
|
432
|
+
merged.logger = createLogger(options.logger, merged.logLevel);
|
|
433
|
+
validateCommonOptions(merged);
|
|
434
|
+
return merged;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Build path selection options with defaults
|
|
439
|
+
* @param {Object} options - User provided options
|
|
440
|
+
* @returns {Object}
|
|
441
|
+
*/
|
|
442
|
+
function buildPathSelectionOptions(options = {}) {
|
|
443
|
+
const merged = deepMerge(DEFAULT_PATH_SELECTION_OPTIONS, options);
|
|
444
|
+
validatePathSelectionOptions(merged);
|
|
445
|
+
return merged;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Build content options with defaults
|
|
450
|
+
* @param {Object} options - User provided options
|
|
451
|
+
* @returns {Object}
|
|
452
|
+
*/
|
|
453
|
+
function buildContentOptions(options = {}) {
|
|
454
|
+
return deepMerge(DEFAULT_CONTENT_OPTIONS, options);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Build keystore options with defaults
|
|
459
|
+
* @param {Object} options - User provided options
|
|
460
|
+
* @returns {Object}
|
|
461
|
+
*/
|
|
462
|
+
function buildKeystoreOptions(options = {}) {
|
|
463
|
+
const merged = deepMerge(DEFAULT_KEYSTORE_OPTIONS, options);
|
|
464
|
+
merged.logger = createLogger(options.logger, merged.logLevel);
|
|
465
|
+
validateCommonOptions(merged);
|
|
466
|
+
validatePathSelectionOptions(merged);
|
|
467
|
+
return merged;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
471
|
+
// AWS SDK OPTIONS BUILDER
|
|
472
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Build AWS SDK client options from library options
|
|
476
|
+
* @param {Object} options - Library options (with aws namespace)
|
|
477
|
+
* @returns {Object} AWS SDK client options
|
|
478
|
+
*/
|
|
479
|
+
function buildAwsSdkOptions(options = {}) {
|
|
480
|
+
const sdkOptions = {};
|
|
481
|
+
const aws = options.aws || options;
|
|
482
|
+
|
|
483
|
+
// Credentials: Only set if explicitly provided
|
|
484
|
+
if (aws.credentials?.accessKeyId && aws.credentials?.secretAccessKey) {
|
|
485
|
+
sdkOptions.credentials = {
|
|
486
|
+
accessKeyId: aws.credentials.accessKeyId,
|
|
487
|
+
secretAccessKey: aws.credentials.secretAccessKey
|
|
488
|
+
};
|
|
489
|
+
if (aws.credentials.sessionToken) {
|
|
490
|
+
sdkOptions.credentials.sessionToken = aws.credentials.sessionToken;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Region: Use provided > env var > SDK default
|
|
495
|
+
if (aws.region) {
|
|
496
|
+
sdkOptions.region = aws.region;
|
|
497
|
+
} else if (process.env.AWS_REGION) {
|
|
498
|
+
sdkOptions.region = process.env.AWS_REGION;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return sdkOptions;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
505
|
+
// EXPORTS
|
|
506
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
507
|
+
|
|
508
|
+
module.exports = {
|
|
509
|
+
// Constants
|
|
510
|
+
RESERVED_KEYS,
|
|
511
|
+
|
|
512
|
+
// Defaults
|
|
513
|
+
DEFAULT_AWS_OPTIONS,
|
|
514
|
+
DEFAULT_ATTESTATION_OPTIONS,
|
|
515
|
+
DEFAULT_COMMON_OPTIONS,
|
|
516
|
+
DEFAULT_ENCRYPT_OPTIONS,
|
|
517
|
+
DEFAULT_DECRYPT_OPTIONS,
|
|
518
|
+
DEFAULT_PATH_SELECTION_OPTIONS,
|
|
519
|
+
DEFAULT_CONTENT_OPTIONS,
|
|
520
|
+
DEFAULT_KEYSTORE_OPTIONS,
|
|
521
|
+
|
|
522
|
+
// Helpers
|
|
523
|
+
deepMerge,
|
|
524
|
+
createLogger,
|
|
525
|
+
|
|
526
|
+
// Validation
|
|
527
|
+
validateKmsKeyId,
|
|
528
|
+
validateAwsOptions,
|
|
529
|
+
validateAttestationOptions,
|
|
530
|
+
validatePathSelectionOptions,
|
|
531
|
+
validateCommonOptions,
|
|
532
|
+
|
|
533
|
+
// Builders
|
|
534
|
+
buildCommonOptions,
|
|
535
|
+
buildEncryptOptions,
|
|
536
|
+
buildDecryptOptions,
|
|
537
|
+
buildPathSelectionOptions,
|
|
538
|
+
buildContentOptions,
|
|
539
|
+
buildKeystoreOptions,
|
|
540
|
+
buildAwsSdkOptions
|
|
541
|
+
};
|