@dotenvx/dotenvx 2.2.1 → 2.3.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 +21 -1
- package/README.md +99 -67
- package/package.json +2 -2
- package/src/cli/actions/decrypt.js +2 -3
- package/src/cli/actions/encrypt.js +2 -3
- package/src/cli/actions/get.js +2 -3
- package/src/cli/actions/keychain/up.js +2 -2
- package/src/cli/actions/keypair.js +2 -3
- package/src/cli/actions/lock/down.js +74 -0
- package/src/cli/actions/lock/up.js +80 -0
- package/src/cli/actions/run.js +2 -3
- package/src/cli/actions/set.js +2 -3
- package/src/cli/commands/armor.js +19 -1
- package/src/cli/commands/lock.js +27 -0
- package/src/cli/commands/{keychain.js → native.js} +13 -13
- package/src/cli/dotenvx.js +16 -20
- package/src/db/session.js +7 -7
- package/src/lib/helpers/errors.js +13 -0
- package/src/lib/helpers/executeDynamic.js +1 -19
- package/src/lib/main.d.ts +9 -33
- package/src/lib/main.js +3 -3
- package/src/lib/providers/index.js +1 -1
- package/src/lib/services/keychainPull.js +1 -1
- package/src/lib/services/lockDown.js +157 -0
- package/src/lib/services/lockUp.js +150 -0
- package/src/cli/actions/normalizeArmorAliases.js +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,27 @@
|
|
|
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/v2.
|
|
5
|
+
[Unreleased](https://github.com/dotenvx/dotenvx/compare/v2.3.0...main)
|
|
6
|
+
|
|
7
|
+
## [2.3.0](https://github.com/dotenvx/dotenvx/compare/v2.2.2...v2.3.0) (2026-07-08)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add `lock up` and `lock down` commands for password protecting private keys ([#875](https://github.com/dotenvx/dotenvx/pull/875))
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
* BREAKING: Rename `keychain` commands to `native` to make way for windows and linux. ([#875](https://github.com/dotenvx/dotenvx/pull/875))
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
|
|
19
|
+
* BREAKING: Remove `--no-ops`. Use `--no-armor`. ([#875](https://github.com/dotenvx/dotenvx/pull/875))
|
|
20
|
+
|
|
21
|
+
## [2.2.2](https://github.com/dotenvx/dotenvx/compare/v2.2.1...v2.2.2) (2026-07-08)
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
* Move `login` and `logout` under armor ([#874](https://github.com/dotenvx/dotenvx/pull/874))
|
|
6
26
|
|
|
7
27
|
## [2.2.1](https://github.com/dotenvx/dotenvx/compare/v2.2.0...v2.2.1) (2026-07-08)
|
|
8
28
|
|
package/README.md
CHANGED
|
@@ -1385,12 +1385,12 @@ $ dotenvx run --token "$DOTENVX_ARMOR_TOKEN" -- yourcommand
|
|
|
1385
1385
|
```
|
|
1386
1386
|
|
|
1387
1387
|
</details>
|
|
1388
|
-
<details><summary>`run --no-
|
|
1388
|
+
<details><summary>`run --no-native`</summary><br>
|
|
1389
1389
|
|
|
1390
|
-
Turn off
|
|
1390
|
+
Turn off OS secret store lookups.
|
|
1391
1391
|
|
|
1392
1392
|
```sh
|
|
1393
|
-
$ dotenvx run --no-
|
|
1393
|
+
$ dotenvx run --no-native -- yourcommand
|
|
1394
1394
|
```
|
|
1395
1395
|
|
|
1396
1396
|
</details>
|
|
@@ -1415,12 +1415,12 @@ World
|
|
|
1415
1415
|
```
|
|
1416
1416
|
|
|
1417
1417
|
</details>
|
|
1418
|
-
<details><summary>`get KEY --no-
|
|
1418
|
+
<details><summary>`get KEY --no-native`</summary><br>
|
|
1419
1419
|
|
|
1420
|
-
Turn off
|
|
1420
|
+
Turn off OS secret store lookups for get.
|
|
1421
1421
|
|
|
1422
1422
|
```sh
|
|
1423
|
-
$ dotenvx get HELLO --no-
|
|
1423
|
+
$ dotenvx get HELLO --no-native
|
|
1424
1424
|
```
|
|
1425
1425
|
|
|
1426
1426
|
</details>
|
|
@@ -1739,12 +1739,12 @@ set HELLO_PLAIN (.env)
|
|
|
1739
1739
|
Keys ending in `_PLAIN` are not encrypted by `dotenvx set` or `dotenvx encrypt`.
|
|
1740
1740
|
|
|
1741
1741
|
</details>
|
|
1742
|
-
<details><summary>`set KEY value --no-
|
|
1742
|
+
<details><summary>`set KEY value --no-native`</summary><br>
|
|
1743
1743
|
|
|
1744
|
-
Turn off
|
|
1744
|
+
Turn off OS secret store lookups for set.
|
|
1745
1745
|
|
|
1746
1746
|
```sh
|
|
1747
|
-
$ dotenvx set HELLO Dotenvx --no-
|
|
1747
|
+
$ dotenvx set HELLO Dotenvx --no-native
|
|
1748
1748
|
```
|
|
1749
1749
|
|
|
1750
1750
|
</details>
|
|
@@ -1779,12 +1779,12 @@ $ dotenvx encrypt -f .env.production
|
|
|
1779
1779
|
```
|
|
1780
1780
|
|
|
1781
1781
|
</details>
|
|
1782
|
-
<details><summary>`encrypt --no-
|
|
1782
|
+
<details><summary>`encrypt --no-native`</summary><br>
|
|
1783
1783
|
|
|
1784
|
-
Turn off
|
|
1784
|
+
Turn off OS secret store lookups for encrypt.
|
|
1785
1785
|
|
|
1786
1786
|
```sh
|
|
1787
|
-
$ dotenvx encrypt --no-
|
|
1787
|
+
$ dotenvx encrypt --no-native
|
|
1788
1788
|
◈ encrypted (.env)
|
|
1789
1789
|
```
|
|
1790
1790
|
|
|
@@ -1918,12 +1918,12 @@ $ dotenvx decrypt
|
|
|
1918
1918
|
```
|
|
1919
1919
|
|
|
1920
1920
|
</details>
|
|
1921
|
-
<details><summary>`decrypt --no-
|
|
1921
|
+
<details><summary>`decrypt --no-native`</summary><br>
|
|
1922
1922
|
|
|
1923
|
-
Turn off
|
|
1923
|
+
Turn off OS secret store lookups for decrypt.
|
|
1924
1924
|
|
|
1925
1925
|
```sh
|
|
1926
|
-
$ dotenvx decrypt --no-
|
|
1926
|
+
$ dotenvx decrypt --no-native
|
|
1927
1927
|
◇ decrypted (.env)
|
|
1928
1928
|
```
|
|
1929
1929
|
|
|
@@ -2039,12 +2039,12 @@ $ dotenvx keypair
|
|
|
2039
2039
|
```
|
|
2040
2040
|
|
|
2041
2041
|
</details>
|
|
2042
|
-
<details><summary>`keypair --no-
|
|
2042
|
+
<details><summary>`keypair --no-native`</summary><br>
|
|
2043
2043
|
|
|
2044
|
-
Turn off
|
|
2044
|
+
Turn off OS secret store lookups for keypair.
|
|
2045
2045
|
|
|
2046
2046
|
```sh
|
|
2047
|
-
$ dotenvx keypair --no-
|
|
2047
|
+
$ dotenvx keypair --no-native
|
|
2048
2048
|
{"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}
|
|
2049
2049
|
```
|
|
2050
2050
|
|
|
@@ -2323,52 +2323,93 @@ CMD ["/usr/local/bin/dotenvx", "run", "--", "node", "apps/backend/index.js"]
|
|
|
2323
2323
|
```
|
|
2324
2324
|
|
|
2325
2325
|
</details>
|
|
2326
|
-
<details><summary>`
|
|
2326
|
+
<details><summary>`lock`</summary><br>
|
|
2327
2327
|
|
|
2328
|
-
|
|
2328
|
+
Lock private keys with a local passphrase to keep them protected inside `.env.keys`.
|
|
2329
|
+
|
|
2330
|
+
```
|
|
2331
|
+
# example
|
|
2332
|
+
DOTENV_PRIVATE_KEY=locked:02f5b97ad58b49ae324cd4e7937bc19b251d006b31cacf46f789eeaf03f923cedc:AZIPDxKqjPLiGl5b4CqVGbR3CIBDUcqHthGaoeWLoUvxbTHJkj3jGoGWGaxFSDUJGQUmWDaExRzxKpVydYF_7qiWr1ecqksOFho5t3EMwKbqX2-y-LZO9K3a4SJaYAjDJXpn3NwG4vAt1oLmGA
|
|
2333
|
+
```
|
|
2334
|
+
|
|
2335
|
+
</details>
|
|
2336
|
+
<details><summary>`lock up`</summary><br>
|
|
2337
|
+
|
|
2338
|
+
Lock a private key in `.env.keys` with a local passphrase.
|
|
2339
|
+
|
|
2340
|
+
```sh
|
|
2341
|
+
$ dotenvx lock up
|
|
2342
|
+
```
|
|
2343
|
+
|
|
2344
|
+
Here's what a locked key looks like:
|
|
2345
|
+
|
|
2346
|
+
```ini
|
|
2347
|
+
# .env.keys
|
|
2348
|
+
DOTENV_PRIVATE_KEY=locked:02f5b97ad58b49ae324cd4e7937bc19b251d006b31cacf46f789eeaf03f923cedc:AZIPDxKqjPLiGl5b4CqVGbR3CIBDUcqHthGaoeWLoUvxbTHJkj3jGoGWGaxFSDUJGQUmWDaExRzxKpVydYF_7qiWr1ecqksOFho5t3EMwKbqX2-y-LZO9K3a4SJaYAjDJXpn3NwG4vAt1oLmGA
|
|
2349
|
+
```
|
|
2350
|
+
|
|
2351
|
+
Specify files with `-f` and `-fk`.
|
|
2352
|
+
|
|
2353
|
+
```sh
|
|
2354
|
+
$ dotenvx lock up -f .env.production -fk .env.keys
|
|
2355
|
+
```
|
|
2356
|
+
|
|
2357
|
+
</details>
|
|
2358
|
+
<details><summary>`lock down`</summary><br>
|
|
2359
|
+
|
|
2360
|
+
Unlock a private key in `.env.keys` with its local passphrase.
|
|
2361
|
+
|
|
2362
|
+
```sh
|
|
2363
|
+
$ dotenvx lock down
|
|
2364
|
+
```
|
|
2365
|
+
|
|
2366
|
+
</details>
|
|
2367
|
+
<details><summary>`native`</summary><br>
|
|
2368
|
+
|
|
2369
|
+
Move private keys into your OS secret store (macOS Keychain supported).
|
|
2329
2370
|
|
|
2330
2371
|
Currently supported on macOS.
|
|
2331
2372
|
|
|
2332
2373
|
</details>
|
|
2333
|
-
<details><summary>`
|
|
2374
|
+
<details><summary>`native up`</summary><br>
|
|
2334
2375
|
|
|
2335
|
-
Move a private key from `.env.keys` into
|
|
2376
|
+
Move a private key from `.env.keys` into your OS secret store.
|
|
2336
2377
|
|
|
2337
2378
|
```sh
|
|
2338
|
-
$ dotenvx
|
|
2379
|
+
$ dotenvx native up
|
|
2339
2380
|
```
|
|
2340
2381
|
|
|
2341
2382
|
Specify files with `-f` and `-fk`.
|
|
2342
2383
|
|
|
2343
2384
|
```sh
|
|
2344
|
-
$ dotenvx
|
|
2385
|
+
$ dotenvx native up -f .env.production -fk .env.keys
|
|
2345
2386
|
```
|
|
2346
2387
|
|
|
2347
2388
|
</details>
|
|
2348
|
-
<details><summary>`
|
|
2389
|
+
<details><summary>`native down`</summary><br>
|
|
2349
2390
|
|
|
2350
|
-
Move a private key from
|
|
2391
|
+
Move a private key from your OS secret store back into `.env.keys`.
|
|
2351
2392
|
|
|
2352
2393
|
```sh
|
|
2353
|
-
$ dotenvx
|
|
2394
|
+
$ dotenvx native down
|
|
2354
2395
|
```
|
|
2355
2396
|
|
|
2356
2397
|
</details>
|
|
2357
|
-
<details><summary>`
|
|
2398
|
+
<details><summary>`native push`</summary><br>
|
|
2358
2399
|
|
|
2359
|
-
Copy a private key from `.env.keys` into
|
|
2400
|
+
Copy a private key from `.env.keys` into your OS secret store.
|
|
2360
2401
|
|
|
2361
2402
|
```sh
|
|
2362
|
-
$ dotenvx
|
|
2403
|
+
$ dotenvx native push
|
|
2363
2404
|
```
|
|
2364
2405
|
|
|
2365
2406
|
</details>
|
|
2366
|
-
<details><summary>`
|
|
2407
|
+
<details><summary>`native pull`</summary><br>
|
|
2367
2408
|
|
|
2368
|
-
Copy a private key from
|
|
2409
|
+
Copy a private key from your OS secret store into `.env.keys`.
|
|
2369
2410
|
|
|
2370
2411
|
```sh
|
|
2371
|
-
$ dotenvx
|
|
2412
|
+
$ dotenvx native pull
|
|
2372
2413
|
```
|
|
2373
2414
|
|
|
2374
2415
|
</details>
|
|
@@ -2382,7 +2423,7 @@ Move private keys into [Dotenvx Armor ⛨](https://dotenvx.com/armor) for off-de
|
|
|
2382
2423
|
Move a private key from `.env.keys` into Dotenvx Armor.
|
|
2383
2424
|
|
|
2384
2425
|
```sh
|
|
2385
|
-
$ dotenvx login
|
|
2426
|
+
$ dotenvx armor login
|
|
2386
2427
|
$ dotenvx armor up
|
|
2387
2428
|
```
|
|
2388
2429
|
|
|
@@ -2434,6 +2475,24 @@ Move an armored key to another team.
|
|
|
2434
2475
|
$ dotenvx armor move --team acme
|
|
2435
2476
|
```
|
|
2436
2477
|
|
|
2478
|
+
</details>
|
|
2479
|
+
<details><summary>`armor login`</summary><br>
|
|
2480
|
+
|
|
2481
|
+
Log in to Dotenvx Armor.
|
|
2482
|
+
|
|
2483
|
+
```sh
|
|
2484
|
+
$ dotenvx armor login
|
|
2485
|
+
```
|
|
2486
|
+
|
|
2487
|
+
</details>
|
|
2488
|
+
<details><summary>`armor logout`</summary><br>
|
|
2489
|
+
|
|
2490
|
+
Log out of Dotenvx Armor.
|
|
2491
|
+
|
|
2492
|
+
```sh
|
|
2493
|
+
$ dotenvx armor logout
|
|
2494
|
+
```
|
|
2495
|
+
|
|
2437
2496
|
</details>
|
|
2438
2497
|
<details><summary>`help`</summary><br>
|
|
2439
2498
|
|
|
@@ -2470,9 +2529,9 @@ Commands:
|
|
|
2470
2529
|
prevent including .env files in docker
|
|
2471
2530
|
|
|
2472
2531
|
Professional Security:
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
armor ⛨ move private keys
|
|
2532
|
+
lock ⊡ lock private keys with a local passphrase
|
|
2533
|
+
native ⌥ move private keys into your OS secret store (macOS Keychain supported)
|
|
2534
|
+
armor ⛨ move private keys into Dotenvx Armor [www.dotenvx.com/armor]
|
|
2476
2535
|
```
|
|
2477
2536
|
|
|
2478
2537
|
You can get more detailed help per command with `dotenvx help COMMAND`.
|
|
@@ -2520,24 +2579,6 @@ X.X.X
|
|
|
2520
2579
|
|
|
2521
2580
|
</details>
|
|
2522
2581
|
|
|
2523
|
-
### Utilities
|
|
2524
|
-
|
|
2525
|
-
Additional workflow helpers.
|
|
2526
|
-
|
|
2527
|
-
<details><summary>`ext scan`</summary><br>
|
|
2528
|
-
|
|
2529
|
-
Scan for leaked secrets.
|
|
2530
|
-
|
|
2531
|
-
```sh
|
|
2532
|
-
$ dotenvx ext scan
|
|
2533
|
-
100 commits scanned.
|
|
2534
|
-
no leaks found
|
|
2535
|
-
```
|
|
2536
|
-
|
|
2537
|
-
Uses [gitleaks](https://gitleaks.io) under the hood.
|
|
2538
|
-
|
|
2539
|
-
</details>
|
|
2540
|
-
|
|
2541
2582
|
### Library 📦
|
|
2542
2583
|
|
|
2543
2584
|
Use dotenvx directly in code.
|
|
@@ -2888,18 +2929,6 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
|
|
|
2888
2929
|
|
|
2889
2930
|
|
|
2890
2931
|
|
|
2891
|
-
## Whitepaper
|
|
2892
|
-
|
|
2893
|
-
> **Dotenvx: Reducing Secrets Risk with Cryptographic Separation**
|
|
2894
|
-
>
|
|
2895
|
-
> Abstract. An ideal secrets solution would not only centralize secrets but also contain the fallout of a breach. While secrets managers offer centralized storage and distribution, their design creates a large blast radius, risking exposure of thousands or even millions of secrets. We propose a solution that reduces the blast radius by splitting secrets management into two distinct components: an encrypted secrets file and a separate decryption key.
|
|
2896
|
-
>
|
|
2897
|
-
> ...
|
|
2898
|
-
>
|
|
2899
|
-
> [Read the whitepaper](https://dotenvx.com/dotenvx.pdf)
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
2932
|
## Guides
|
|
2904
2933
|
|
|
2905
2934
|
> Go deeper with `dotenvx` – detailed framework and platform guides.
|
|
@@ -2931,6 +2960,9 @@ This is known as *Decryption at Access* and is written about in [the whitepaper]
|
|
|
2931
2960
|
* [Render](https://dotenvx.com/docs/platforms/render)
|
|
2932
2961
|
* [CI/CDs](https://dotenvx.com/docs#cis)
|
|
2933
2962
|
* [GitHub Actions](https://dotenvx.com/docs/cis/github-actions)
|
|
2963
|
+
* [Password Managers](https://dotenvx.com/docs#password-managers)
|
|
2964
|
+
* [1Password](https://dotenvx.com/docs/guides/1password)
|
|
2965
|
+
* [Bitwarden](https://dotenvx.com/docs/guides/bitwarden)
|
|
2934
2966
|
* [Background Jobs](https://dotenvx.com/docs#background-jobs)
|
|
2935
2967
|
* [Trigger.dev](https://dotenvx.com/docs/background-jobs/triggerdotdev)
|
|
2936
2968
|
* [Package Managers](https://dotenvx.com/docs#package-managers)
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.3.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a secure dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"standard": "standard",
|
|
37
37
|
"standard:fix": "standard --fix",
|
|
38
38
|
"test": "tap run --test-env=DOTENVX_NO_ARMOR=true --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
39
|
-
"testshell": "bash
|
|
39
|
+
"testshell": "shellspec --shell /bin/bash",
|
|
40
40
|
"release:check": "npm test && npm run testshell",
|
|
41
41
|
"prerelease": "npm run release:check",
|
|
42
42
|
"release": "npm run release:check && standard-version"
|
|
@@ -4,12 +4,11 @@ const { logger } = require('./../../shared/logger')
|
|
|
4
4
|
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
5
5
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
6
6
|
const Session = require('../../db/session')
|
|
7
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
8
7
|
|
|
9
8
|
const decryptTransform = require('./../../lib/transforms/decrypt')
|
|
10
9
|
|
|
11
10
|
async function decrypt () {
|
|
12
|
-
const options =
|
|
11
|
+
const options = this.opts()
|
|
13
12
|
const spinner = await createSpinner({ ...options, text: 'decrypting' })
|
|
14
13
|
|
|
15
14
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
@@ -17,7 +16,7 @@ async function decrypt () {
|
|
|
17
16
|
const envs = this.envs
|
|
18
17
|
const sesh = new Session()
|
|
19
18
|
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
20
|
-
const noKeychain = options.
|
|
19
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
21
20
|
|
|
22
21
|
let errorCount = 0
|
|
23
22
|
|
|
@@ -6,10 +6,9 @@ const encryptTransform = require('./../../lib/transforms/encrypt')
|
|
|
6
6
|
const catchAndLog = require('../../lib/helpers/catchAndLog')
|
|
7
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
8
|
const Session = require('../../db/session')
|
|
9
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
9
|
|
|
11
10
|
async function encryptAction () {
|
|
12
|
-
const options =
|
|
11
|
+
const options = this.opts()
|
|
13
12
|
const spinner = await createSpinner({ ...options, text: 'encrypting' })
|
|
14
13
|
const sesh = new Session()
|
|
15
14
|
|
|
@@ -21,7 +20,7 @@ async function encryptAction () {
|
|
|
21
20
|
const fk = options.envKeysFile || '.env.keys'
|
|
22
21
|
const noCreate = options.create === false
|
|
23
22
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
24
|
-
const noKeychain = options.
|
|
23
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
25
24
|
|
|
26
25
|
let errorCount = 0
|
|
27
26
|
|
package/src/cli/actions/get.js
CHANGED
|
@@ -6,11 +6,10 @@ const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
|
6
6
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
7
7
|
const Session = require('../../db/session')
|
|
8
8
|
const getResolver = require('./../../lib/resolvers/get')
|
|
9
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
9
|
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
11
10
|
|
|
12
11
|
async function get (key) {
|
|
13
|
-
const options = normalizeDotenvConfigConvention(
|
|
12
|
+
const options = normalizeDotenvConfigConvention(this.opts())
|
|
14
13
|
const spinner = await createSpinner({ ...options, text: 'decrypting' })
|
|
15
14
|
|
|
16
15
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
@@ -33,7 +32,7 @@ async function get (key) {
|
|
|
33
32
|
try {
|
|
34
33
|
const sesh = new Session()
|
|
35
34
|
const noArmor = options.armor === false || (await sesh.noArmor())
|
|
36
|
-
const noKeychain = options.
|
|
35
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
37
36
|
const { parsed, errors } = await getResolver({
|
|
38
37
|
key,
|
|
39
38
|
envs,
|
|
@@ -5,7 +5,7 @@ const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
|
|
|
5
5
|
|
|
6
6
|
async function up () {
|
|
7
7
|
const options = this.opts()
|
|
8
|
-
const spinner = await createSpinner({ ...options, text: '
|
|
8
|
+
const spinner = await createSpinner({ ...options, text: 'storing' })
|
|
9
9
|
|
|
10
10
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
11
11
|
|
|
@@ -15,7 +15,7 @@ async function up () {
|
|
|
15
15
|
|
|
16
16
|
if (spinner) spinner.stop()
|
|
17
17
|
if (changed) {
|
|
18
|
-
logger.success(`⌥
|
|
18
|
+
logger.success(`⌥ stored (${keyDisplay})`)
|
|
19
19
|
} else {
|
|
20
20
|
logger.info(`○ no change (${keyDisplay})`)
|
|
21
21
|
}
|
|
@@ -3,10 +3,9 @@ const { logger } = require('./../../shared/logger')
|
|
|
3
3
|
const keypairResolver = require('./../../lib/resolvers/keypair')
|
|
4
4
|
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
5
5
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
6
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
7
6
|
|
|
8
7
|
async function keypair (key) {
|
|
9
|
-
const options =
|
|
8
|
+
const options = this.opts()
|
|
10
9
|
const spinner = await createSpinner({ ...options, text: 'retrieving' })
|
|
11
10
|
|
|
12
11
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
@@ -21,7 +20,7 @@ async function keypair (key) {
|
|
|
21
20
|
envFile: options.envFile,
|
|
22
21
|
envKeysFile: options.envKeysFile,
|
|
23
22
|
armor: options.armor,
|
|
24
|
-
noKeychain: options.
|
|
23
|
+
noKeychain: options.native === false || options.noNative === true,
|
|
25
24
|
token: options.token,
|
|
26
25
|
command: process.argv.slice(2),
|
|
27
26
|
onStatus: (text) => {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const crypto = require('crypto')
|
|
2
|
+
|
|
3
|
+
const { logger } = require('../../../shared/logger')
|
|
4
|
+
const LockDown = require('./../../../lib/services/lockDown')
|
|
5
|
+
const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
|
|
6
|
+
const prompts = require('../../../lib/helpers/prompts')
|
|
7
|
+
|
|
8
|
+
function unlockedValue (lockedPrivateKey, passphrase) {
|
|
9
|
+
const parts = lockedPrivateKey.split(':')
|
|
10
|
+
const payload = Buffer.from(parts.slice(2).join(':'), 'base64url')
|
|
11
|
+
const version = payload.subarray(0, 1)[0]
|
|
12
|
+
const salt = payload.subarray(1, 17)
|
|
13
|
+
const iv = payload.subarray(17, 29)
|
|
14
|
+
const tag = payload.subarray(29, 45)
|
|
15
|
+
const ciphertext = payload.subarray(45)
|
|
16
|
+
const key = crypto.scryptSync(passphrase, salt, 32)
|
|
17
|
+
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv)
|
|
18
|
+
|
|
19
|
+
if (version !== 1) {
|
|
20
|
+
throw new Error('unsupported locked private key version')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
decipher.setAuthTag(tag)
|
|
24
|
+
|
|
25
|
+
return Buffer.concat([
|
|
26
|
+
decipher.update(ciphertext),
|
|
27
|
+
decipher.final()
|
|
28
|
+
]).toString('utf8')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function down () {
|
|
32
|
+
const options = this.opts()
|
|
33
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const lockDown = new LockDown(options.envFile, options.envKeysFile)
|
|
37
|
+
const plan = lockDown.plan()
|
|
38
|
+
let results = plan.alreadyUnlocked
|
|
39
|
+
|
|
40
|
+
if (plan.locked.length > 0) {
|
|
41
|
+
const passphrase = await prompts.password({
|
|
42
|
+
message: 'passphrase',
|
|
43
|
+
prefix: '⊡',
|
|
44
|
+
separator: '='
|
|
45
|
+
}, {
|
|
46
|
+
input: process.stdin,
|
|
47
|
+
output: process.stderr
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
results = lockDown.run(lockedPrivateKey => unlockedValue(lockedPrivateKey, passphrase)).results
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const result of results) {
|
|
54
|
+
const keyDisplay = armoredKeyDisplay(result.publicKeyValue) || result.privateKeyName
|
|
55
|
+
|
|
56
|
+
if (result.changed) {
|
|
57
|
+
logger.success(`⊡ unlocked (${keyDisplay})`)
|
|
58
|
+
} else {
|
|
59
|
+
logger.info(`○ no change (${keyDisplay})`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error.code === 'PROMPT_CANCELLED') {
|
|
64
|
+
process.exit(130)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
logger.error(error.message)
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = down
|
|
74
|
+
module.exports.unlockedValue = unlockedValue
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const crypto = require('crypto')
|
|
2
|
+
|
|
3
|
+
const { logger } = require('../../../shared/logger')
|
|
4
|
+
const LockUp = require('./../../../lib/services/lockUp')
|
|
5
|
+
const armoredKeyDisplay = require('../../../lib/helpers/armoredKeyDisplay')
|
|
6
|
+
const prompts = require('../../../lib/helpers/prompts')
|
|
7
|
+
|
|
8
|
+
function lockedValue (privateKey, passphrase, publicKey) {
|
|
9
|
+
const salt = crypto.randomBytes(16)
|
|
10
|
+
const iv = crypto.randomBytes(12)
|
|
11
|
+
const key = crypto.scryptSync(passphrase, salt, 32)
|
|
12
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
|
|
13
|
+
const ciphertext = Buffer.concat([
|
|
14
|
+
cipher.update(privateKey, 'utf8'),
|
|
15
|
+
cipher.final()
|
|
16
|
+
])
|
|
17
|
+
const tag = cipher.getAuthTag()
|
|
18
|
+
const payload = Buffer.concat([
|
|
19
|
+
Buffer.from([1]),
|
|
20
|
+
salt,
|
|
21
|
+
iv,
|
|
22
|
+
tag,
|
|
23
|
+
ciphertext
|
|
24
|
+
]).toString('base64url')
|
|
25
|
+
|
|
26
|
+
return `locked:${publicKey}:${payload}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function up () {
|
|
30
|
+
const options = this.opts()
|
|
31
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const lockUp = new LockUp(options.envFile, options.envKeysFile)
|
|
35
|
+
|
|
36
|
+
const plan = lockUp.plan()
|
|
37
|
+
let results = plan.alreadyLocked.map(lockedPrivateKey => ({
|
|
38
|
+
changed: false,
|
|
39
|
+
privateKeyName: lockedPrivateKey.privateKeyName,
|
|
40
|
+
privateKeyValue: undefined,
|
|
41
|
+
lockedPrivateKeyValue: lockedPrivateKey.lockedPrivateKeyValue,
|
|
42
|
+
publicKeyValue: lockedPrivateKey.publicKeyValue,
|
|
43
|
+
alreadyLocked: true
|
|
44
|
+
}))
|
|
45
|
+
|
|
46
|
+
if (plan.matches.length > 0) {
|
|
47
|
+
const passphrase = await prompts.password({
|
|
48
|
+
message: 'passphrase',
|
|
49
|
+
prefix: '⊡',
|
|
50
|
+
separator: '='
|
|
51
|
+
}, {
|
|
52
|
+
input: process.stdin,
|
|
53
|
+
output: process.stderr
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
results = lockUp.run((privateKey, publicKey) => lockedValue(privateKey, passphrase, publicKey)).results
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const result of results) {
|
|
60
|
+
const keyDisplay = armoredKeyDisplay(result.publicKeyValue) || result.privateKeyName
|
|
61
|
+
|
|
62
|
+
if (result.changed) {
|
|
63
|
+
logger.success(`⊡ locked (${keyDisplay})`)
|
|
64
|
+
} else {
|
|
65
|
+
logger.info(`○ no change (${keyDisplay})`)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error.code === 'PROMPT_CANCELLED') {
|
|
70
|
+
process.exit(130)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.error(error.message)
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = up
|
|
80
|
+
module.exports.lockedValue = lockedValue
|
package/src/cli/actions/run.js
CHANGED
|
@@ -6,7 +6,6 @@ const envsResolver = require('./../../lib/resolvers/envs')
|
|
|
6
6
|
const catchAndLog = require('./../../lib/helpers/catchAndLog')
|
|
7
7
|
const createSpinner = require('../../lib/helpers/createSpinner')
|
|
8
8
|
const Session = require('../../db/session')
|
|
9
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
10
9
|
const normalizeDotenvConfigQuiet = require('../../lib/helpers/normalizeDotenvConfigQuiet')
|
|
11
10
|
const normalizeDotenvConfigConvention = require('../../lib/helpers/normalizeDotenvConfigConvention')
|
|
12
11
|
|
|
@@ -47,7 +46,7 @@ function uniqueInjectedKeys (processedEnvs) {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
async function run () {
|
|
50
|
-
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(
|
|
49
|
+
const options = normalizeDotenvConfigConvention(normalizeDotenvConfigQuiet(this.opts()))
|
|
51
50
|
|
|
52
51
|
let commandArgs = this.args
|
|
53
52
|
if (commandArgs.length < 1) {
|
|
@@ -63,7 +62,7 @@ async function run () {
|
|
|
63
62
|
|
|
64
63
|
const sesh = new Session()
|
|
65
64
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
66
|
-
const noKeychain = options.
|
|
65
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
67
66
|
|
|
68
67
|
if (commandArgs.length < 1) {
|
|
69
68
|
if (spinner) spinner.stop()
|
package/src/cli/actions/set.js
CHANGED
|
@@ -8,10 +8,9 @@ const createSpinner = require('../../lib/helpers/createSpinner')
|
|
|
8
8
|
const Errors = require('../../lib/helpers/errors')
|
|
9
9
|
const prompts = require('../../lib/helpers/prompts')
|
|
10
10
|
const Session = require('../../db/session')
|
|
11
|
-
const normalizeArmorAliases = require('./normalizeArmorAliases')
|
|
12
11
|
|
|
13
12
|
async function set (key, value) {
|
|
14
|
-
const options =
|
|
13
|
+
const options = this.opts()
|
|
15
14
|
|
|
16
15
|
let encrypt = true
|
|
17
16
|
let settingMessage = 'encrypting'
|
|
@@ -65,7 +64,7 @@ async function set (key, value) {
|
|
|
65
64
|
const fk = options.envKeysFile || '.env.keys'
|
|
66
65
|
const noCreate = options.create === false
|
|
67
66
|
const noArmor = options.armor === false || (!options.token && (await sesh.noArmor()))
|
|
68
|
-
const noKeychain = options.
|
|
67
|
+
const noKeychain = options.native === false || options.noNative === true
|
|
69
68
|
|
|
70
69
|
let errorCount = 0
|
|
71
70
|
|