env-verifier
Advanced tools
Comparing version 1.1.3 to 1.2.0
export interface MappedConfig { | ||
[key: string]: any | string | undefined | MappedConfig; | ||
[key: string]: any | string | undefined | MappedConfig | Secret; | ||
} | ||
@@ -9,2 +9,5 @@ export interface TransformFn { | ||
export interface ConfigWithEnvKeys { | ||
[key: string]: string | InsertValue | TransformTuple | SecretKey | ConfigWithEnvKeys; | ||
} | ||
export interface NotASecretObject { | ||
[key: string]: string | InsertValue | TransformTuple | ConfigWithEnvKeys; | ||
@@ -19,2 +22,13 @@ } | ||
} | ||
declare class SecretKey { | ||
secret: string; | ||
constructor(secret: string); | ||
} | ||
export declare class Secret { | ||
toJSON(): string; | ||
reveal: { | ||
(): string; | ||
}; | ||
constructor(secret: string); | ||
} | ||
export declare function verify(config: ConfigWithEnvKeys, env?: { | ||
@@ -30,2 +44,3 @@ [key: string]: string | undefined; | ||
export declare function insert(value: any): InsertValue; | ||
export declare function secret(envKey: string): SecretKey; | ||
export {}; |
@@ -8,47 +8,78 @@ "use strict"; | ||
} | ||
const recursiveVerify = ({ config, env, errors = [], path = '' }) => { | ||
const mapConf = (key) => { | ||
const value = config[key]; | ||
const subPath = path.length === 0 ? key : `${path}.${key}`; | ||
const getValueFromEnv = (key) => { | ||
const envValue = env[key]; | ||
if (envValue === undefined || envValue.length === 0) { | ||
errors.push(new Error(`environment value ${key} is missing from config object at ${subPath}`)); | ||
return undefined; | ||
} | ||
return envValue; | ||
}; | ||
if (value instanceof InsertValue) { | ||
return { [key]: value.value }; | ||
} | ||
else if (Array.isArray(value)) { | ||
const [envKey, transformFn] = value; | ||
const envValue = getValueFromEnv(envKey); | ||
const transforedValue = envValue && transformFn(envValue); | ||
return { [key]: transforedValue }; | ||
} | ||
else if (typeof value === 'string') { | ||
const envValue = getValueFromEnv(value); | ||
return { [key]: envValue }; | ||
} | ||
else { | ||
const { errors: subErrors, config: subConfig } = recursiveVerify({ | ||
config: value, | ||
env, | ||
path: subPath | ||
}); | ||
errors = errors.concat(subErrors); | ||
return { [key]: subConfig }; | ||
} | ||
}; | ||
const reduceConf = (acc, obj) => ({ | ||
...acc, | ||
...obj | ||
class SecretKey { | ||
constructor(secret) { | ||
this.secret = secret; | ||
} | ||
} | ||
class Secret { | ||
toJSON() { | ||
return '[secret]'; | ||
} | ||
constructor(secret) { | ||
this.reveal = () => secret; | ||
} | ||
} | ||
exports.Secret = Secret; | ||
const isNode = () => typeof process === 'object' && process + '' === '[object process]'; | ||
const getSecretObject = (secret) => { | ||
const secretObj = new Secret(secret); | ||
if (isNode()) { | ||
const util = require('util'); | ||
secretObj[util.inspect.custom] = () => '[secret]'; | ||
} | ||
return secretObj; | ||
}; | ||
const getEnvValueOrErrorCurried = (env, subPath) => (key) => { | ||
const envValue = env[key]; | ||
if (envValue === undefined || envValue.length === 0) { | ||
const error = new Error(`environment value ${key} is missing from config object at ${subPath}`); | ||
return [undefined, [error]]; | ||
} | ||
return [envValue, []]; | ||
}; | ||
const getMapConfigFunction = ({ config, env, path = '' }) => (key) => { | ||
const value = config[key]; | ||
const subPath = path.length === 0 ? key : `${path}.${key}`; | ||
const getEnvValueOrError = getEnvValueOrErrorCurried(env, subPath); | ||
if (value instanceof SecretKey) { | ||
const secretKey = value.secret; | ||
const [secretValue, errors] = getEnvValueOrError(secretKey); | ||
return [{ [key]: getSecretObject(secretValue) }, errors]; | ||
} | ||
if (value instanceof InsertValue) { | ||
return [{ [key]: value.value }, []]; | ||
} | ||
if (Array.isArray(value)) { | ||
const [envKey, transformFn] = value; | ||
const [envValue, errors] = getEnvValueOrError(envKey); | ||
const transforedValue = envValue && transformFn(envValue); | ||
return [{ [key]: transforedValue }, errors]; | ||
} | ||
if (typeof value === 'string') { | ||
const [envValue, errors] = getEnvValueOrError(value); | ||
return [{ [key]: envValue }, errors]; | ||
} | ||
const { errors, config: subConfig } = recursiveVerify({ | ||
config: value, | ||
env, | ||
path: subPath | ||
}); | ||
const mappedConf = Object.keys(config).map(mapConf); | ||
const newConfig = mappedConf.reduce(reduceConf, {}); | ||
return { config: newConfig, env, errors, path }; | ||
return [{ [key]: subConfig }, errors]; | ||
}; | ||
const reduceConf = (acc, [config, errors]) => { | ||
return { | ||
config: Object.assign({}, acc.config, config), | ||
errors: acc.errors.concat(errors) | ||
}; | ||
}; | ||
const recursiveVerify = (paramCollection) => { | ||
const mapConf = getMapConfigFunction(paramCollection); | ||
const mappedConf = Object.keys(paramCollection.config).map(mapConf); | ||
return mappedConf.reduce(reduceConf, { config: {}, errors: [] }); | ||
}; | ||
function verify(config, env = process.env) { | ||
const { config: builtConfig, errors } = recursiveVerify({ config, env }); | ||
const { config: builtConfig, errors } = recursiveVerify({ | ||
config, | ||
env | ||
}); | ||
const errorMessages = errors.map(({ message }) => message); | ||
@@ -70,2 +101,6 @@ return { config: builtConfig, errors: errorMessages }; | ||
exports.insert = insert; | ||
function secret(envKey) { | ||
return new SecretKey(envKey); | ||
} | ||
exports.secret = secret; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "env-verifier", | ||
"version": "1.1.3", | ||
"version": "1.2.0", | ||
"description": "\"Make sure you have all your env variables!\"", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -70,2 +70,3 @@ # env-verifier | ||
- [Arbitrary Value Insertion](#arbitraryValueInsertion) | ||
- [Secret Insertion](#secretInsertion) | ||
- [Error Generation and Reporting](#errorGenerationAndReporting) | ||
@@ -87,11 +88,11 @@ - [Variable Transformation (TransformTuple)](#variableTransformation) | ||
interface ConfigWithEnvKeys { | ||
[key: string]: string | InsertValue | TransformTuple | ConfigWithEnvKeys | ||
[key: string]: string | InsertValue | SecretKey | TransformTuple | ConfigWithEnvKeys | ||
} | ||
interface MappedConfig { | ||
[key: string]: any | string | undefined | Config | ||
[key: string]: any | string | undefined | Secret | Config | ||
} | ||
interface VerifiedConfig { | ||
[key: string]: any | string | VerifiedConfig | ||
[key: string]: any | string | Secret | VerifiedConfig | ||
} | ||
@@ -103,2 +104,8 @@ | ||
class Secret { | ||
reveal(): string | ||
} | ||
function secret(envKey: string): SecretKey | ||
function insert(value: any): InsertValue | ||
@@ -130,2 +137,65 @@ | ||
#### <a name="secretInsertion"><a/> Secret Insertion | ||
As of env-verifier version `1.2.0`, the obfuscation of env secrets is supported. | ||
by wrapping the env key of the secret in the `secret` function exported by `env-verifier`, the secret will be retrieved and wrapped in a `Secret` object (see function specification above). | ||
Note: support for transforming or inserting secrets is not supported at this time. | ||
To retrieve the secret, the `reveal` function can be called. | ||
What secret obfuscation will do: | ||
- protect secrets from casual logging of the produced config object | ||
- `JSON.stringify` of the config object will replace all secrets with the string `'[secret]'` | ||
What secret obfuscation will not do: | ||
- prevent the actually logging of the revealed secret | ||
- mutate the actual string returned from the `env` object | ||
```javascript | ||
const { verify, secret } = require('env-verifier') | ||
const env = { | ||
PASSWORD: 'superSecretPassword' | ||
} | ||
const { config } = verify({ | ||
password: secret('PASSWORD') | ||
... // other env key names | ||
}, env) | ||
module.exports = config | ||
//exports: | ||
{ | ||
password: { | ||
reveal(): string | ||
} | ||
... // other env values | ||
} | ||
config.password.reveal() | ||
// returns: | ||
'superSecretPassword' | ||
console.log(config) | ||
// prints: | ||
// { | ||
// if you're using nodejs: | ||
// password: [secret] | ||
// if you're using other JS environments: | ||
// password: Secret { reveal: [Function] } | ||
// ... other env values | ||
// } | ||
JSON.stringify(config) | ||
// returns | ||
// { | ||
// "password": "[secret]" | ||
// ... other env values | ||
// } | ||
``` | ||
#### <a name="errorGenerationAndReporting"><a/> Error Generation and Reporting | ||
@@ -161,4 +231,6 @@ | ||
This package works best with projects that have centralized config files, IE: You map your `.env` variables to a `config` object in a file, and `import`/`require` that config object wherever you need `.env` values. | ||
This package is written in TypeScript and is built/distributed for environments that support the majority of the es2016 specification. | ||
This package also works best with projects that have centralized config files, IE: You map your `.env` variables to a `config` object in a file, and `import`/`require` that config object wherever you need `.env` values. | ||
Other than that, just install the package and get going! | ||
@@ -165,0 +237,0 @@ |
@@ -9,3 +9,3 @@ { | ||
"module": "commonjs", | ||
"target": "es2018", | ||
"target": "es2016", | ||
"sourceMap": true, | ||
@@ -22,4 +22,5 @@ "inlineSources": true, | ||
"ts-node", | ||
"dist" | ||
"dist", | ||
"./**/*.spec.ts" | ||
] | ||
} |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
270
27325
11
176
3
1