@dotenvx/dotenvx 1.74.2 → 1.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -1
- package/README.md +70 -22
- package/package.json +2 -2
- package/src/cli/commands/ext.js +2 -2
- package/src/cli/dotenvx.js +29 -27
- package/src/cli/examples.js +8 -8
- package/src/db/device.js +3 -9
- package/src/lib/helpers/cryptography/decryptKeyValue.js +10 -10
- package/src/lib/helpers/cryptography/encryptValue.js +2 -7
- package/src/lib/helpers/cryptography/index.js +0 -1
- package/src/lib/helpers/cryptography/localKeypair.js +6 -13
- package/src/lib/helpers/decryptDeviceValue.js +2 -5
- package/src/lib/helpers/encryptDeviceValue.js +2 -4
- package/src/lib/helpers/installPrecommitHook.js +9 -7
- package/src/lib/helpers/isFullyEncrypted.js +2 -2
- package/src/lib/helpers/parse.js +2 -2
- package/src/lib/services/armorPush.js +2 -2
- package/src/lib/services/decrypt.js +5 -5
- package/src/lib/services/encrypt.js +4 -4
- package/src/lib/services/genexample.js +1 -1
- package/src/lib/services/prebuild.js +3 -3
- package/src/lib/services/precommit.js +3 -3
- package/src/lib/services/rotate.js +3 -3
- package/src/lib/services/sets.js +3 -3
- package/src/lib/helpers/cryptography/isEncrypted.js +0 -7
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
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.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.75.0...main)
|
|
6
|
+
|
|
7
|
+
## [1.75.0](https://github.com/dotenvx/dotenvx/compare/v1.74.4...v1.75.0) (2026-06-19)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Swap in `@dotenvx/primitives` ([#848](https://github.com/dotenvx/dotenvx/pull/848))
|
|
12
|
+
* `ext` commands become first-class([#847](https://github.com/dotenvx/dotenvx/pull/847))
|
|
13
|
+
|
|
14
|
+
## [1.74.4](https://github.com/dotenvx/dotenvx/compare/v1.74.3...v1.74.4) (2026-06-19)
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
* Remove separately published `@dotenvx/next-env` from this repo ([#846](https://github.com/dotenvx/dotenvx/pull/846))
|
|
19
|
+
|
|
20
|
+
## [1.74.3](https://github.com/dotenvx/dotenvx/compare/v1.74.2...v1.74.3) (2026-06-19)
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
* Bundle dotenvx with `@dotenvx/next-env` for user convenience ([#845](https://github.com/dotenvx/dotenvx/pull/845))
|
|
6
25
|
|
|
7
26
|
## [1.74.2](https://github.com/dotenvx/dotenvx/compare/v1.74.1...v1.74.2) (2026-06-18)
|
|
8
27
|
|
package/README.md
CHANGED
|
@@ -150,6 +150,47 @@ $ dotenvx run -- npx tsx index.ts
|
|
|
150
150
|
Hello Dotenvx
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
+
</details>
|
|
154
|
+
<details><summary>Next.js ▲</summary><br>
|
|
155
|
+
|
|
156
|
+
Install Dotenvx and `@dotenvx/next-env`.
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
$ npm install @dotenvx/dotenvx
|
|
160
|
+
$ npm install @dotenvx/next-env
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Override `@next/env` in your `package.json`.
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"overrides": {
|
|
168
|
+
"@next/env": "npm:@dotenvx/next-env"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Encrypt your `.env` file.
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
$ npx dotenvx encrypt
|
|
177
|
+
◈ encrypted (.env)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Your encrypted secrets are automatically injected and readable in Next.js.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { NextResponse } from 'next/server'
|
|
184
|
+
|
|
185
|
+
export async function GET() {
|
|
186
|
+
return NextResponse.json({
|
|
187
|
+
HELLO: process.env.HELLO
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Set `DOTENV_PRIVATE_KEY` in production before deploying.
|
|
193
|
+
|
|
153
194
|
</details>
|
|
154
195
|
<details><summary>Cloudflare Workers ⛅️</summary><br>
|
|
155
196
|
|
|
@@ -1613,7 +1654,7 @@ $ echo "HELLO=Dotenvx" > .env
|
|
|
1613
1654
|
|
|
1614
1655
|
$ dotenvx encrypt
|
|
1615
1656
|
◈ encrypted (.env) + local key (.env.keys)
|
|
1616
|
-
⮕ next run [dotenvx
|
|
1657
|
+
⮕ next run [dotenvx gitignore --pattern .env.keys] to gitignore .env.keys
|
|
1617
1658
|
⮕ next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally
|
|
1618
1659
|
```
|
|
1619
1660
|
|
|
@@ -1628,7 +1669,7 @@ $ echo "HELLO=Production" > .env.production
|
|
|
1628
1669
|
|
|
1629
1670
|
$ dotenvx encrypt -f .env.production
|
|
1630
1671
|
◈ encrypted (.env.production) + local key (.env.keys)
|
|
1631
|
-
⮕ next run [dotenvx
|
|
1672
|
+
⮕ next run [dotenvx gitignore --pattern .env.keys] to gitignore .env.keys
|
|
1632
1673
|
⮕ next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally
|
|
1633
1674
|
```
|
|
1634
1675
|
|
|
@@ -2121,12 +2162,19 @@ Commands:
|
|
|
2121
2162
|
set <KEY> <value> set a single environment variable
|
|
2122
2163
|
encrypt encrypt .env file(s)
|
|
2123
2164
|
decrypt decrypt .env file(s)
|
|
2165
|
+
rotate rotate keypair(s) and re-encrypt .env file(s)
|
|
2124
2166
|
keypair [KEY] print public/private keys for .env file(s)
|
|
2125
2167
|
ls [directory] print all .env files in a tree structure
|
|
2168
|
+
gitignore append to .gitignore
|
|
2169
|
+
precommit [directory]
|
|
2170
|
+
prevent committing .env files to code
|
|
2171
|
+
prebuild [directory]
|
|
2172
|
+
prevent including .env files in docker
|
|
2126
2173
|
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2174
|
+
Professional Security:
|
|
2175
|
+
login log in to move keys off-device, share with your team, and audit access
|
|
2176
|
+
logout log out of connected security features
|
|
2177
|
+
armor ⛨ move private keys off-device [www.dotenvx.com/armor]
|
|
2130
2178
|
```
|
|
2131
2179
|
|
|
2132
2180
|
You can get more detailed help per command with `dotenvx help COMMAND`.
|
|
@@ -2174,9 +2222,9 @@ X.X.X
|
|
|
2174
2222
|
|
|
2175
2223
|
</details>
|
|
2176
2224
|
|
|
2177
|
-
###
|
|
2225
|
+
### Utilities
|
|
2178
2226
|
|
|
2179
|
-
|
|
2227
|
+
Additional workflow helpers.
|
|
2180
2228
|
|
|
2181
2229
|
<details><summary>`ext genexample`</summary><br>
|
|
2182
2230
|
|
|
@@ -2233,47 +2281,47 @@ HELLO=""
|
|
|
2233
2281
|
```
|
|
2234
2282
|
|
|
2235
2283
|
</details>
|
|
2236
|
-
<details><summary>`
|
|
2284
|
+
<details><summary>`gitignore`</summary><br>
|
|
2237
2285
|
|
|
2238
2286
|
Gitignore your `.env` files.
|
|
2239
2287
|
|
|
2240
2288
|
```sh
|
|
2241
|
-
$ dotenvx
|
|
2289
|
+
$ dotenvx gitignore
|
|
2242
2290
|
▣ ignored .env* (.gitignore)
|
|
2243
2291
|
```
|
|
2244
2292
|
|
|
2245
2293
|
</details>
|
|
2246
|
-
<details><summary>`
|
|
2294
|
+
<details><summary>`gitignore --pattern`</summary><br>
|
|
2247
2295
|
|
|
2248
2296
|
Gitignore specific pattern(s) of `.env` files.
|
|
2249
2297
|
|
|
2250
2298
|
```sh
|
|
2251
|
-
$ dotenvx
|
|
2299
|
+
$ dotenvx gitignore --pattern .env.keys
|
|
2252
2300
|
▣ ignored .env.keys (.gitignore)
|
|
2253
2301
|
```
|
|
2254
2302
|
|
|
2255
2303
|
</details>
|
|
2256
|
-
<details><summary>`
|
|
2304
|
+
<details><summary>`precommit`</summary><br>
|
|
2257
2305
|
|
|
2258
2306
|
Prevent `.env` files from being committed to code.
|
|
2259
2307
|
|
|
2260
2308
|
```sh
|
|
2261
|
-
$ dotenvx
|
|
2309
|
+
$ dotenvx precommit
|
|
2262
2310
|
▣ .env files (1) protected (encrypted or gitignored)
|
|
2263
2311
|
```
|
|
2264
2312
|
|
|
2265
2313
|
</details>
|
|
2266
|
-
<details><summary>`
|
|
2314
|
+
<details><summary>`precommit --install`</summary><br>
|
|
2267
2315
|
|
|
2268
2316
|
Install a shell script to `.git/hooks/pre-commit` to prevent accidentally committing any `.env` files to source control.
|
|
2269
2317
|
|
|
2270
2318
|
```sh
|
|
2271
|
-
$ dotenvx
|
|
2272
|
-
▣ dotenvx
|
|
2319
|
+
$ dotenvx precommit --install
|
|
2320
|
+
▣ dotenvx precommit installed [.git/hooks/pre-commit]
|
|
2273
2321
|
```
|
|
2274
2322
|
|
|
2275
2323
|
</details>
|
|
2276
|
-
<details><summary>`
|
|
2324
|
+
<details><summary>`precommit directory`</summary><br>
|
|
2277
2325
|
|
|
2278
2326
|
Prevent `.env` files from being committed to code inside a specified path to a directory.
|
|
2279
2327
|
|
|
@@ -2282,12 +2330,12 @@ $ echo "HELLO=Dotenvx" > .env
|
|
|
2282
2330
|
$ mkdir -p apps/backend
|
|
2283
2331
|
$ echo "HELLO=Backend" > apps/backend/.env
|
|
2284
2332
|
|
|
2285
|
-
$ dotenvx
|
|
2333
|
+
$ dotenvx precommit apps/backend
|
|
2286
2334
|
▣ apps/backend/.env not protected (encrypted or gitignored)
|
|
2287
2335
|
```
|
|
2288
2336
|
|
|
2289
2337
|
</details>
|
|
2290
|
-
<details><summary>`
|
|
2338
|
+
<details><summary>`prebuild`</summary><br>
|
|
2291
2339
|
|
|
2292
2340
|
Prevent `.env` files from being built into your docker containers.
|
|
2293
2341
|
|
|
@@ -2302,12 +2350,12 @@ COPY --from=dotenv/dotenvx:latest /usr/local/bin/dotenvx /bin/local/bin
|
|
|
2302
2350
|
|
|
2303
2351
|
# ... orther container commands
|
|
2304
2352
|
|
|
2305
|
-
RUN dotenvx
|
|
2353
|
+
RUN dotenvx prebuild
|
|
2306
2354
|
CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "index.js"]
|
|
2307
2355
|
```
|
|
2308
2356
|
|
|
2309
2357
|
</details>
|
|
2310
|
-
<details><summary>`
|
|
2358
|
+
<details><summary>`prebuild directory`</summary><br>
|
|
2311
2359
|
|
|
2312
2360
|
Prevent `.env` files from being built into your docker containers inside a specified path to a directory.
|
|
2313
2361
|
|
|
@@ -2322,7 +2370,7 @@ COPY --from=dotenv/dotenvx:latest /usr/local/bin/dotenvx /bin/local/bin
|
|
|
2322
2370
|
|
|
2323
2371
|
# ... orther container commands
|
|
2324
2372
|
|
|
2325
|
-
RUN dotenvx
|
|
2373
|
+
RUN dotenvx prebuild apps/backend
|
|
2326
2374
|
CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "apps/backend/index.js"]
|
|
2327
2375
|
```
|
|
2328
2376
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.75.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -43,10 +43,10 @@
|
|
|
43
43
|
},
|
|
44
44
|
"funding": "https://dotenvx.com",
|
|
45
45
|
"dependencies": {
|
|
46
|
+
"@dotenvx/primitives": "^0.7.1",
|
|
46
47
|
"commander": "^11.1.0",
|
|
47
48
|
"conf": "^10.2.0",
|
|
48
49
|
"dotenv": "^17.2.1",
|
|
49
|
-
"eciesjs": "^0.4.10",
|
|
50
50
|
"enquirer": "^2.4.1",
|
|
51
51
|
"env-paths": "^2.2.1",
|
|
52
52
|
"execa": "^5.1.1",
|
package/src/cli/commands/ext.js
CHANGED
|
@@ -39,7 +39,7 @@ ext.command('genexample')
|
|
|
39
39
|
|
|
40
40
|
// dotenvx ext gitignore
|
|
41
41
|
ext.command('gitignore')
|
|
42
|
-
.description('append to .gitignore
|
|
42
|
+
.description('append to .gitignore')
|
|
43
43
|
.addHelpText('after', examples.gitignore)
|
|
44
44
|
.option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
|
|
45
45
|
.action(function (...args) {
|
|
@@ -48,7 +48,7 @@ ext.command('gitignore')
|
|
|
48
48
|
|
|
49
49
|
// dotenvx ext prebuild
|
|
50
50
|
ext.command('prebuild')
|
|
51
|
-
.description('prevent including .env files in docker
|
|
51
|
+
.description('prevent including .env files in docker')
|
|
52
52
|
.addHelpText('after', examples.prebuild)
|
|
53
53
|
.argument('[directory]', 'directory to prevent including .env files from', '.')
|
|
54
54
|
.action(function (...args) {
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -194,6 +194,34 @@ program.command('ls')
|
|
|
194
194
|
return require('./actions/ls').apply(this, args)
|
|
195
195
|
})
|
|
196
196
|
|
|
197
|
+
// dotenvx gitignore
|
|
198
|
+
program.command('gitignore')
|
|
199
|
+
.description('append to .gitignore')
|
|
200
|
+
.addHelpText('after', examples.gitignore)
|
|
201
|
+
.option('--pattern <patterns...>', 'pattern(s) to gitignore', ['.env*'])
|
|
202
|
+
.action(function (...args) {
|
|
203
|
+
return require('./actions/ext/gitignore').apply(this, args)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// dotenvx precommit
|
|
207
|
+
program.command('precommit')
|
|
208
|
+
.description('prevent committing .env files to code')
|
|
209
|
+
.addHelpText('after', examples.precommit)
|
|
210
|
+
.argument('[directory]', 'directory to prevent committing .env files from', '.')
|
|
211
|
+
.option('-i, --install', 'install to .git/hooks/pre-commit')
|
|
212
|
+
.action(function (...args) {
|
|
213
|
+
return require('./actions/ext/precommit').apply(this, args)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// dotenvx prebuild
|
|
217
|
+
program.command('prebuild')
|
|
218
|
+
.description('prevent including .env files in docker')
|
|
219
|
+
.addHelpText('after', examples.prebuild)
|
|
220
|
+
.argument('[directory]', 'directory to prevent including .env files from', '.')
|
|
221
|
+
.action(function (...args) {
|
|
222
|
+
return require('./actions/ext/prebuild').apply(this, args)
|
|
223
|
+
})
|
|
224
|
+
|
|
197
225
|
// dotenvx doctor
|
|
198
226
|
program.command('doctor', { hidden: true })
|
|
199
227
|
.description('scan for dotenv loaders')
|
|
@@ -242,7 +270,6 @@ program.addHelpText('after', 'Professional Security: ')
|
|
|
242
270
|
program.addHelpText('after', ' login log in to move keys off-device, share with your team, and audit access')
|
|
243
271
|
program.addHelpText('after', ' logout log out of connected security features')
|
|
244
272
|
program.addHelpText('after', ' armor ⛨ move private keys off-device [www.dotenvx.com/armor]')
|
|
245
|
-
program.addHelpText('after', ' ext ⊕ extensions')
|
|
246
273
|
|
|
247
274
|
// dotenvx armor
|
|
248
275
|
require('./commands/armor')(program.command('armor', { hidden: true }))
|
|
@@ -250,30 +277,6 @@ require('./commands/armor')(program.command('armor', { hidden: true }))
|
|
|
250
277
|
// dotenvx ext
|
|
251
278
|
program.addCommand(require('./commands/ext'))
|
|
252
279
|
|
|
253
|
-
//
|
|
254
|
-
// MOVED
|
|
255
|
-
//
|
|
256
|
-
program.command('prebuild')
|
|
257
|
-
.description('DEPRECATED: moved to [dotenvx ext prebuild]')
|
|
258
|
-
.addHelpText('after', examples.prebuild)
|
|
259
|
-
.argument('[directory]', 'directory to prevent including .env files from', '.')
|
|
260
|
-
.action(function (...args) {
|
|
261
|
-
logger.warn('DEPRECATION NOTICE: [prebuild] has moved to [dotenvx ext prebuild]')
|
|
262
|
-
|
|
263
|
-
require('./actions/ext/prebuild').apply(this, args)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
program.command('precommit')
|
|
267
|
-
.description('DEPRECATED: moved to [dotenvx ext precommit]')
|
|
268
|
-
.addHelpText('after', examples.precommit)
|
|
269
|
-
.argument('[directory]', 'directory to prevent committing .env files from', '.')
|
|
270
|
-
.option('-i, --install', 'install to .git/hooks/pre-commit')
|
|
271
|
-
.action(function (...args) {
|
|
272
|
-
logger.warn('DEPRECATION NOTICE: [precommit] has moved to [dotenvx ext precommit]')
|
|
273
|
-
|
|
274
|
-
require('./actions/ext/precommit').apply(this, args)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
280
|
// override helpInformation to hide DEPRECATED and 'ext' commands
|
|
278
281
|
program.helpInformation = function () {
|
|
279
282
|
const originalHelp = Command.prototype.helpInformation.call(this)
|
|
@@ -286,8 +289,7 @@ program.helpInformation = function () {
|
|
|
286
289
|
const filteredLines = lines.filter(line =>
|
|
287
290
|
!line.includes('DEPRECATED') &&
|
|
288
291
|
!line.includes('help [command]') &&
|
|
289
|
-
!line.includes('🔌 extensions')
|
|
290
|
-
!/^\s*ls\b/.test(line)
|
|
292
|
+
!line.includes('🔌 extensions')
|
|
291
293
|
)
|
|
292
294
|
|
|
293
295
|
return filteredLines.join('\n')
|
package/src/cli/examples.js
CHANGED
|
@@ -27,14 +27,14 @@ const precommit = function () {
|
|
|
27
27
|
Examples:
|
|
28
28
|
|
|
29
29
|
\`\`\`
|
|
30
|
-
$ dotenvx
|
|
31
|
-
$ dotenvx
|
|
30
|
+
$ dotenvx precommit
|
|
31
|
+
$ dotenvx precommit --install
|
|
32
32
|
\`\`\`
|
|
33
33
|
|
|
34
34
|
Try it:
|
|
35
35
|
|
|
36
36
|
\`\`\`
|
|
37
|
-
$ dotenvx
|
|
37
|
+
$ dotenvx precommit
|
|
38
38
|
[dotenvx@0.45.0][precommit] success
|
|
39
39
|
\`\`\`
|
|
40
40
|
`
|
|
@@ -45,13 +45,13 @@ const prebuild = function () {
|
|
|
45
45
|
Examples:
|
|
46
46
|
|
|
47
47
|
\`\`\`
|
|
48
|
-
$ dotenvx
|
|
48
|
+
$ dotenvx prebuild
|
|
49
49
|
\`\`\`
|
|
50
50
|
|
|
51
51
|
Try it:
|
|
52
52
|
|
|
53
53
|
\`\`\`
|
|
54
|
-
$ dotenvx
|
|
54
|
+
$ dotenvx prebuild
|
|
55
55
|
[dotenvx@0.10.0][prebuild] success
|
|
56
56
|
\`\`\`
|
|
57
57
|
`
|
|
@@ -62,14 +62,14 @@ const gitignore = function () {
|
|
|
62
62
|
Examples:
|
|
63
63
|
|
|
64
64
|
\`\`\`
|
|
65
|
-
$ dotenvx
|
|
66
|
-
$ dotenvx
|
|
65
|
+
$ dotenvx gitignore
|
|
66
|
+
$ dotenvx gitignore --pattern .env.keys
|
|
67
67
|
\`\`\`
|
|
68
68
|
|
|
69
69
|
Try it:
|
|
70
70
|
|
|
71
71
|
\`\`\`
|
|
72
|
-
$ dotenvx
|
|
72
|
+
$ dotenvx gitignore
|
|
73
73
|
▣ ignored .env* (.gitignore)
|
|
74
74
|
\`\`\`
|
|
75
75
|
`
|
package/src/db/device.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const Conf = require('conf')
|
|
2
|
-
const {
|
|
2
|
+
const { derive, keypair } = require('@dotenvx/primitives')
|
|
3
3
|
|
|
4
4
|
const encryptDeviceValue = require('./../lib/helpers/encryptDeviceValue')
|
|
5
5
|
const decryptDeviceValue = require('./../lib/helpers/decryptDeviceValue')
|
|
@@ -38,9 +38,7 @@ class Device {
|
|
|
38
38
|
return currentPrivateKey
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
const kp = new PrivateKey()
|
|
43
|
-
const _privateKey = kp.secret.toString('hex')
|
|
41
|
+
const _privateKey = keypair().privateKey
|
|
44
42
|
|
|
45
43
|
this.store.set('private_key/1', _privateKey)
|
|
46
44
|
|
|
@@ -54,11 +52,7 @@ class Device {
|
|
|
54
52
|
return ''
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
|
|
58
|
-
const _privateKey = new PrivateKey(Buffer.from(privateKeyHex, 'hex'))
|
|
59
|
-
|
|
60
|
-
// compute publicKey from privateKey
|
|
61
|
-
return _privateKey.publicKey.toHex()
|
|
55
|
+
return derive(privateKeyHex)
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
encrypt (value) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
const { decrypt } = require('
|
|
1
|
+
const { decrypt } = require('@dotenvx/primitives')
|
|
2
2
|
|
|
3
3
|
const Errors = require('./../errors')
|
|
4
4
|
|
|
5
5
|
const PREFIX = 'encrypted:'
|
|
6
6
|
|
|
7
|
+
function primitiveMessage (error) {
|
|
8
|
+
return error.message.replace(/^\[[A-Z_]+\] /, '')
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
function decryptKeyValue (key, value, privateKeyName, privateKey) {
|
|
8
12
|
let decryptedValue
|
|
9
13
|
let decryptionError
|
|
@@ -18,23 +22,19 @@ function decryptKeyValue (key, value, privateKeyName, privateKey) {
|
|
|
18
22
|
} else {
|
|
19
23
|
const privateKeys = privateKey.split(',')
|
|
20
24
|
for (const privKey of privateKeys) {
|
|
21
|
-
const secret = Buffer.from(privKey, 'hex')
|
|
22
|
-
const encoded = value.substring(PREFIX.length)
|
|
23
|
-
const ciphertext = Buffer.from(encoded, 'base64')
|
|
24
|
-
|
|
25
25
|
try {
|
|
26
|
-
decryptedValue = decrypt(
|
|
26
|
+
decryptedValue = decrypt(privKey, value)
|
|
27
27
|
decryptionError = null // reset to null error (scenario for multiple private keys)
|
|
28
28
|
break
|
|
29
29
|
} catch (e) {
|
|
30
|
-
if (e.
|
|
30
|
+
if (e.code === 'INVALID_PRIVATE_KEY') {
|
|
31
31
|
decryptionError = new Errors({ key, privateKeyName, privateKey }).invalidPrivateKey()
|
|
32
|
-
} else if (e.
|
|
32
|
+
} else if (e.code === 'WRONG_PRIVATE_KEY') {
|
|
33
33
|
decryptionError = new Errors({ key, privateKeyName, privateKey }).wrongPrivateKey()
|
|
34
|
-
} else if (e.
|
|
34
|
+
} else if (e.code === 'MALFORMED_ENCRYPTED_DATA' || primitiveMessage(e).startsWith('bad point:')) {
|
|
35
35
|
decryptionError = new Errors({ key, privateKeyName, privateKey }).malformedEncryptedData()
|
|
36
36
|
} else {
|
|
37
|
-
decryptionError = new Errors({ key, privateKeyName, privateKey, message: e
|
|
37
|
+
decryptionError = new Errors({ key, privateKeyName, privateKey, message: primitiveMessage(e) }).decryptionFailed()
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
const { encrypt } = require('
|
|
2
|
-
|
|
3
|
-
const PREFIX = 'encrypted:'
|
|
1
|
+
const { encrypt } = require('@dotenvx/primitives')
|
|
4
2
|
|
|
5
3
|
function encryptValue (value, publicKey) {
|
|
6
|
-
|
|
7
|
-
const encoded = Buffer.from(ciphertext, 'hex').toString('base64') // base64 encode ciphertext
|
|
8
|
-
|
|
9
|
-
return `${PREFIX}${encoded}`
|
|
4
|
+
return encrypt(publicKey, value)
|
|
10
5
|
}
|
|
11
6
|
|
|
12
7
|
module.exports = encryptValue
|
|
@@ -4,7 +4,6 @@ module.exports = {
|
|
|
4
4
|
localKeypair: require('./localKeypair'),
|
|
5
5
|
encryptValue: require('./encryptValue'),
|
|
6
6
|
decryptKeyValue: require('./decryptKeyValue'),
|
|
7
|
-
isEncrypted: require('./isEncrypted'),
|
|
8
7
|
isPublicKey: require('./isPublicKey'),
|
|
9
8
|
mutateKeysSrc: require('./mutateKeysSrc'),
|
|
10
9
|
mutateKeysSrcSync: require('./mutateKeysSrcSync'),
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { derive, keypair } = require('@dotenvx/primitives')
|
|
2
2
|
|
|
3
3
|
function localKeypair (existingPrivateKey) {
|
|
4
|
-
let kp
|
|
5
|
-
|
|
6
4
|
if (existingPrivateKey) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
return {
|
|
6
|
+
publicKey: derive(existingPrivateKey),
|
|
7
|
+
privateKey: existingPrivateKey
|
|
8
|
+
}
|
|
10
9
|
}
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
const privateKey = kp.secret.toString('hex')
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
publicKey,
|
|
17
|
-
privateKey
|
|
18
|
-
}
|
|
11
|
+
return keypair()
|
|
19
12
|
}
|
|
20
13
|
|
|
21
14
|
module.exports = localKeypair
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
const { decrypt } = require('
|
|
1
|
+
const { decrypt } = require('@dotenvx/primitives')
|
|
2
2
|
|
|
3
3
|
function decryptDeviceValue (value, privateKey) {
|
|
4
|
-
|
|
5
|
-
const ciphertext = Buffer.from(value, 'base64')
|
|
6
|
-
|
|
7
|
-
return decrypt(secret, ciphertext).toString()
|
|
4
|
+
return decrypt(privateKey, value, { prefix: false })
|
|
8
5
|
}
|
|
9
6
|
|
|
10
7
|
module.exports = decryptDeviceValue
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
const { encrypt } = require('
|
|
1
|
+
const { encrypt } = require('@dotenvx/primitives')
|
|
2
2
|
|
|
3
3
|
function encryptDeviceValue (value, publicKey) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return Buffer.from(ciphertext, 'hex').toString('base64')
|
|
4
|
+
return encrypt(publicKey, value, { prefix: false })
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
module.exports = encryptDeviceValue
|
|
@@ -6,10 +6,10 @@ const HOOK_SCRIPT = `#!/bin/sh
|
|
|
6
6
|
|
|
7
7
|
if command -v dotenvx 2>&1 >/dev/null
|
|
8
8
|
then
|
|
9
|
-
dotenvx
|
|
9
|
+
dotenvx precommit
|
|
10
10
|
elif npx dotenvx -V >/dev/null 2>&1
|
|
11
11
|
then
|
|
12
|
-
npx dotenvx
|
|
12
|
+
npx dotenvx precommit
|
|
13
13
|
else
|
|
14
14
|
if [ -t 2 ]; then
|
|
15
15
|
RED="$(printf '\\033[31m')"
|
|
@@ -19,7 +19,7 @@ else
|
|
|
19
19
|
RESET=""
|
|
20
20
|
fi
|
|
21
21
|
|
|
22
|
-
printf "%s☠ 'dotenvx
|
|
22
|
+
printf "%s☠ 'dotenvx precommit' command not found (.git/hooks/precommit) fix: [curl -sfS https://dotenvx.sh | sh]%s\n" "$RED" "$RESET" >&2
|
|
23
23
|
exit 1
|
|
24
24
|
fi
|
|
25
25
|
`
|
|
@@ -35,17 +35,19 @@ class InstallPrecommitHook {
|
|
|
35
35
|
try {
|
|
36
36
|
// Check if the pre-commit file already exists
|
|
37
37
|
if (this._exists()) {
|
|
38
|
+
const currentHook = this._currentHook()
|
|
39
|
+
|
|
38
40
|
// Check if 'dotenvx precommit' already exists in the file
|
|
39
|
-
if (
|
|
41
|
+
if (currentHook.includes('dotenvx precommit') || currentHook.includes('dotenvx ext precommit')) {
|
|
40
42
|
// do nothing
|
|
41
|
-
successMessage = `▣ dotenvx
|
|
43
|
+
successMessage = `▣ dotenvx precommit exists [${this.hookPath}]`
|
|
42
44
|
} else {
|
|
43
45
|
this._appendHook()
|
|
44
|
-
successMessage = `▣ dotenvx
|
|
46
|
+
successMessage = `▣ dotenvx precommit appended [${this.hookPath}]`
|
|
45
47
|
}
|
|
46
48
|
} else {
|
|
47
49
|
this._createHook()
|
|
48
|
-
successMessage = `▣ dotenvx
|
|
50
|
+
successMessage = `▣ dotenvx precommit installed [${this.hookPath}]`
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
1
2
|
const dotenvParse = require('./dotenvParse')
|
|
2
|
-
const isEncrypted = require('./cryptography/isEncrypted')
|
|
3
3
|
const isPublicKey = require('./cryptography/isPublicKey')
|
|
4
4
|
|
|
5
5
|
function isFullyEncrypted (src) {
|
|
@@ -14,7 +14,7 @@ function isFullyEncrypted (src) {
|
|
|
14
14
|
//
|
|
15
15
|
// key => [value1, ...]
|
|
16
16
|
for (const value of values) {
|
|
17
|
-
const result =
|
|
17
|
+
const result = encrypted(value) || isPublicKey(key)
|
|
18
18
|
if (!result) {
|
|
19
19
|
return false
|
|
20
20
|
}
|
package/src/lib/helpers/parse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const decryptKeyValue = require('./cryptography/decryptKeyValue')
|
|
2
|
-
const
|
|
2
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
3
3
|
const evalKeyValue = require('./evalKeyValue')
|
|
4
4
|
const resolveEscapeSequences = require('./resolveEscapeSequences')
|
|
5
5
|
|
|
@@ -45,7 +45,7 @@ class Parse {
|
|
|
45
45
|
this.errors.push(e)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const encryptedPrefixed =
|
|
48
|
+
const encryptedPrefixed = encrypted(this.parsed[key]) // do not eval encrypted prefix
|
|
49
49
|
|
|
50
50
|
// eval empty, double, or backticks
|
|
51
51
|
let evaled = false
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { derive } = require('@dotenvx/primitives')
|
|
2
2
|
const prompts = require('../helpers/prompts')
|
|
3
3
|
const PostArmorPush = require('../api/postArmorPush')
|
|
4
4
|
const keyNamesForEnvFile = require('../helpers/keyResolution/keyNamesForEnvFile')
|
|
@@ -7,7 +7,7 @@ const teamChoicesFromMeta = require('../helpers/teamChoicesFromMeta')
|
|
|
7
7
|
|
|
8
8
|
function publicKeyFromPrivateKey (privateKey) {
|
|
9
9
|
try {
|
|
10
|
-
return
|
|
10
|
+
return derive(privateKey)
|
|
11
11
|
} catch {
|
|
12
12
|
return ''
|
|
13
13
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fsx = require('./../helpers/fsx')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const picomatch = require('picomatch')
|
|
4
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
4
5
|
|
|
5
6
|
const TYPE_ENV_FILE = 'envFile'
|
|
6
7
|
|
|
@@ -16,8 +17,7 @@ const {
|
|
|
16
17
|
} = require('./../helpers/keyResolution')
|
|
17
18
|
|
|
18
19
|
const {
|
|
19
|
-
decryptKeyValue
|
|
20
|
-
isEncrypted
|
|
20
|
+
decryptKeyValue
|
|
21
21
|
} = require('./../helpers/cryptography')
|
|
22
22
|
|
|
23
23
|
const replace = require('./../helpers/replace')
|
|
@@ -101,12 +101,12 @@ class Decrypt {
|
|
|
101
101
|
continue
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
104
|
+
const hasEncrypted = values.some(value => encrypted(value))
|
|
105
|
+
if (hasEncrypted) {
|
|
106
106
|
row.keys.push(key) // track key(s)
|
|
107
107
|
|
|
108
108
|
const decryptedValues = values.map(value => {
|
|
109
|
-
if (!
|
|
109
|
+
if (!encrypted(value)) {
|
|
110
110
|
return value
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fsx = require('./../helpers/fsx')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const picomatch = require('picomatch')
|
|
4
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
4
5
|
|
|
5
6
|
const TYPE_ENV_FILE = 'envFile'
|
|
6
7
|
|
|
@@ -17,7 +18,6 @@ const {
|
|
|
17
18
|
|
|
18
19
|
const {
|
|
19
20
|
encryptValue,
|
|
20
|
-
isEncrypted,
|
|
21
21
|
isPublicKey,
|
|
22
22
|
provision,
|
|
23
23
|
provisionWithPrivateKey
|
|
@@ -151,12 +151,12 @@ class Encrypt {
|
|
|
151
151
|
continue
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
154
|
+
const fullyEncrypted = values.every(value => encrypted(value) || isPublicKey(key))
|
|
155
|
+
if (!fullyEncrypted) {
|
|
156
156
|
row.keys.push(key) // track key(s)
|
|
157
157
|
|
|
158
158
|
const encryptedValues = values.map(value => {
|
|
159
|
-
if (
|
|
159
|
+
if (encrypted(value) || isPublicKey(key)) {
|
|
160
160
|
return value
|
|
161
161
|
}
|
|
162
162
|
|
|
@@ -34,7 +34,7 @@ class Genexample {
|
|
|
34
34
|
const filepath = path.resolve(this.directory, envFilepath)
|
|
35
35
|
if (!fsx.existsSync(filepath)) {
|
|
36
36
|
const error = new Errors({ envFilepath, filepath }).missingEnvFile()
|
|
37
|
-
error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
|
|
37
|
+
error.help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx ext genexample]`
|
|
38
38
|
throw error
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -47,7 +47,7 @@ class Prebuild {
|
|
|
47
47
|
if (file === '.env.example' || file === '.env.x') {
|
|
48
48
|
const warning = new Errors({
|
|
49
49
|
message: `${file} ignored (should not be)`,
|
|
50
|
-
help: `fix: [dotenvx
|
|
50
|
+
help: `fix: [dotenvx gitignore --pattern !${file}]`
|
|
51
51
|
}).custom()
|
|
52
52
|
warnings.push(warning)
|
|
53
53
|
}
|
|
@@ -59,10 +59,10 @@ class Prebuild {
|
|
|
59
59
|
// if contents are encrypted don't raise an error
|
|
60
60
|
if (!encrypted) {
|
|
61
61
|
let errorMsg = `${file} not encrypted/dockerignored`
|
|
62
|
-
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx
|
|
62
|
+
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx gitignore --pattern ${file}]`
|
|
63
63
|
if (file.includes('.env.keys')) {
|
|
64
64
|
errorMsg = `${file} not dockerignored`
|
|
65
|
-
errorHelp = `fix: [dotenvx
|
|
65
|
+
errorHelp = `fix: [dotenvx gitignore --pattern ${file}]`
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
throw new Errors({ message: errorMsg, help: errorHelp }).custom()
|
|
@@ -63,7 +63,7 @@ class Precommit {
|
|
|
63
63
|
if (file === '.env.example' || file === '.env.x') {
|
|
64
64
|
const warning = new Errors({
|
|
65
65
|
message: `${file} ignored (should not be)`,
|
|
66
|
-
help: `fix: [dotenvx
|
|
66
|
+
help: `fix: [dotenvx gitignore --pattern !${file}]`
|
|
67
67
|
}).custom()
|
|
68
68
|
warnings.push(warning)
|
|
69
69
|
}
|
|
@@ -75,10 +75,10 @@ class Precommit {
|
|
|
75
75
|
// if contents are encrypted don't raise an error
|
|
76
76
|
if (!encrypted) {
|
|
77
77
|
let errorMsg = `${file} not encrypted/gitignored`
|
|
78
|
-
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx
|
|
78
|
+
let errorHelp = `fix: [dotenvx encrypt -f ${file}] or [dotenvx gitignore --pattern ${file}]`
|
|
79
79
|
if (file.includes('.env.keys')) {
|
|
80
80
|
errorMsg = `${file} not gitignored`
|
|
81
|
-
errorHelp = `fix: [dotenvx
|
|
81
|
+
errorHelp = `fix: [dotenvx gitignore --pattern ${file}]`
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
throw new Errors({ message: errorMsg, help: errorHelp }).custom()
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fsx = require('./../helpers/fsx')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const picomatch = require('picomatch')
|
|
4
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
4
5
|
|
|
5
6
|
const TYPE_ENV_FILE = 'envFile'
|
|
6
7
|
|
|
@@ -19,8 +20,7 @@ const {
|
|
|
19
20
|
armorKeypair,
|
|
20
21
|
localKeypair,
|
|
21
22
|
encryptValue,
|
|
22
|
-
decryptKeyValue
|
|
23
|
-
isEncrypted
|
|
23
|
+
decryptKeyValue
|
|
24
24
|
} = require('./../helpers/cryptography')
|
|
25
25
|
|
|
26
26
|
const append = require('./../helpers/append')
|
|
@@ -134,7 +134,7 @@ class Rotate {
|
|
|
134
134
|
continue
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
const value = [...values].reverse().find(value =>
|
|
137
|
+
const value = [...values].reverse().find(value => encrypted(value))
|
|
138
138
|
if (value) { // only re-encrypt those already encrypted
|
|
139
139
|
row.keys.push(key) // track key(s)
|
|
140
140
|
|
package/src/lib/services/sets.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fsx = require('./../helpers/fsx')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const { encrypted } = require('@dotenvx/primitives')
|
|
3
4
|
|
|
4
5
|
const TYPE_ENV_FILE = 'envFile'
|
|
5
6
|
|
|
@@ -18,7 +19,6 @@ const {
|
|
|
18
19
|
const {
|
|
19
20
|
encryptValue,
|
|
20
21
|
decryptKeyValue,
|
|
21
|
-
isEncrypted,
|
|
22
22
|
provision,
|
|
23
23
|
provisionSync,
|
|
24
24
|
provisionWithPrivateKey
|
|
@@ -124,7 +124,7 @@ class Sets {
|
|
|
124
124
|
if (seededWithInitialKey) {
|
|
125
125
|
row.originalValue = null
|
|
126
126
|
}
|
|
127
|
-
const wasPlainText = !
|
|
127
|
+
const wasPlainText = !encrypted(row.originalValue)
|
|
128
128
|
this.readableFilepaths.add(envFilepath)
|
|
129
129
|
|
|
130
130
|
if (this.encrypt) {
|
|
@@ -241,7 +241,7 @@ class Sets {
|
|
|
241
241
|
if (seededWithInitialKey) {
|
|
242
242
|
row.originalValue = null
|
|
243
243
|
}
|
|
244
|
-
const wasPlainText = !
|
|
244
|
+
const wasPlainText = !encrypted(row.originalValue)
|
|
245
245
|
this.readableFilepaths.add(envFilepath)
|
|
246
246
|
|
|
247
247
|
if (this.encrypt) {
|