@tsmx/secure-config-tool
Advanced tools
Comparing version 2.2.1 to 2.3.0
@@ -18,3 +18,3 @@ const fs = require('fs'); | ||
try { | ||
configKey = cryptUtils.retrieveKey(); | ||
configKey = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY); | ||
} | ||
@@ -21,0 +21,0 @@ catch (error) { |
@@ -8,3 +8,3 @@ const cryptUtils = require('../utils/crypt'); | ||
try { | ||
key = cryptUtils.retrieveKey(verbose); | ||
key = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY, verbose); | ||
decrypted = cryptUtils.decrypt(secret, key); | ||
@@ -11,0 +11,0 @@ } |
@@ -7,3 +7,3 @@ const cryptUtils = require('../utils/crypt'); | ||
try { | ||
key = cryptUtils.retrieveKey(verbose); | ||
key = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY, verbose); | ||
} | ||
@@ -10,0 +10,0 @@ catch (error) { |
@@ -16,3 +16,3 @@ const fs = require('fs'); | ||
try { | ||
configKey = cryptUtils.retrieveKey(); | ||
configKey = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY); | ||
} | ||
@@ -19,0 +19,0 @@ catch (error) { |
@@ -14,3 +14,3 @@ const fs = require('fs'); | ||
try { | ||
configKey = cryptUtils.retrieveKey(); | ||
configKey = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY); | ||
} | ||
@@ -17,0 +17,0 @@ catch (error) { |
{ | ||
"name": "@tsmx/secure-config-tool", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "Command-line tool for @tsmx/secure-config.", | ||
@@ -28,3 +28,3 @@ "main": "secure-config-tool.js", | ||
"@tsmx/string-crypto": "^1.0.2", | ||
"commander": "^11.1.0" | ||
"commander": "^12.1.0" | ||
}, | ||
@@ -42,2 +42,3 @@ "keywords": [ | ||
"key injection", | ||
"key rotation", | ||
"multi-environment", | ||
@@ -49,5 +50,8 @@ "JSON", | ||
"devDependencies": { | ||
"eslint": "^8.41.0", | ||
"@eslint/js": "^9.8.0", | ||
"eslint": "^9.8.0", | ||
"eslint-plugin-jest": "^28.8.0", | ||
"globals": "^15.9.0", | ||
"jest": "^29.2.2" | ||
} | ||
} |
@@ -11,9 +11,10 @@ # [**@tsmx/secure-config-tool**](https://github.com/tsmx/secure-config-tool) | ||
Features: | ||
- create secure configurations with encrypted secrets and a HMAC out of existing JSON files | ||
- update HMAC values of existing secure configuration files after they have changed | ||
- test existing secure configuration JSON files (HMAC validation & decryption) | ||
- generate keys | ||
- encrypt single secrets for copy & paste into existing configurations | ||
- decrypt single secrets for testing purposes | ||
**Features:** | ||
- [create secure configurations](#create) with encrypted secrets and a HMAC out of existing JSON files | ||
- [key rotation](#rotate-key) of an existing secure configuration | ||
- [update HMAC](#update-hmac) values of existing secure configuration files after they have changed | ||
- [test](#test) existing secure configuration JSON files (HMAC validation & decryption) | ||
- [generate keys](#genkey) | ||
- [encrypt single secrets](#encrypt) for copy & paste into existing configurations | ||
- [decrypt single secrets](#decrypt) for testing purposes | ||
@@ -70,4 +71,30 @@ To get more information please also check out the [secure-config documentation](https://tsmx.net/secure-config/). | ||
Specify a property name to store the generated HMAC value in. Defaults to `__hmac` if the option is not present. Doesn't have any effect if `-nh` is specified at the same time. | ||
Specify a property name to store the generated HMAC value in. Defaults to `__hmac` if the option is not present. Doesn't have any effect if `-nh` is specified at the same time. | ||
### rotate-key | ||
Rotates the key of an existing secure configuration file produced with [create](#create). Environment variables `CONFIG_ENCRYPTION_KEY` and `CONFIG_ENCRYPTION_KEY_NEW` must be set: | ||
- `CONFIG_ENCRYPTION_KEY`: the key for the existing secure configuration file | ||
- `CONFIG_ENCRYPTION_KEY_NEW`: the ney key to rotate to (Hint: you can use the [genkey option](#genkey) to generate a new one) | ||
Basic console example: | ||
``` | ||
[tsmx@localhost ]$ export CONFIG_ENCRYPTION_KEY=... | ||
[tsmx@localhost ]$ export CONFIG_ENCRYPTION_KEY_NEW=... | ||
[tsmx@localhost ]$ secure-config-tool rotate-key config-production.json | ||
``` | ||
The result is printed to stdout. Use `>` to save it in a new file or the `--overwrite` option. | ||
If the source secure configuration file includes a HMAC in the default `__hmac` property, it will be updated automatically using the new key. If the source file has a HMAC in a custom named property, use the `-hp` option to provide the property name and it will be updated. | ||
#### -hp, --hmac-prop | ||
Use this option to specify the property name of an existing HMAC value to be updated in the source secure configuration file if it is deviating from the default `__hmac`. | ||
#### -o, --overwrite | ||
Overwrite the original configuration file after rotating the key instead of writing to stdout. | ||
### update-hmac | ||
@@ -149,2 +176,4 @@ | ||
### 2.3.0 | ||
- [Key rotation](#rotate-key) feature added | ||
@@ -151,0 +180,0 @@ ## Test |
@@ -6,2 +6,3 @@ #!/usr/bin/env node | ||
const updateHmac = require('./functions/update-hmac'); | ||
const rotateKey = require('./functions/rotate-key'); | ||
const testFile = require('./functions/test-file'); | ||
@@ -36,2 +37,10 @@ const createKey = require('./functions/create-key'); | ||
program | ||
.command('rotate-key <config-file>') | ||
.description('Rotates the key of an existing secure-config configuration file') | ||
.option('-o, --overwrite', 'overwrite file directly instead of writing to stdout') | ||
.option('-hp, --hmac-prop <hmac-prop>', 'custom name of the HMAC property to be updated with the new key (if one), default is \'__hmac\'') | ||
.action(rotateKey) | ||
.addHelpText('after', helpTexts.rotateHelpText); | ||
program | ||
.command('test <config-file>') | ||
@@ -38,0 +47,0 @@ .description('Test decryption and HMAC validation for an existing secure-config configuration file') |
@@ -37,5 +37,5 @@ const cryptUtils = require('../utils/crypt'); | ||
it('tests a successful key retrieval', () => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY; | ||
process.env[cryptUtils.CONFIG_ENCRYPTION_KEY] = TEST_KEY; | ||
expect(testOutput.length).toBe(0); | ||
const key = cryptUtils.retrieveKey(true); | ||
const key = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY, true); | ||
expect(key).toBeDefined(); | ||
@@ -49,5 +49,5 @@ expect(key.length).toBe(32); | ||
it('tests a successful key retrieval for a hexadecimal string', () => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY_HEX; | ||
process.env[cryptUtils.CONFIG_ENCRYPTION_KEY] = TEST_KEY_HEX; | ||
expect(testOutput.length).toBe(0); | ||
const key = cryptUtils.retrieveKey(true); | ||
const key = cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY, true); | ||
expect(key).toBeDefined(); | ||
@@ -62,4 +62,4 @@ expect(key.length).toBe(64); | ||
expect(() => { | ||
cryptUtils.retrieveKey(); | ||
}).toThrow('Environment variable CONFIG_ENCRYPTION_KEY not set.'); | ||
cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY); | ||
}).toThrow(`Environment variable ${cryptUtils.CONFIG_ENCRYPTION_KEY} not set.`); | ||
}); | ||
@@ -69,5 +69,5 @@ | ||
expect(() => { | ||
process.env['CONFIG_ENCRYPTION_KEY'] = TEST_KEY_BROKEN; | ||
cryptUtils.retrieveKey(); | ||
}).toThrow('CONFIG_ENCRYPTION_KEY length must be 32 bytes.'); | ||
process.env[cryptUtils.CONFIG_ENCRYPTION_KEY] = TEST_KEY_BROKEN; | ||
cryptUtils.retrieveKey(cryptUtils.CONFIG_ENCRYPTION_KEY); | ||
}).toThrow(`${cryptUtils.CONFIG_ENCRYPTION_KEY} length must be 32 bytes.`); | ||
}); | ||
@@ -74,0 +74,0 @@ |
@@ -73,4 +73,4 @@ const fs = require('fs'); | ||
expect(updatedJson.database.host).toStrictEqual(originalJson.database.host); | ||
expect(updatedJson.database.user).toStrictEqual(originalJson.database.user); | ||
expect(updatedJson.database.pass).toStrictEqual(originalJson.database.pass); | ||
expect(updatedJson.database.username).toStrictEqual(originalJson.database.username); | ||
expect(updatedJson.database.password).toStrictEqual(originalJson.database.password); | ||
expect(updatedJson.database.port).toStrictEqual(originalJson.database.port); | ||
@@ -95,4 +95,4 @@ expect(updatedJson['__hmac']).not.toStrictEqual(originalJson['__hmac']); | ||
expect(updatedJson.database.host).toStrictEqual(originalJson.database.host); | ||
expect(updatedJson.database.user).toStrictEqual(originalJson.database.user); | ||
expect(updatedJson.database.pass).toStrictEqual(originalJson.database.pass); | ||
expect(updatedJson.database.username).toStrictEqual(originalJson.database.username); | ||
expect(updatedJson.database.password).toStrictEqual(originalJson.database.password); | ||
expect(updatedJson.database.port).toStrictEqual(originalJson.database.port); | ||
@@ -99,0 +99,0 @@ expect(updatedJson['__hmac']).toBeUndefined(); |
@@ -5,2 +5,4 @@ const crypto = require('crypto'); | ||
const prefix = 'ENCRYPTED|'; | ||
const encryptionKey = 'CONFIG_ENCRYPTION_KEY'; | ||
const encryptionKeyNew = 'CONFIG_ENCRYPTION_KEY_NEW'; | ||
@@ -12,17 +14,19 @@ const decryptErrorMessage = `Decryption failed. Please check that the right key is used and the encrypted secret is valid and has the form "ENCRYPTED|IV|DATA" | ||
module.exports.DECRYPTION_ERROR = decryptErrorMessage; | ||
module.exports.CONFIG_ENCRYPTION_KEY = encryptionKey; | ||
module.exports.CONFIG_ENCRYPTION_KEY_NEW = encryptionKeyNew; | ||
module.exports.retrieveKey = function (verbose = false) { | ||
module.exports.retrieveKey = function (keyName, verbose = false) { | ||
const hexReg = new RegExp('^[0-9A-F]{64}$', 'i'); | ||
let result = null; | ||
if (!process.env.CONFIG_ENCRYPTION_KEY) { | ||
throw new Error('Environment variable CONFIG_ENCRYPTION_KEY not set.'); | ||
if (!process.env[keyName]) { | ||
throw new Error(`Environment variable ${keyName} not set.`); | ||
} | ||
else if (process.env.CONFIG_ENCRYPTION_KEY.toString().length == 32 || hexReg.test(process.env.CONFIG_ENCRYPTION_KEY.toString())) { | ||
result = process.env.CONFIG_ENCRYPTION_KEY.toString(); | ||
else if (process.env[keyName].toString().length == 32 || hexReg.test(process.env[keyName].toString())) { | ||
result = process.env[keyName].toString(); | ||
} | ||
else { | ||
throw new Error('CONFIG_ENCRYPTION_KEY length must be 32 bytes.'); | ||
throw new Error(`${keyName} length must be 32 bytes.`); | ||
} | ||
if (verbose) { | ||
console.log('CONFIG_ENCRYPTION_KEY found, using key: **************************' + result.slice(result.length - 6)); | ||
console.log(`${keyName} found, using key: **************************` + result.slice(result.length - 6)); | ||
} | ||
@@ -41,3 +45,3 @@ return result; | ||
} | ||
catch (error) { | ||
catch (_error) { | ||
throw new Error(decryptErrorMessage); | ||
@@ -44,0 +48,0 @@ } |
module.exports.createHelpText = ` | ||
If no patterns are specified with the -p option then the default patterns are used: \'user\',\'pass\',\'token\'. | ||
If no patterns are specified with the -p option then the default patterns are used: 'user','pass','token'. | ||
For every supplied pattern a case-insensitive regex match will be done for every key of the original JSON. | ||
@@ -28,2 +28,12 @@ If the match succeeds, the value of the key will be encrypted. | ||
module.exports.rotateHelpText = ` | ||
Loads an existing secure-config file and updates encryption as well as optional HMAC from old CONFIG_ENCRYPTION_KEY to CONFIG_ENCRYPTION_KEY_NEW. | ||
Both environment variables must be set. | ||
Examples: | ||
$ secure-config-tool rotate-key config.json > config-production.json | ||
$ secure-config-tool rotate-key -hp "_signature" -o config-production.json | ||
`; | ||
module.exports.testHelpText = ` | ||
@@ -30,0 +40,0 @@ Examples: |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
77882
37
1421
182
5
61
+ Addedcommander@12.1.0(transitive)
- Removedcommander@11.1.0(transitive)
Updatedcommander@^12.1.0