sec-ry 1.1.5
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.bundle.js +7211 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +37 -0
- package/dist/cmd/changelog.d.ts +2 -0
- package/dist/cmd/changelog.js +726 -0
- package/dist/cmd/denc.d.ts +2 -0
- package/dist/cmd/denc.js +74 -0
- package/dist/cmd/enc.d.ts +2 -0
- package/dist/cmd/enc.js +225 -0
- package/dist/cmd/example.d.ts +1 -0
- package/dist/cmd/example.js +92 -0
- package/dist/cmd/fp.d.ts +2 -0
- package/dist/cmd/fp.js +29 -0
- package/dist/cmd/gen.d.ts +2 -0
- package/dist/cmd/gen.js +76 -0
- package/dist/cmd/history.d.ts +2 -0
- package/dist/cmd/history.js +25 -0
- package/dist/cmd/info.d.ts +2 -0
- package/dist/cmd/info.js +28 -0
- package/dist/cmd/root.d.ts +1 -0
- package/dist/cmd/root.js +191 -0
- package/dist/cmd/seal.d.ts +3 -0
- package/dist/cmd/seal.js +81 -0
- package/dist/cmd/upgrade.d.ts +1 -0
- package/dist/cmd/upgrade.js +44 -0
- package/dist/cmd/verify.d.ts +2 -0
- package/dist/cmd/verify.js +30 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +27 -0
- package/dist/lib/args.d.ts +8 -0
- package/dist/lib/args.js +43 -0
- package/dist/lib/clip.d.ts +2 -0
- package/dist/lib/clip.js +39 -0
- package/dist/lib/crypto.d.ts +29 -0
- package/dist/lib/crypto.js +312 -0
- package/dist/lib/env.d.ts +13 -0
- package/dist/lib/env.js +42 -0
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.js +61 -0
- package/dist/lib/history.d.ts +9 -0
- package/dist/lib/history.js +35 -0
- package/dist/lib/password.d.ts +2 -0
- package/dist/lib/password.js +31 -0
- package/dist/lib/rc.d.ts +7 -0
- package/dist/lib/rc.js +45 -0
- package/dist/lib/ui.d.ts +21 -0
- package/dist/lib/ui.js +137 -0
- package/package.json +44 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.VERSION = exports.TOKEN_PREFIX = void 0;
|
|
37
|
+
exports.encrypt = encrypt;
|
|
38
|
+
exports.decrypt = decrypt;
|
|
39
|
+
exports.verify = verify;
|
|
40
|
+
exports.hmacFingerprint = hmacFingerprint;
|
|
41
|
+
exports.inspectToken = inspectToken;
|
|
42
|
+
exports.generatePassword = generatePassword;
|
|
43
|
+
exports.parseExpiry = parseExpiry;
|
|
44
|
+
exports.seal = seal;
|
|
45
|
+
exports.sealVerify = sealVerify;
|
|
46
|
+
exports.sealS2 = sealS2;
|
|
47
|
+
exports.sealVerifyS2 = sealVerifyS2;
|
|
48
|
+
const crypto_1 = require("crypto");
|
|
49
|
+
const _PFX_V1 = "secry:v1:";
|
|
50
|
+
const _PFX_V2 = "secry:v2:";
|
|
51
|
+
const _PFX_V3 = "secry:v3:";
|
|
52
|
+
const _CPX_V1 = "sec:v1:";
|
|
53
|
+
const _CPX_V2 = "sec:v2:";
|
|
54
|
+
const _CPX_V3 = "sec:v3:";
|
|
55
|
+
const _ALG_V1 = "aes-256-gcm";
|
|
56
|
+
const _ALG_V2 = "chacha20-poly1305";
|
|
57
|
+
const _ALG_V3 = "aes-256-cbc";
|
|
58
|
+
const _SL = 16;
|
|
59
|
+
const _NL = 12;
|
|
60
|
+
const _NL_V3 = 16;
|
|
61
|
+
const _TL = 16;
|
|
62
|
+
const _KL = 32;
|
|
63
|
+
const _SL_C = 8;
|
|
64
|
+
const _SCRYPT = { N: 16384, r: 8, p: 1 };
|
|
65
|
+
const _IS_TERMUX = (process.env.PREFIX ?? "").includes("com.termux");
|
|
66
|
+
const _SCRYPT_S2 = _IS_TERMUX
|
|
67
|
+
? { N: 16384, r: 4, p: 2 }
|
|
68
|
+
: { N: 65536, r: 8, p: 1 };
|
|
69
|
+
exports.TOKEN_PREFIX = _PFX_V2;
|
|
70
|
+
exports.VERSION = "v2";
|
|
71
|
+
function _kdf(pass, salt) {
|
|
72
|
+
const buf = Buffer.from(pass, "utf8");
|
|
73
|
+
const key = (0, crypto_1.scryptSync)(buf, salt, _KL, _SCRYPT);
|
|
74
|
+
buf.fill(0);
|
|
75
|
+
return key;
|
|
76
|
+
}
|
|
77
|
+
function _encExp(ms) {
|
|
78
|
+
const b = Buffer.allocUnsafe(8);
|
|
79
|
+
b.writeUInt32BE(Math.floor(ms / 0x100000000), 0);
|
|
80
|
+
b.writeUInt32BE(ms >>> 0, 4);
|
|
81
|
+
return b;
|
|
82
|
+
}
|
|
83
|
+
function _decExp(b) {
|
|
84
|
+
return b.readUInt32BE(0) * 0x100000000 + b.readUInt32BE(4);
|
|
85
|
+
}
|
|
86
|
+
function encrypt(plaintext, password, opts = {}, version = 2, compact = false) {
|
|
87
|
+
const pfx = compact ? (version === 3 ? _CPX_V3 : version === 1 ? _CPX_V1 : _CPX_V2) : (version === 3 ? _PFX_V3 : version === 1 ? _PFX_V1 : _PFX_V2);
|
|
88
|
+
const sl = compact ? _SL_C : _SL;
|
|
89
|
+
const alg = version === 2 ? _ALG_V2 : version === 3 ? _ALG_V3 : _ALG_V1;
|
|
90
|
+
const nl = version === 3 ? _NL_V3 : _NL;
|
|
91
|
+
const salt = (0, crypto_1.randomBytes)(sl);
|
|
92
|
+
const nonce = (0, crypto_1.randomBytes)(nl);
|
|
93
|
+
const key = _kdf(password, salt);
|
|
94
|
+
const hasx = opts.expiresMs !== undefined;
|
|
95
|
+
const meta = Buffer.alloc(1);
|
|
96
|
+
meta[0] = hasx ? 1 : 0;
|
|
97
|
+
const inner = Buffer.concat([meta, hasx ? _encExp(opts.expiresMs) : Buffer.alloc(0), Buffer.from(plaintext, "utf8")]);
|
|
98
|
+
if (version === 3) {
|
|
99
|
+
const ciph = (0, crypto_1.createCipheriv)(alg, key, nonce);
|
|
100
|
+
const body = Buffer.concat([ciph.update(inner), ciph.final()]);
|
|
101
|
+
return pfx + Buffer.concat([salt, nonce, body]).toString("base64url");
|
|
102
|
+
}
|
|
103
|
+
const ciph = (0, crypto_1.createCipheriv)(alg, key, nonce, { authTagLength: _TL });
|
|
104
|
+
const body = Buffer.concat([ciph.update(inner), ciph.final()]);
|
|
105
|
+
const tag = ciph.getAuthTag();
|
|
106
|
+
return pfx + Buffer.concat([salt, nonce, tag, body]).toString("base64url");
|
|
107
|
+
}
|
|
108
|
+
function decrypt(token, password) {
|
|
109
|
+
let pfx;
|
|
110
|
+
let alg;
|
|
111
|
+
let ver;
|
|
112
|
+
let nl;
|
|
113
|
+
let compact = false;
|
|
114
|
+
if (token.startsWith(_PFX_V3)) {
|
|
115
|
+
pfx = _PFX_V3;
|
|
116
|
+
alg = _ALG_V3;
|
|
117
|
+
ver = 3;
|
|
118
|
+
nl = _NL_V3;
|
|
119
|
+
}
|
|
120
|
+
else if (token.startsWith(_PFX_V2)) {
|
|
121
|
+
pfx = _PFX_V2;
|
|
122
|
+
alg = _ALG_V2;
|
|
123
|
+
ver = 2;
|
|
124
|
+
nl = _NL;
|
|
125
|
+
}
|
|
126
|
+
else if (token.startsWith(_PFX_V1)) {
|
|
127
|
+
pfx = _PFX_V1;
|
|
128
|
+
alg = _ALG_V1;
|
|
129
|
+
ver = 1;
|
|
130
|
+
nl = _NL;
|
|
131
|
+
}
|
|
132
|
+
else if (token.startsWith(_CPX_V3)) {
|
|
133
|
+
pfx = _CPX_V3;
|
|
134
|
+
alg = _ALG_V3;
|
|
135
|
+
ver = 3;
|
|
136
|
+
nl = _NL_V3;
|
|
137
|
+
compact = true;
|
|
138
|
+
}
|
|
139
|
+
else if (token.startsWith(_CPX_V2)) {
|
|
140
|
+
pfx = _CPX_V2;
|
|
141
|
+
alg = _ALG_V2;
|
|
142
|
+
ver = 2;
|
|
143
|
+
nl = _NL;
|
|
144
|
+
compact = true;
|
|
145
|
+
}
|
|
146
|
+
else if (token.startsWith(_CPX_V1)) {
|
|
147
|
+
pfx = _CPX_V1;
|
|
148
|
+
alg = _ALG_V1;
|
|
149
|
+
ver = 1;
|
|
150
|
+
nl = _NL;
|
|
151
|
+
compact = true;
|
|
152
|
+
}
|
|
153
|
+
else
|
|
154
|
+
throw Object.assign(new Error("invalid token format"), { code: "ERR_FORMAT" });
|
|
155
|
+
const payload = Buffer.from(token.slice(pfx.length), "base64url");
|
|
156
|
+
const sl = compact ? _SL_C : _SL;
|
|
157
|
+
const salt = payload.subarray(0, sl);
|
|
158
|
+
const nonce = payload.subarray(sl, sl + nl);
|
|
159
|
+
const key = _kdf(password, salt);
|
|
160
|
+
let inner;
|
|
161
|
+
try {
|
|
162
|
+
if (ver === 3) {
|
|
163
|
+
const dc = (0, crypto_1.createDecipheriv)(alg, key, nonce);
|
|
164
|
+
inner = Buffer.concat([dc.update(payload.subarray(sl + nl)), dc.final()]);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const tag = payload.subarray(sl + nl, sl + nl + _TL);
|
|
168
|
+
const body = payload.subarray(sl + nl + _TL);
|
|
169
|
+
const dc = (0, crypto_1.createDecipheriv)(alg, key, nonce, ver >= 2 ? { authTagLength: _TL } : undefined);
|
|
170
|
+
dc.setAuthTag(tag);
|
|
171
|
+
inner = Buffer.concat([dc.update(body), dc.final()]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
throw Object.assign(new Error("wrong password or corrupted token"), { code: "ERR_AUTH" });
|
|
176
|
+
}
|
|
177
|
+
const hasx = inner[0] === 1;
|
|
178
|
+
if (hasx) {
|
|
179
|
+
const expMs = _decExp(inner.subarray(1, 9));
|
|
180
|
+
const expDate = new Date(expMs);
|
|
181
|
+
if (Date.now() > expMs)
|
|
182
|
+
throw Object.assign(new Error(`token expired at ${expDate.toISOString()}`), { code: "ERR_EXPIRED", expiredAt: expDate });
|
|
183
|
+
return { plaintext: inner.subarray(9).toString("utf8"), expiresAt: expDate, tokenVersion: ver };
|
|
184
|
+
}
|
|
185
|
+
return { plaintext: inner.subarray(1).toString("utf8"), tokenVersion: ver };
|
|
186
|
+
}
|
|
187
|
+
function verify(token, password) {
|
|
188
|
+
try {
|
|
189
|
+
decrypt(token, password);
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function hmacFingerprint(input, secret) {
|
|
197
|
+
return (0, crypto_1.createHmac)("sha256", secret).update(input).digest("hex").slice(0, 16);
|
|
198
|
+
}
|
|
199
|
+
function inspectToken(token) {
|
|
200
|
+
let pfx;
|
|
201
|
+
let algo;
|
|
202
|
+
let ver;
|
|
203
|
+
let nl;
|
|
204
|
+
let compact2 = false;
|
|
205
|
+
if (token.startsWith(_PFX_V3)) {
|
|
206
|
+
pfx = _PFX_V3;
|
|
207
|
+
algo = "AES-256-CBC";
|
|
208
|
+
ver = 3;
|
|
209
|
+
nl = _NL_V3;
|
|
210
|
+
}
|
|
211
|
+
else if (token.startsWith(_PFX_V2)) {
|
|
212
|
+
pfx = _PFX_V2;
|
|
213
|
+
algo = "ChaCha20-Poly1305";
|
|
214
|
+
ver = 2;
|
|
215
|
+
nl = _NL;
|
|
216
|
+
}
|
|
217
|
+
else if (token.startsWith(_PFX_V1)) {
|
|
218
|
+
pfx = _PFX_V1;
|
|
219
|
+
algo = "AES-256-GCM";
|
|
220
|
+
ver = 1;
|
|
221
|
+
nl = _NL;
|
|
222
|
+
}
|
|
223
|
+
else if (token.startsWith(_CPX_V3)) {
|
|
224
|
+
pfx = _CPX_V3;
|
|
225
|
+
algo = "AES-256-CBC";
|
|
226
|
+
ver = 3;
|
|
227
|
+
nl = _NL_V3;
|
|
228
|
+
compact2 = true;
|
|
229
|
+
}
|
|
230
|
+
else if (token.startsWith(_CPX_V2)) {
|
|
231
|
+
pfx = _CPX_V2;
|
|
232
|
+
algo = "ChaCha20-Poly1305";
|
|
233
|
+
ver = 2;
|
|
234
|
+
nl = _NL;
|
|
235
|
+
compact2 = true;
|
|
236
|
+
}
|
|
237
|
+
else if (token.startsWith(_CPX_V1)) {
|
|
238
|
+
pfx = _CPX_V1;
|
|
239
|
+
algo = "AES-256-GCM";
|
|
240
|
+
ver = 1;
|
|
241
|
+
nl = _NL;
|
|
242
|
+
compact2 = true;
|
|
243
|
+
}
|
|
244
|
+
else
|
|
245
|
+
throw Object.assign(new Error("invalid token format"), { code: "ERR_FORMAT" });
|
|
246
|
+
const payload = Buffer.from(token.slice(pfx.length), "base64url");
|
|
247
|
+
const sl2 = compact2 ? _SL_C : _SL;
|
|
248
|
+
if (payload.length < sl2 + nl + 2)
|
|
249
|
+
throw Object.assign(new Error("token is too short or malformed"), { code: "ERR_MALFORMED" });
|
|
250
|
+
return { version: `v${ver}`, prefix: pfx, algo, compact: compact2, saltHex: payload.subarray(0, sl2).toString("hex"), nonceHex: payload.subarray(sl2, sl2 + nl).toString("hex"), payloadBytes: payload.length };
|
|
251
|
+
}
|
|
252
|
+
function generatePassword(bytes = 24) { return (0, crypto_1.randomBytes)(bytes).toString("base64url"); }
|
|
253
|
+
function parseExpiry(raw) {
|
|
254
|
+
const m = raw.match(/^(\d+)(s|m|h|d)$/);
|
|
255
|
+
if (!m)
|
|
256
|
+
throw new Error(`invalid expiry format: "${raw}" — use 30s, 5m, 2h, 7d`);
|
|
257
|
+
const u = { s: 1000, m: 60000, h: 3600000, d: 86400000 };
|
|
258
|
+
return Date.now() + parseInt(m[1], 10) * u[m[2]];
|
|
259
|
+
}
|
|
260
|
+
const _PFX_S1 = "secry:s1:";
|
|
261
|
+
const _PFX_S2 = "secry:s2:";
|
|
262
|
+
const _PEPPER_S2 = "enclove.secry.s2.pepper.v1";
|
|
263
|
+
function seal(input, secret) {
|
|
264
|
+
const salt = (0, crypto_1.randomBytes)(16);
|
|
265
|
+
const key = (0, crypto_1.scryptSync)(Buffer.from(secret, "utf8"), salt, 32, _SCRYPT);
|
|
266
|
+
const mac = (0, crypto_1.createHmac)("sha256", key).update(input, "utf8").digest();
|
|
267
|
+
return _PFX_S1 + Buffer.concat([salt, mac]).toString("base64url");
|
|
268
|
+
}
|
|
269
|
+
function sealVerify(input, secret, token) {
|
|
270
|
+
if (!token.startsWith(_PFX_S1))
|
|
271
|
+
return false;
|
|
272
|
+
try {
|
|
273
|
+
const raw = Buffer.from(token.slice(_PFX_S1.length), "base64url");
|
|
274
|
+
if (raw.length < 48)
|
|
275
|
+
return false;
|
|
276
|
+
const salt = raw.subarray(0, 16);
|
|
277
|
+
const mac = raw.subarray(16);
|
|
278
|
+
const key = (0, crypto_1.scryptSync)(Buffer.from(secret, "utf8"), salt, 32, _SCRYPT);
|
|
279
|
+
const exp = (0, crypto_1.createHmac)("sha256", key).update(input, "utf8").digest();
|
|
280
|
+
return exp.length === mac.length && require("crypto").timingSafeEqual(exp, mac);
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function sealS2(input, secret) {
|
|
287
|
+
const salt = (0, crypto_1.randomBytes)(32);
|
|
288
|
+
const peppered = Buffer.from(_PEPPER_S2 + secret, "utf8");
|
|
289
|
+
const key = (0, crypto_1.scryptSync)(peppered, salt, 32, _SCRYPT_S2);
|
|
290
|
+
const { blake3 } = await Promise.resolve().then(() => __importStar(require("@noble/hashes/blake3.js")));
|
|
291
|
+
const out = blake3(new TextEncoder().encode(input), { key, dkLen: 64 });
|
|
292
|
+
return _PFX_S2 + Buffer.concat([salt, Buffer.from(out)]).toString("base64url");
|
|
293
|
+
}
|
|
294
|
+
async function sealVerifyS2(input, secret, token) {
|
|
295
|
+
if (!token.startsWith(_PFX_S2))
|
|
296
|
+
return false;
|
|
297
|
+
try {
|
|
298
|
+
const raw = Buffer.from(token.slice(_PFX_S2.length), "base64url");
|
|
299
|
+
if (raw.length < 96)
|
|
300
|
+
return false;
|
|
301
|
+
const salt = raw.subarray(0, 32);
|
|
302
|
+
const mac = raw.subarray(32);
|
|
303
|
+
const peppered = Buffer.from(_PEPPER_S2 + secret, "utf8");
|
|
304
|
+
const key = (0, crypto_1.scryptSync)(peppered, salt, 32, _SCRYPT_S2);
|
|
305
|
+
const { blake3 } = await Promise.resolve().then(() => __importStar(require("@noble/hashes/blake3.js")));
|
|
306
|
+
const exp = Buffer.from(blake3(new TextEncoder().encode(input), { key, dkLen: 64 }));
|
|
307
|
+
return exp.length === mac.length && require("crypto").timingSafeEqual(exp, mac);
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Env = "termux" | "linux" | "macos" | "unknown";
|
|
2
|
+
export interface EnvInfo {
|
|
3
|
+
type: Env;
|
|
4
|
+
isTermux: boolean;
|
|
5
|
+
isLinux: boolean;
|
|
6
|
+
isMacOS: boolean;
|
|
7
|
+
hasTTY: boolean;
|
|
8
|
+
hasRawMode: boolean;
|
|
9
|
+
hasColor: boolean;
|
|
10
|
+
cols: number;
|
|
11
|
+
rows: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const env: EnvInfo;
|
package/dist/lib/env.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.env = void 0;
|
|
4
|
+
function _detect() {
|
|
5
|
+
if (process.env.TERMUX_VERSION !== undefined ||
|
|
6
|
+
(process.env.PREFIX ?? "").includes("com.termux") ||
|
|
7
|
+
(process.env.HOME ?? "").includes("com.termux"))
|
|
8
|
+
return "termux";
|
|
9
|
+
if (process.platform === "darwin")
|
|
10
|
+
return "macos";
|
|
11
|
+
if (process.platform === "linux")
|
|
12
|
+
return "linux";
|
|
13
|
+
return "unknown";
|
|
14
|
+
}
|
|
15
|
+
function _cols() {
|
|
16
|
+
try {
|
|
17
|
+
return process.stdout.columns || 80;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return 80;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function _rows() {
|
|
24
|
+
try {
|
|
25
|
+
return process.stdout.rows || 24;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return 24;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const _type = _detect();
|
|
32
|
+
exports.env = {
|
|
33
|
+
type: _type,
|
|
34
|
+
isTermux: _type === "termux",
|
|
35
|
+
isLinux: _type === "linux",
|
|
36
|
+
isMacOS: _type === "macos",
|
|
37
|
+
hasTTY: !!process.stdin.isTTY,
|
|
38
|
+
hasRawMode: typeof process.stdin.setRawMode === "function",
|
|
39
|
+
hasColor: process.env.NO_COLOR === undefined && !!process.stderr.isTTY,
|
|
40
|
+
cols: _cols(),
|
|
41
|
+
rows: _rows(),
|
|
42
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installErrorHandler(): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installErrorHandler = installErrorHandler;
|
|
4
|
+
const _nc = !process.stderr.isTTY || process.env.NO_COLOR !== undefined;
|
|
5
|
+
const _e = _nc ? "" : "\x1b[31m";
|
|
6
|
+
const _c = _nc ? "" : "\x1b[36m";
|
|
7
|
+
const _d = _nc ? "" : "\x1b[2m";
|
|
8
|
+
const _r = _nc ? "" : "\x1b[0m";
|
|
9
|
+
const _DEFS = [
|
|
10
|
+
{ match: (m) => m.includes("secry:") && (m.includes("$") || m.includes("expanded")),
|
|
11
|
+
message: "token contains unescaped shell characters",
|
|
12
|
+
fix: "use single quotes: secry denc 'secry:v2:...' -sw password", exit: 2 },
|
|
13
|
+
{ match: (m, c) => c === "EACCES" || m.includes("permission denied"),
|
|
14
|
+
message: "permission denied",
|
|
15
|
+
fix: "check file permissions", exit: 2 },
|
|
16
|
+
{ match: (m, c) => c === "ENOMEM" || m.includes("out of memory") || m.includes("heap"),
|
|
17
|
+
message: "out of memory",
|
|
18
|
+
fix: "input may be too large. try: NODE_OPTIONS=--max-old-space-size=512 secry ...", exit: 2 },
|
|
19
|
+
{ match: (m) => m.includes("scrypt") || m.includes("memory limit exceeded"),
|
|
20
|
+
message: "scrypt memory limit exceeded",
|
|
21
|
+
fix: "device may have limited memory. try a shorter input or restart Termux", exit: 2 },
|
|
22
|
+
{ match: (m, c) => c === "MODULE_NOT_FOUND" || m.includes("cannot find module"),
|
|
23
|
+
message: "internal module not found",
|
|
24
|
+
fix: "reinstall secry: npm install -g secry", exit: 1 },
|
|
25
|
+
{ match: (m, c) => c === "ERR_INVALID_ARG_TYPE" || c === "ERR_INVALID_ARG_VALUE" || m.includes("invalid argument"),
|
|
26
|
+
message: "invalid argument",
|
|
27
|
+
fix: "run secry -h to see correct usage", exit: 1 },
|
|
28
|
+
{ match: (m, c) => c === "ENOENT" || m.includes("no such file"),
|
|
29
|
+
message: "file not found",
|
|
30
|
+
fix: "check that the file path is correct", exit: 2 },
|
|
31
|
+
];
|
|
32
|
+
function _fmt(msg, fix) {
|
|
33
|
+
return `\n${_e}error${_r} ${msg}\n${_d}fix${_r} ${_c}${fix}${_r}\n`;
|
|
34
|
+
}
|
|
35
|
+
function _handle(err) {
|
|
36
|
+
const msg = (err.message ?? "").toLowerCase();
|
|
37
|
+
const code = err.code ?? "";
|
|
38
|
+
for (const d of _DEFS) {
|
|
39
|
+
if (d.match(msg, code)) {
|
|
40
|
+
process.stderr.write(_fmt(d.message, d.fix));
|
|
41
|
+
process.exit(d.exit);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function _unknown(msg) {
|
|
47
|
+
process.stderr.write(`\n${_e}error${_r} unexpected error\n${_d}fix${_r} ${_c}run secry -h for usage${_r}\n${_d} ${msg}${_r}\n`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
function installErrorHandler() {
|
|
51
|
+
process.on("uncaughtException", (err) => { if (!_handle(err))
|
|
52
|
+
_unknown(err.message ?? String(err)); });
|
|
53
|
+
process.on("unhandledRejection", (reason) => {
|
|
54
|
+
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
55
|
+
if (!_handle(err))
|
|
56
|
+
_unknown(err.message);
|
|
57
|
+
});
|
|
58
|
+
process.on("warning", () => { });
|
|
59
|
+
const _orig = process.emit.bind(process);
|
|
60
|
+
process.emit = (ev, ...a) => ev === "warning" ? false : _orig(ev, ...a);
|
|
61
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordHistory = recordHistory;
|
|
4
|
+
exports.getHistory = getHistory;
|
|
5
|
+
exports.clearHistory = clearHistory;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const os_1 = require("os");
|
|
9
|
+
const crypto_1 = require("crypto");
|
|
10
|
+
const _FILE = (0, path_1.join)((0, os_1.tmpdir)(), ".secry_history");
|
|
11
|
+
function _load() {
|
|
12
|
+
if (!(0, fs_1.existsSync)(_FILE))
|
|
13
|
+
return [];
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse((0, fs_1.readFileSync)(_FILE, "utf8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function _save(e) {
|
|
22
|
+
try {
|
|
23
|
+
(0, fs_1.writeFileSync)(_FILE, JSON.stringify(e), "utf8");
|
|
24
|
+
}
|
|
25
|
+
catch { }
|
|
26
|
+
}
|
|
27
|
+
function recordHistory(cmd, token) {
|
|
28
|
+
const e = _load();
|
|
29
|
+
e.push({ ts: Date.now(), cmd, fp: (0, crypto_1.createHash)("sha256").update(token).digest("hex").slice(0, 8) });
|
|
30
|
+
if (e.length > 50)
|
|
31
|
+
e.splice(0, e.length - 50);
|
|
32
|
+
_save(e);
|
|
33
|
+
}
|
|
34
|
+
function getHistory() { return _load(); }
|
|
35
|
+
function clearHistory() { _save([]); }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePassword = resolvePassword;
|
|
4
|
+
exports.describePasswordSource = describePasswordSource;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
function resolvePassword(raw) {
|
|
7
|
+
if (raw.startsWith("@")) {
|
|
8
|
+
const path = raw.slice(1).trim();
|
|
9
|
+
try {
|
|
10
|
+
return (0, fs_1.readFileSync)(path, "utf8").trim();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw Object.assign(new Error(`cannot read password file: ${path}`), { code: "ERR_PWFILE" });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (raw.startsWith("env:")) {
|
|
17
|
+
const name = raw.slice(4).trim();
|
|
18
|
+
const val = process.env[name];
|
|
19
|
+
if (!val)
|
|
20
|
+
throw Object.assign(new Error(`env variable not set: ${name}`), { code: "ERR_PWENV" });
|
|
21
|
+
return val;
|
|
22
|
+
}
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
function describePasswordSource(raw) {
|
|
26
|
+
if (raw.startsWith("@"))
|
|
27
|
+
return `file(${raw.slice(1)})`;
|
|
28
|
+
if (raw.startsWith("env:"))
|
|
29
|
+
return `env(${raw.slice(4)})`;
|
|
30
|
+
return "inline";
|
|
31
|
+
}
|
package/dist/lib/rc.d.ts
ADDED
package/dist/lib/rc.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadRC = loadRC;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
const _DEFAULTS = { bytes: 24, maxSize: 20, clip: false };
|
|
8
|
+
const _RC_FILE = ".secryrc";
|
|
9
|
+
function _parse(raw) {
|
|
10
|
+
const out = {};
|
|
11
|
+
for (const line of raw.split("\n")) {
|
|
12
|
+
const t = line.trim();
|
|
13
|
+
if (!t || t.startsWith("#"))
|
|
14
|
+
continue;
|
|
15
|
+
const eq = t.indexOf("=");
|
|
16
|
+
if (eq === -1)
|
|
17
|
+
continue;
|
|
18
|
+
const k = t.slice(0, eq).trim();
|
|
19
|
+
const v = t.slice(eq + 1).trim();
|
|
20
|
+
if (k === "password")
|
|
21
|
+
out.password = v;
|
|
22
|
+
if (k === "bytes")
|
|
23
|
+
out.bytes = parseInt(v, 10);
|
|
24
|
+
if (k === "maxSize")
|
|
25
|
+
out.maxSize = parseInt(v, 10);
|
|
26
|
+
if (k === "clip")
|
|
27
|
+
out.clip = v === "true";
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
function _load(path) {
|
|
32
|
+
if (!(0, fs_1.existsSync)(path))
|
|
33
|
+
return null;
|
|
34
|
+
try {
|
|
35
|
+
return _parse((0, fs_1.readFileSync)(path, "utf8"));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function loadRC() {
|
|
42
|
+
const local = _load((0, path_1.resolve)(process.cwd(), _RC_FILE));
|
|
43
|
+
const global = _load((0, path_1.join)((0, os_1.homedir)(), _RC_FILE));
|
|
44
|
+
return { ..._DEFAULTS, ...global, ...local };
|
|
45
|
+
}
|
package/dist/lib/ui.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
|
2
|
+
export declare const PKG = "secry";
|
|
3
|
+
export declare const BIN = "secry";
|
|
4
|
+
export declare let QUIET: boolean;
|
|
5
|
+
export declare function setQuiet(v: boolean): void;
|
|
6
|
+
export declare function data(s: string): void;
|
|
7
|
+
export declare function err(msg: string, code?: number): never;
|
|
8
|
+
export declare function warn(msg: string): void;
|
|
9
|
+
export declare function info(msg: string): void;
|
|
10
|
+
export declare function ok(msg: string): void;
|
|
11
|
+
export interface BoxRow {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
valueColor?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function box(rows: BoxRow[]): void;
|
|
17
|
+
export declare function section(title: string): void;
|
|
18
|
+
export declare function token(t: string): void;
|
|
19
|
+
export declare function ms(elapsed: number): void;
|
|
20
|
+
export declare function row(label: string, value: string, color?: string): void;
|
|
21
|
+
export declare function usage(): void;
|