diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd140c6..8ca9a0d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.2.0...master) +## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.3.0...master) + +## [16.3.0](https://github.com/motdotla/dotenv/compare/v16.2.0...v16.3.0) (2023-06-16) + +### Added + +- Optionally pass `DOTENV_KEY` to options rather than relying on `process.env.DOTENV_KEY`. Defaults to `process.env.DOTENV_KEY` [#754](https://github.com/motdotla/dotenv/pull/754) ## [16.2.0](https://github.com/motdotla/dotenv/compare/v16.1.4...v16.2.0) (2023-06-15) diff --git a/README.md b/README.md index 885275c7..2d092938 100644 --- a/README.md +++ b/README.md @@ -339,6 +339,16 @@ console.log(myObject) // values from .env or .env.vault live here now. console.log(process.env) // this was not changed or written to ``` +##### DOTENV_KEY + +Default: `process.env.DOTENV_KEY` + +Pass the `DOTENV_KEY` directly to config options. Defaults to looking for `process.env.DOTENV_KEY` environment variable. Note this only applies to decrypting `.env.vault` files. If passed as null or undefined, or not passed at all, dotenv falls back to its traditional job of parsing a `.env` file. + +```js +require('dotenv').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production' }) +``` + ### Parse The engine which parses the contents of your file containing environment diff --git a/lib/cli-options.js b/lib/cli-options.js index b5012a89..09aca5bb 100644 --- a/lib/cli-options.js +++ b/lib/cli-options.js @@ -1,4 +1,4 @@ -const re = /^dotenv_config_(encoding|path|debug|override)=(.+)$/ +const re = /^dotenv_config_(encoding|path|debug|override|DOTENV_KEY)=(.+)$/ module.exports = function optionMatcher (args) { return args.reduce(function (acc, cur) { diff --git a/lib/env-options.js b/lib/env-options.js index 64e2db7b..7ebae3d7 100644 --- a/lib/env-options.js +++ b/lib/env-options.js @@ -17,4 +17,8 @@ if (process.env.DOTENV_CONFIG_OVERRIDE != null) { options.override = process.env.DOTENV_CONFIG_OVERRIDE } +if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) { + options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY +} + module.exports = options diff --git a/lib/main.js b/lib/main.js index 69cb939f..84f81a1e 100644 --- a/lib/main.js +++ b/lib/main.js @@ -58,7 +58,7 @@ function _parseVault (options) { // handle scenario for comma separated keys - for use with key rotation // example: DOTENV_KEY="dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenv.org/vault/.env.vault?environment=prod" - const keys = _dotenvKey().split(',') + const keys = _dotenvKey(options).split(',') const length = keys.length let decrypted @@ -99,11 +99,18 @@ function _debug (message) { console.log(`[dotenv@${version}][DEBUG] ${message}`) } -function _dotenvKey () { +function _dotenvKey (options) { + // prioritize developer directly setting options.DOTENV_KEY + if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) { + return options.DOTENV_KEY + } + + // secondary infra already contains a DOTENV_KEY environment variable if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) { return process.env.DOTENV_KEY } + // fallback to empty string return '' } @@ -212,7 +219,7 @@ function config (options) { const vaultPath = _vaultPath(options) // fallback to original dotenv if DOTENV_KEY is not set - if (_dotenvKey().length === 0) { + if (_dotenvKey(options).length === 0) { return DotenvModule.configDotenv(options) } diff --git a/tests/test-config-vault.js b/tests/test-config-vault.js index d8cd313c..160ffa58 100644 --- a/tests/test-config-vault.js +++ b/tests/test-config-vault.js @@ -194,6 +194,20 @@ t.test('does write over keys already in process.env if override turned on', ct = ct.equal(process.env.ALPHA, 'zeta') }) +t.test('when DOTENV_KEY is passed as an option it successfully decrypts and injects', ct => { + envStub.restore() + envStub = sinon.stub(process.env, 'DOTENV_KEY').value('') + + ct.plan(2) + + const result = dotenv.config({ path: testPath, DOTENV_KEY: dotenvKey }) + + ct.equal(result.parsed.ALPHA, 'zeta') + ct.equal(process.env.ALPHA, 'zeta') + + ct.end() +}) + t.test('can write to a different object rather than process.env', ct => { ct.plan(3) diff --git a/tests/test-env-options.js b/tests/test-env-options.js index cb6cacf5..7bfad616 100644 --- a/tests/test-env-options.js +++ b/tests/test-env-options.js @@ -9,6 +9,7 @@ const e = process.env.DOTENV_CONFIG_ENCODING const p = process.env.DOTENV_CONFIG_PATH const d = process.env.DOTENV_CONFIG_DEBUG const o = process.env.DOTENV_CONFIG_OVERRIDE +const dk = process.env.DOTENV_CONFIG_DOTENV_KEY // get fresh object for each test function options () { @@ -30,6 +31,7 @@ delete process.env.DOTENV_CONFIG_ENCODING delete process.env.DOTENV_CONFIG_PATH delete process.env.DOTENV_CONFIG_DEBUG delete process.env.DOTENV_CONFIG_OVERRIDE +delete process.env.DOTENV_CONFIG_DOTENV_KEY t.same(options(), {}) @@ -45,8 +47,12 @@ testOption('DOTENV_CONFIG_DEBUG', 'true', { debug: 'true' }) // sets override option testOption('DOTENV_CONFIG_OVERRIDE', 'true', { override: 'true' }) +// sets DOTENV_KEY option +testOption('DOTENV_CONFIG_DOTENV_KEY', 'dotenv://:key_ddcaa26504cd70a@dotenv.org/vault/.env.vault?environment=development', { DOTENV_KEY: 'dotenv://:key_ddcaa26504cd70a@dotenv.org/vault/.env.vault?environment=development' }) + // restore existing env process.env.DOTENV_CONFIG_ENCODING = e process.env.DOTENV_CONFIG_PATH = p process.env.DOTENV_CONFIG_DEBUG = d process.env.DOTENV_CONFIG_OVERRIDE = o +process.env.DOTENV_CONFIG_DOTENV_KEY = dk