vaultkeeper 0.7.0 → 0.7.1
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/index.cjs +100 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +100 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1391,13 +1391,19 @@ var DpapiBackend = class {
|
|
|
1391
1391
|
const entryPath = getEntryPath2(storageDir, id);
|
|
1392
1392
|
const script = [
|
|
1393
1393
|
"Add-Type -AssemblyName System.Security",
|
|
1394
|
-
|
|
1394
|
+
"$b64 = [Console]::In.ReadToEnd()",
|
|
1395
|
+
"$bytes = [System.Convert]::FromBase64String($b64.Trim())",
|
|
1395
1396
|
"$entropy = $null",
|
|
1396
1397
|
"$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser",
|
|
1397
1398
|
"$encrypted = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $entropy, $scope)",
|
|
1398
1399
|
`[System.IO.File]::WriteAllBytes(${JSON.stringify(entryPath)}, $encrypted)`
|
|
1399
1400
|
].join("; ");
|
|
1400
|
-
|
|
1401
|
+
const secretBuf = Buffer.from(secret, "utf8");
|
|
1402
|
+
const stdinPayload = secretBuf.toString("base64");
|
|
1403
|
+
secretBuf.fill(0);
|
|
1404
|
+
await execCommand("powershell", ["-NoProfile", "-Command", script], {
|
|
1405
|
+
stdin: stdinPayload
|
|
1406
|
+
});
|
|
1401
1407
|
}
|
|
1402
1408
|
async retrieve(id) {
|
|
1403
1409
|
const storageDir = this.#storageDir;
|
|
@@ -2950,11 +2956,16 @@ async function createToken(key, claims, options) {
|
|
|
2950
2956
|
function isObject2(value) {
|
|
2951
2957
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2952
2958
|
}
|
|
2959
|
+
function isLeasePresence(value) {
|
|
2960
|
+
if (!isObject2(value)) return false;
|
|
2961
|
+
const { op, at, method, backend } = value;
|
|
2962
|
+
return typeof op === "string" && typeof at === "number" && typeof method === "string" && typeof backend === "string";
|
|
2963
|
+
}
|
|
2953
2964
|
function parseVaultClaims(raw) {
|
|
2954
2965
|
if (!isObject2(raw)) {
|
|
2955
2966
|
return void 0;
|
|
2956
2967
|
}
|
|
2957
|
-
const { jti, exp, iat, sub, exe, use, tid, bkd, val, ref } = raw;
|
|
2968
|
+
const { jti, exp, iat, sub, exe, use, tid, bkd, val, ref, kty, kid, kgen, pres } = raw;
|
|
2958
2969
|
if (typeof jti !== "string") return void 0;
|
|
2959
2970
|
if (typeof exp !== "number") return void 0;
|
|
2960
2971
|
if (typeof iat !== "number") return void 0;
|
|
@@ -2962,10 +2973,29 @@ function parseVaultClaims(raw) {
|
|
|
2962
2973
|
if (typeof exe !== "string") return void 0;
|
|
2963
2974
|
if (use !== null && typeof use !== "number") return void 0;
|
|
2964
2975
|
if (tid !== 1 && tid !== 2 && tid !== 3) return void 0;
|
|
2965
|
-
if (typeof bkd !== "string") return void 0;
|
|
2966
|
-
if (typeof val !== "string") return void 0;
|
|
2976
|
+
if (bkd !== void 0 && typeof bkd !== "string") return void 0;
|
|
2977
|
+
if (val !== void 0 && typeof val !== "string") return void 0;
|
|
2967
2978
|
if (typeof ref !== "string") return void 0;
|
|
2968
|
-
|
|
2979
|
+
if (kty !== void 0 && kty !== "secret" && kty !== "signing-key") return void 0;
|
|
2980
|
+
if (kid !== void 0 && typeof kid !== "string") return void 0;
|
|
2981
|
+
if (kgen !== void 0 && typeof kgen !== "number") return void 0;
|
|
2982
|
+
if (pres !== void 0 && !isLeasePresence(pres)) return void 0;
|
|
2983
|
+
return {
|
|
2984
|
+
jti,
|
|
2985
|
+
exp,
|
|
2986
|
+
iat,
|
|
2987
|
+
sub,
|
|
2988
|
+
exe,
|
|
2989
|
+
use: use ?? null,
|
|
2990
|
+
tid,
|
|
2991
|
+
bkd,
|
|
2992
|
+
val,
|
|
2993
|
+
ref,
|
|
2994
|
+
kty,
|
|
2995
|
+
kid,
|
|
2996
|
+
kgen,
|
|
2997
|
+
pres
|
|
2998
|
+
};
|
|
2969
2999
|
}
|
|
2970
3000
|
async function decryptToken(key, jwe) {
|
|
2971
3001
|
let plaintext;
|
|
@@ -3046,15 +3076,44 @@ function validateClaims(claims, usedCount = 0) {
|
|
|
3046
3076
|
if (claims.exe.trim() === "") {
|
|
3047
3077
|
throw new VaultError("Invalid token: exe must not be empty");
|
|
3048
3078
|
}
|
|
3049
|
-
if (claims.bkd.trim() === "") {
|
|
3050
|
-
throw new VaultError("Invalid token: bkd must not be empty");
|
|
3051
|
-
}
|
|
3052
|
-
if (claims.val.trim() === "") {
|
|
3053
|
-
throw new VaultError("Invalid token: val must not be empty");
|
|
3054
|
-
}
|
|
3055
3079
|
if (claims.ref.trim() === "") {
|
|
3056
3080
|
throw new VaultError("Invalid token: ref must not be empty");
|
|
3057
3081
|
}
|
|
3082
|
+
switch (claims.kty) {
|
|
3083
|
+
case "signing-key": {
|
|
3084
|
+
if (claims.val !== void 0) {
|
|
3085
|
+
throw new VaultError("Invalid token: signing lease must not carry a val");
|
|
3086
|
+
}
|
|
3087
|
+
if (claims.kid === void 0 || claims.kid.trim() === "") {
|
|
3088
|
+
throw new VaultError("Invalid token: kid must not be empty");
|
|
3089
|
+
}
|
|
3090
|
+
if (claims.kgen === void 0) {
|
|
3091
|
+
throw new VaultError("Invalid token: kgen is required for a signing lease");
|
|
3092
|
+
}
|
|
3093
|
+
if (!Number.isSafeInteger(claims.kgen) || claims.kgen < 0) {
|
|
3094
|
+
throw new VaultError("Invalid token: kgen must be a non-negative integer");
|
|
3095
|
+
}
|
|
3096
|
+
break;
|
|
3097
|
+
}
|
|
3098
|
+
case "secret":
|
|
3099
|
+
case void 0: {
|
|
3100
|
+
if (claims.bkd === void 0 || claims.bkd.trim() === "") {
|
|
3101
|
+
throw new VaultError("Invalid token: bkd must not be empty");
|
|
3102
|
+
}
|
|
3103
|
+
if (claims.val === void 0 || claims.val.trim() === "") {
|
|
3104
|
+
throw new VaultError("Invalid token: val must not be empty");
|
|
3105
|
+
}
|
|
3106
|
+
if (claims.kid !== void 0 || claims.kgen !== void 0 || claims.pres !== void 0) {
|
|
3107
|
+
throw new VaultError(
|
|
3108
|
+
"Invalid token: secret claim must not carry signing-lease fields (kid/kgen/pres)"
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
break;
|
|
3112
|
+
}
|
|
3113
|
+
default: {
|
|
3114
|
+
throw new VaultError(`Invalid token: unrecognized claim kind kty=${String(claims.kty)}`);
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3058
3117
|
if (claims.iat > claims.exp) {
|
|
3059
3118
|
throw new VaultError("Invalid token: iat must not be after exp");
|
|
3060
3119
|
}
|
|
@@ -4069,6 +4128,16 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
4069
4128
|
"This capability token authorizes a signing key, not a secret \u2014 it cannot be read with getSecret(). Use sign() instead."
|
|
4070
4129
|
);
|
|
4071
4130
|
}
|
|
4131
|
+
if (claims.kty === "signing-key") {
|
|
4132
|
+
throw new AuthorizationDeniedError(
|
|
4133
|
+
"This capability token authorizes a signing key, not a secret \u2014 it cannot be read with getSecret(). Use sign() instead."
|
|
4134
|
+
);
|
|
4135
|
+
}
|
|
4136
|
+
if (claims.val === void 0) {
|
|
4137
|
+
throw new AuthorizationDeniedError(
|
|
4138
|
+
"This capability token does not carry a secret value \u2014 it is malformed or was not minted as a secret token."
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4072
4141
|
return createSecretAccessor(claims.val);
|
|
4073
4142
|
}
|
|
4074
4143
|
/**
|
|
@@ -4411,7 +4480,7 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
4411
4480
|
// ---------------------------------------------------------------------------
|
|
4412
4481
|
static #resolveSecrets(token) {
|
|
4413
4482
|
if (token instanceof CapabilityToken) {
|
|
4414
|
-
return _VaultKeeper.#
|
|
4483
|
+
return _VaultKeeper.#requireSecretValue(token);
|
|
4415
4484
|
}
|
|
4416
4485
|
const result = {};
|
|
4417
4486
|
for (const [name, t] of Object.entries(token)) {
|
|
@@ -4420,7 +4489,7 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
4420
4489
|
`Invalid capability token for secret "${name}" \u2014 expected a CapabilityToken from authorize()`
|
|
4421
4490
|
);
|
|
4422
4491
|
}
|
|
4423
|
-
result[name] = _VaultKeeper.#
|
|
4492
|
+
result[name] = _VaultKeeper.#requireSecretValue(t);
|
|
4424
4493
|
}
|
|
4425
4494
|
return result;
|
|
4426
4495
|
}
|
|
@@ -4428,7 +4497,10 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
4428
4497
|
* Resolve a token to its secret claims, rejecting a signing-key token.
|
|
4429
4498
|
*
|
|
4430
4499
|
* Defense in depth: a signing-key capability must never be injectable as a
|
|
4431
|
-
* secret through `fetch()`/`exec()`.
|
|
4500
|
+
* secret through `fetch()`/`exec()`. This rejects both the in-memory
|
|
4501
|
+
* signing-key token (`isSigningClaims`) and a JWE-based signing-key lease
|
|
4502
|
+
* (`kty: 'signing-key'`) — the two capability shapes are discriminated
|
|
4503
|
+
* differently but neither ever carries a secret value.
|
|
4432
4504
|
*/
|
|
4433
4505
|
static #requireSecretClaims(token) {
|
|
4434
4506
|
const claims = validateCapabilityToken(token);
|
|
@@ -4437,8 +4509,21 @@ var VaultKeeper = class _VaultKeeper {
|
|
|
4437
4509
|
"This capability token authorizes a signing key, not a secret \u2014 it cannot be injected into fetch() or exec()."
|
|
4438
4510
|
);
|
|
4439
4511
|
}
|
|
4512
|
+
if (claims.kty === "signing-key") {
|
|
4513
|
+
throw new AuthorizationDeniedError(
|
|
4514
|
+
"This capability token authorizes a signing-key lease, not a secret \u2014 it cannot be injected into fetch() or exec()."
|
|
4515
|
+
);
|
|
4516
|
+
}
|
|
4440
4517
|
return claims;
|
|
4441
4518
|
}
|
|
4519
|
+
/** Resolve a token to its secret value, rejecting a claims shape with no `val`. */
|
|
4520
|
+
static #requireSecretValue(token) {
|
|
4521
|
+
const claims = _VaultKeeper.#requireSecretClaims(token);
|
|
4522
|
+
if (claims.val === void 0 || claims.val.trim() === "") {
|
|
4523
|
+
throw new AuthorizationDeniedError("This capability token does not authorize a secret value.");
|
|
4524
|
+
}
|
|
4525
|
+
return claims.val;
|
|
4526
|
+
}
|
|
4442
4527
|
/**
|
|
4443
4528
|
* Validate a caller-supplied resource name. `kind` names the resource in the
|
|
4444
4529
|
* error so a signing-key caller is not told about a "secret".
|