@envsave/cli 1.0.37 → 1.0.39
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/dist/cli.js +18 -36
- package/dist/index.js +3 -10
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -274,7 +274,7 @@ __export(exports_vault, {
|
|
|
274
274
|
configureRateLimit: () => configureRateLimit,
|
|
275
275
|
changePassword: () => changePassword
|
|
276
276
|
});
|
|
277
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, appendFileSync
|
|
277
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync, appendFileSync } from "node:fs";
|
|
278
278
|
import { join as join2 } from "node:path";
|
|
279
279
|
import { homedir as homedir2 } from "node:os";
|
|
280
280
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
@@ -304,12 +304,12 @@ function guardRealVault() {
|
|
|
304
304
|
}
|
|
305
305
|
function readConfig() {
|
|
306
306
|
if (!existsSync2(getConfigFile())) {
|
|
307
|
-
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX
|
|
307
|
+
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
|
|
308
308
|
}
|
|
309
309
|
try {
|
|
310
310
|
return JSON.parse(readFileSync3(getConfigFile(), "utf8"));
|
|
311
311
|
} catch {
|
|
312
|
-
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX
|
|
312
|
+
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
function writeConfig(config) {
|
|
@@ -321,8 +321,6 @@ function configureRateLimit(password, options) {
|
|
|
321
321
|
const config = readConfig();
|
|
322
322
|
if (options.maxGetsPerMinute !== undefined)
|
|
323
323
|
config.maxGetsPerMinute = options.maxGetsPerMinute;
|
|
324
|
-
if (options.sessionTtlHours !== undefined)
|
|
325
|
-
config.sessionTtlHours = options.sessionTtlHours;
|
|
326
324
|
writeConfig(config);
|
|
327
325
|
return config;
|
|
328
326
|
}
|
|
@@ -459,12 +457,6 @@ function readSessionCache() {
|
|
|
459
457
|
if (!existsSync2(getSessionFile()) || !existsSync2(getCacheFile()))
|
|
460
458
|
return null;
|
|
461
459
|
try {
|
|
462
|
-
const sessionStat = statSync(getSessionFile());
|
|
463
|
-
const age = Date.now() - sessionStat.mtimeMs;
|
|
464
|
-
const config = readConfig();
|
|
465
|
-
if (age > config.sessionTtlHours * 3600000) {
|
|
466
|
-
return null;
|
|
467
|
-
}
|
|
468
460
|
const sessionKeyHex = readFileSync3(getSessionFile(), "utf8").trim();
|
|
469
461
|
const sessionKey = Buffer.from(sessionKeyHex, "hex");
|
|
470
462
|
const payload = readFileSync3(getCacheFile());
|
|
@@ -522,7 +514,7 @@ function initVault(password, options) {
|
|
|
522
514
|
const hmacSeed = randomBytes2(32).toString("hex");
|
|
523
515
|
const sshKeyContent = options?.sshKeyPath ? readFileSync3(options.sshKeyPath, "utf8") : undefined;
|
|
524
516
|
const vault = {
|
|
525
|
-
version:
|
|
517
|
+
version: 5,
|
|
526
518
|
hmacSeed,
|
|
527
519
|
sshKeyPath: options?.sshKeyPath,
|
|
528
520
|
secrets: {},
|
|
@@ -552,7 +544,8 @@ function setSecret(key, value, password) {
|
|
|
552
544
|
const vault = readMainVault(password);
|
|
553
545
|
const sshKeyContent = loadSshKey(vault);
|
|
554
546
|
const opaque = computeOpaqueName(vault.hmacSeed, key, sshKeyContent);
|
|
555
|
-
|
|
547
|
+
const updated = opaque in vault.secrets;
|
|
548
|
+
if (!updated && !hasValidLicense()) {
|
|
556
549
|
if (vault.registry.length >= FREE_TIER_MAX_SECRETS) {
|
|
557
550
|
throw new Error(`Free tier limit: max ${FREE_TIER_MAX_SECRETS} secrets. Run 'envsave activate <token>' to unlock unlimited secrets.
|
|
558
551
|
Get your license at https://api.envsave.com`);
|
|
@@ -564,7 +557,7 @@ Get your license at https://api.envsave.com`);
|
|
|
564
557
|
}
|
|
565
558
|
writeMainVault(vault, password);
|
|
566
559
|
writeSessionCache(vault.secrets, vault.registry);
|
|
567
|
-
return opaque;
|
|
560
|
+
return { opaque, updated };
|
|
568
561
|
}
|
|
569
562
|
function showSecret(cleanName, password) {
|
|
570
563
|
const vault = readMainVault(password);
|
|
@@ -689,12 +682,11 @@ function hasSecretWithSession(opaqueKey) {
|
|
|
689
682
|
return false;
|
|
690
683
|
}
|
|
691
684
|
}
|
|
692
|
-
var FREE_TIER_MAX_SECRETS = 5,
|
|
685
|
+
var FREE_TIER_MAX_SECRETS = 5, DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_MAX = 30, BASE_CHARS_V3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@$&*=-!", BASE_CHARS_V4 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
|
|
693
686
|
var init_vault = __esm(() => {
|
|
694
687
|
init_crypto();
|
|
695
688
|
init_env_parser();
|
|
696
689
|
init_license();
|
|
697
|
-
DEFAULT_SESSION_TTL_MS = 4 * 60 * 60 * 1000;
|
|
698
690
|
DEFAULT_RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
|
699
691
|
});
|
|
700
692
|
|
|
@@ -703,7 +695,7 @@ var exports_scanner = {};
|
|
|
703
695
|
__export(exports_scanner, {
|
|
704
696
|
scanProject: () => scanProject
|
|
705
697
|
});
|
|
706
|
-
import { readdirSync, readFileSync as readFileSync4, statSync
|
|
698
|
+
import { readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
707
699
|
import { join as join3, relative } from "node:path";
|
|
708
700
|
function walkDir(dir, base, results) {
|
|
709
701
|
let entries;
|
|
@@ -718,7 +710,7 @@ function walkDir(dir, base, results) {
|
|
|
718
710
|
const fullPath = join3(dir, entry);
|
|
719
711
|
let stat;
|
|
720
712
|
try {
|
|
721
|
-
stat =
|
|
713
|
+
stat = statSync(fullPath);
|
|
722
714
|
} catch {
|
|
723
715
|
continue;
|
|
724
716
|
}
|
|
@@ -860,7 +852,7 @@ var init_scanner = __esm(() => {
|
|
|
860
852
|
init_vault();
|
|
861
853
|
init_license();
|
|
862
854
|
import { createInterface } from "node:readline";
|
|
863
|
-
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as
|
|
855
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
864
856
|
import { spawnSync } from "node:child_process";
|
|
865
857
|
import { createHash } from "node:crypto";
|
|
866
858
|
import { join as join4 } from "node:path";
|
|
@@ -908,7 +900,6 @@ Usage:
|
|
|
908
900
|
envsave passwd Change master password
|
|
909
901
|
envsave config Show rate limit config
|
|
910
902
|
envsave config --max-gets <n> Set max gets per minute (password required)
|
|
911
|
-
envsave config --session-ttl <hours> Set session TTL in hours (password required)
|
|
912
903
|
envsave list List clean key names (password required)
|
|
913
904
|
envsave import <file> Import from .env file (password required)
|
|
914
905
|
envsave export Export as .env format (password required)
|
|
@@ -1051,9 +1042,9 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
|
|
|
1051
1042
|
process.exit(1);
|
|
1052
1043
|
}
|
|
1053
1044
|
const password = await requirePassword();
|
|
1054
|
-
const opaque = setSecret(key, value, password);
|
|
1045
|
+
const { opaque, updated } = setSecret(key, value, password);
|
|
1055
1046
|
console.log(`
|
|
1056
|
-
${key} → ${opaque}
|
|
1047
|
+
${updated ? "Updated" : "Added"}: ${key} → ${opaque}
|
|
1057
1048
|
`);
|
|
1058
1049
|
console.log(` Use this in your code to access ${key}:`);
|
|
1059
1050
|
console.log(` envsave.get("${opaque}")
|
|
@@ -1144,32 +1135,23 @@ Use 'envsave reveal <KEY>' to get the opaque key for each secret.`);
|
|
|
1144
1135
|
}
|
|
1145
1136
|
case "config": {
|
|
1146
1137
|
const maxGets = getFlag("--max-gets");
|
|
1147
|
-
|
|
1148
|
-
if (!maxGets && !sessionTtl) {
|
|
1138
|
+
if (!maxGets) {
|
|
1149
1139
|
const config2 = getRateLimitConfig();
|
|
1150
1140
|
console.log(`Rate limit config:`);
|
|
1151
1141
|
console.log(` Max gets per minute: ${config2.maxGetsPerMinute}`);
|
|
1152
|
-
console.log(` Session TTL: ${config2.sessionTtlHours} hours`);
|
|
1153
1142
|
break;
|
|
1154
1143
|
}
|
|
1155
1144
|
const password = await requirePassword();
|
|
1156
|
-
const options = {
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
if (sessionTtl)
|
|
1160
|
-
options.sessionTtlHours = parseFloat(sessionTtl);
|
|
1145
|
+
const options = {
|
|
1146
|
+
maxGetsPerMinute: parseInt(maxGets, 10)
|
|
1147
|
+
};
|
|
1161
1148
|
if (options.maxGetsPerMinute !== undefined && (isNaN(options.maxGetsPerMinute) || options.maxGetsPerMinute < 1)) {
|
|
1162
1149
|
console.error("--max-gets must be a positive number.");
|
|
1163
1150
|
process.exit(1);
|
|
1164
1151
|
}
|
|
1165
|
-
if (options.sessionTtlHours !== undefined && (isNaN(options.sessionTtlHours) || options.sessionTtlHours <= 0)) {
|
|
1166
|
-
console.error("--session-ttl must be a positive number.");
|
|
1167
|
-
process.exit(1);
|
|
1168
|
-
}
|
|
1169
1152
|
const config = configureRateLimit(password, options);
|
|
1170
1153
|
console.log(`Config updated:`);
|
|
1171
1154
|
console.log(` Max gets per minute: ${config.maxGetsPerMinute}`);
|
|
1172
|
-
console.log(` Session TTL: ${config.sessionTtlHours} hours`);
|
|
1173
1155
|
break;
|
|
1174
1156
|
}
|
|
1175
1157
|
case "passwd": {
|
|
@@ -1468,7 +1450,7 @@ Use 'envsave list' to verify your secrets.`);
|
|
|
1468
1450
|
break;
|
|
1469
1451
|
}
|
|
1470
1452
|
if (cloudStatus.hasBackup && hasLocal && cloudStatus.checksum !== localChecksum) {
|
|
1471
|
-
const localMtime =
|
|
1453
|
+
const localMtime = statSync2(vaultFile).mtimeMs;
|
|
1472
1454
|
const cloudDate = cloudStatus.updatedAt ? new Date(cloudStatus.updatedAt).getTime() : 0;
|
|
1473
1455
|
console.log("Local and cloud vaults differ:");
|
|
1474
1456
|
console.log(` Local: ${localSize} bytes, modified ${new Date(localMtime).toISOString()}`);
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/vault.ts
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, appendFileSync
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, chmodSync, appendFileSync } from "node:fs";
|
|
3
3
|
import { join as join2 } from "node:path";
|
|
4
4
|
import { homedir as homedir2 } from "node:os";
|
|
5
5
|
|
|
@@ -40,7 +40,6 @@ function getRateFile() {
|
|
|
40
40
|
function getAccessLog() {
|
|
41
41
|
return join2(getEnvsaveDir(), "access.log");
|
|
42
42
|
}
|
|
43
|
-
var DEFAULT_SESSION_TTL_MS = 4 * 60 * 60 * 1000;
|
|
44
43
|
var DEFAULT_RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
|
45
44
|
var DEFAULT_RATE_LIMIT_MAX = 30;
|
|
46
45
|
function getConfigFile() {
|
|
@@ -51,12 +50,12 @@ function guardRealVault() {
|
|
|
51
50
|
}
|
|
52
51
|
function readConfig() {
|
|
53
52
|
if (!existsSync(getConfigFile())) {
|
|
54
|
-
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX
|
|
53
|
+
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
|
|
55
54
|
}
|
|
56
55
|
try {
|
|
57
56
|
return JSON.parse(readFileSync(getConfigFile(), "utf8"));
|
|
58
57
|
} catch {
|
|
59
|
-
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX
|
|
58
|
+
return { maxGetsPerMinute: DEFAULT_RATE_LIMIT_MAX };
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
function ensureDir() {
|
|
@@ -69,12 +68,6 @@ function readSessionCache() {
|
|
|
69
68
|
if (!existsSync(getSessionFile()) || !existsSync(getCacheFile()))
|
|
70
69
|
return null;
|
|
71
70
|
try {
|
|
72
|
-
const sessionStat = statSync(getSessionFile());
|
|
73
|
-
const age = Date.now() - sessionStat.mtimeMs;
|
|
74
|
-
const config = readConfig();
|
|
75
|
-
if (age > config.sessionTtlHours * 3600000) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
71
|
const sessionKeyHex = readFileSync(getSessionFile(), "utf8").trim();
|
|
79
72
|
const sessionKey = Buffer.from(sessionKeyHex, "hex");
|
|
80
73
|
const payload = readFileSync(getCacheFile());
|