-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathindex.js
More file actions
95 lines (86 loc) · 3.02 KB
/
index.js
File metadata and controls
95 lines (86 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
var { KMSClient, DecryptCommand } = require('@aws-sdk/client-kms');
var util = require('util');
var queue = require('d3-queue').queue;
module.exports = js;
module.exports.sh = sh;
module.exports.scrub = scrub;
/**
* @param {object} env Object with variables to decrypt. Usually `process.env`.
* @param {function} callback Callback function. Passed an object with scrubbed vars safe for debugging.
*/
function js(env, callback) {
decrypt(env, function(err, decrypted) {
if (err) return callback(err);
var scrubbed = {};
decrypted.forEach(function(data) {
env[data.key] = data.decrypted;
scrubbed[data.key] = scrub(data.decrypted);
});
callback(null, scrubbed);
});
}
/**
* @param {object} env Object with variables to decrypt. Usually `process.env`.
* @param {function} callback Callback function. Passed an string that can be eval()'d in bash to set env vars.
*/
function sh(env, callback) {
decrypt(env, function(err, decrypted) {
if (err) return callback(err);
var output = '';
decrypted.forEach(function(data) {
output += util.format('export %s=\'%s\'; ', data.key, data.decrypted);
output += util.format('echo \'Decrypted %s=%s\'; ', data.key, scrub(data.decrypted));
});
callback(null, output);
});
}
/**
* Private decrypt function that does not scrub output. Not exposed as a public API.
*
* @param {object} env Object with variables to decrypt.
* @param {function} callback Callback function.
*/
function decrypt(env, callback) {
if (!env.AWS_DEFAULT_REGION) return callback(new Error('AWS_DEFAULT_REGION env var must be set'));
var kms = new KMSClient({
region: env.AWS_DEFAULT_REGION,
maxAttempts: 10
});
var q = queue();
var MAX_CIPHERTEXT_SIZE = 4096;
var BASE64_REGEX = /^[A-Za-z0-9+/]*={0,2}$/;
for (var key in env) {
if (!(/^secure:/).test(env[key])) continue;
var ciphertextB64 = env[key].replace(/^secure:/,'');
if (ciphertextB64.length > MAX_CIPHERTEXT_SIZE) {
return callback(new Error('Ciphertext for key "' + key + '" exceeds maximum allowed size of ' + MAX_CIPHERTEXT_SIZE + ' bytes'));
}
if (!BASE64_REGEX.test(ciphertextB64)) {
return callback(new Error('Invalid base64 format for key "' + key + '"'));
}
q.defer(function(key, val, done) {
try {
var command = new DecryptCommand({
CiphertextBlob: Buffer.from(val, 'base64')
});
kms.send(command).then(function(data) {
done(null, { key: key, val: val, decrypted: Buffer.from(data.Plaintext, 'base64').toString('utf8') });
}).catch(function(err) {
done(err);
});
} catch (e) {
done(new Error('Failed to decode base64 for key "' + key + '": ' + e.message));
}
}, key, ciphertextB64);
}
q.awaitAll(function(err, results) {
if (err) return callback(err);
callback(null, results);
});
}
/**
* Scrub an input string to a fixed length preserving the last 4 characters.
*/
function scrub(value) {
return util.format('************%s', value.substr(-4));
}