vaultkeeper 0.7.0 → 0.8.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/dist/index.d.cts CHANGED
@@ -527,6 +527,34 @@ declare class FilesystemError extends VaultError {
527
527
  declare class RotationInProgressError extends VaultError {
528
528
  constructor(message: string);
529
529
  }
530
+ /**
531
+ * Thrown when a test-only double — a class built purely to fabricate a
532
+ * vaultkeeper-internal signal (e.g. a granted presence check, an unlocked
533
+ * backend) for driving negative test cases — refuses to construct because it
534
+ * detected it is running outside a test environment.
535
+ *
536
+ * @remarks
537
+ * A fabricated signal is, by construction, exactly the thing a real
538
+ * deployment must never be able to forge. A loud, typed refusal at
539
+ * construction time is the last of several stacked guards keeping such a
540
+ * double unreachable from production — see, for example,
541
+ * `PresenceSimulatorBackend` in `@vaultkeeper/test-helpers`. Inspect
542
+ * {@link TestDoubleMisuseError.doubleName} for which class refused to
543
+ * construct and {@link TestDoubleMisuseError.detectedEnvironment} for the
544
+ * environment value that triggered the refusal.
545
+ *
546
+ * @public
547
+ */
548
+ declare class TestDoubleMisuseError extends VaultError {
549
+ /** The name of the test double class that refused to construct. */
550
+ readonly doubleName: string;
551
+ /**
552
+ * The environment value (e.g. `process.env.NODE_ENV`) that triggered the
553
+ * refusal.
554
+ */
555
+ readonly detectedEnvironment: string;
556
+ constructor(message: string, doubleName: string, detectedEnvironment: string);
557
+ }
530
558
 
531
559
  /**
532
560
  * Shared types and interfaces for vaultkeeper.
@@ -2128,4 +2156,4 @@ declare function platformNativeBackendType(): string;
2128
2156
  */
2129
2157
  declare function loadConfig(configDir?: string): Promise<VaultConfig>;
2130
2158
 
2131
- export { AccessorConsumedError, AuthorizationDeniedError, type BackendCapabilities, type BackendConfig, type BackendFactory, BackendLockedError, BackendRegistry, type BackendSetupFactory, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, type ExecRequest, type ExecResult, ExecutableTrustRequiredError, type ExecutableTrustStatus, FetchError, type FetchRequest, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, type KeyStatus, type ListableBackend, NotCapableError, type Platform, PluginNotFoundError, type PreflightCheck, type PreflightCheckError, type PreflightCheckErrorKind, type PreflightCheckStatus, type PreflightResult, type PresenceCapableBackend, PresenceDeclinedError, type PresenceOperation, type PresenceRequirementOptions, PresenceTimeoutError, REDACTED, RotationInProgressError, type RunDoctorOptions, type ScopedPreflightCheck, type SecretAccessor, type SecretBackend, SecretNotFoundError, type SecretTokenMap, type SetupChoice, SetupError, type SetupOptions, type SetupOptionsBase, type SetupQuestion, type SetupResult, type SignRequest, type SignResult, type SigningAlgorithm, type SigningBackend, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, type SigningPublicKey, TokenExpiredError, TokenRevokedError, type TrustTier, UnknownBackendTypeError, UsageLimitExceededError, type VaultConfig, VaultError, VaultKeeper, type VaultKeeperOptions, type VaultResponse, type VerifyRequest, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
2159
+ export { AccessorConsumedError, AuthorizationDeniedError, type BackendCapabilities, type BackendConfig, type BackendFactory, BackendLockedError, BackendRegistry, type BackendSetupFactory, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, type ExecRequest, type ExecResult, ExecutableTrustRequiredError, type ExecutableTrustStatus, FetchError, type FetchRequest, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, type KeyStatus, type ListableBackend, NotCapableError, type Platform, PluginNotFoundError, type PreflightCheck, type PreflightCheckError, type PreflightCheckErrorKind, type PreflightCheckStatus, type PreflightResult, type PresenceCapableBackend, PresenceDeclinedError, type PresenceOperation, type PresenceRequirementOptions, PresenceTimeoutError, REDACTED, RotationInProgressError, type RunDoctorOptions, type ScopedPreflightCheck, type SecretAccessor, type SecretBackend, SecretNotFoundError, type SecretTokenMap, type SetupChoice, SetupError, type SetupOptions, type SetupOptionsBase, type SetupQuestion, type SetupResult, type SignRequest, type SignResult, type SigningAlgorithm, type SigningBackend, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, type SigningPublicKey, TestDoubleMisuseError, TokenExpiredError, TokenRevokedError, type TrustTier, UnknownBackendTypeError, UsageLimitExceededError, type VaultConfig, VaultError, VaultKeeper, type VaultKeeperOptions, type VaultResponse, type VerifyRequest, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
package/dist/index.d.ts CHANGED
@@ -527,6 +527,34 @@ declare class FilesystemError extends VaultError {
527
527
  declare class RotationInProgressError extends VaultError {
528
528
  constructor(message: string);
529
529
  }
530
+ /**
531
+ * Thrown when a test-only double — a class built purely to fabricate a
532
+ * vaultkeeper-internal signal (e.g. a granted presence check, an unlocked
533
+ * backend) for driving negative test cases — refuses to construct because it
534
+ * detected it is running outside a test environment.
535
+ *
536
+ * @remarks
537
+ * A fabricated signal is, by construction, exactly the thing a real
538
+ * deployment must never be able to forge. A loud, typed refusal at
539
+ * construction time is the last of several stacked guards keeping such a
540
+ * double unreachable from production — see, for example,
541
+ * `PresenceSimulatorBackend` in `@vaultkeeper/test-helpers`. Inspect
542
+ * {@link TestDoubleMisuseError.doubleName} for which class refused to
543
+ * construct and {@link TestDoubleMisuseError.detectedEnvironment} for the
544
+ * environment value that triggered the refusal.
545
+ *
546
+ * @public
547
+ */
548
+ declare class TestDoubleMisuseError extends VaultError {
549
+ /** The name of the test double class that refused to construct. */
550
+ readonly doubleName: string;
551
+ /**
552
+ * The environment value (e.g. `process.env.NODE_ENV`) that triggered the
553
+ * refusal.
554
+ */
555
+ readonly detectedEnvironment: string;
556
+ constructor(message: string, doubleName: string, detectedEnvironment: string);
557
+ }
530
558
 
531
559
  /**
532
560
  * Shared types and interfaces for vaultkeeper.
@@ -2128,4 +2156,4 @@ declare function platformNativeBackendType(): string;
2128
2156
  */
2129
2157
  declare function loadConfig(configDir?: string): Promise<VaultConfig>;
2130
2158
 
2131
- export { AccessorConsumedError, AuthorizationDeniedError, type BackendCapabilities, type BackendConfig, type BackendFactory, BackendLockedError, BackendRegistry, type BackendSetupFactory, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, type ExecRequest, type ExecResult, ExecutableTrustRequiredError, type ExecutableTrustStatus, FetchError, type FetchRequest, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, type KeyStatus, type ListableBackend, NotCapableError, type Platform, PluginNotFoundError, type PreflightCheck, type PreflightCheckError, type PreflightCheckErrorKind, type PreflightCheckStatus, type PreflightResult, type PresenceCapableBackend, PresenceDeclinedError, type PresenceOperation, type PresenceRequirementOptions, PresenceTimeoutError, REDACTED, RotationInProgressError, type RunDoctorOptions, type ScopedPreflightCheck, type SecretAccessor, type SecretBackend, SecretNotFoundError, type SecretTokenMap, type SetupChoice, SetupError, type SetupOptions, type SetupOptionsBase, type SetupQuestion, type SetupResult, type SignRequest, type SignResult, type SigningAlgorithm, type SigningBackend, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, type SigningPublicKey, TokenExpiredError, TokenRevokedError, type TrustTier, UnknownBackendTypeError, UsageLimitExceededError, type VaultConfig, VaultError, VaultKeeper, type VaultKeeperOptions, type VaultResponse, type VerifyRequest, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
2159
+ export { AccessorConsumedError, AuthorizationDeniedError, type BackendCapabilities, type BackendConfig, type BackendFactory, BackendLockedError, BackendRegistry, type BackendSetupFactory, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, type ExecRequest, type ExecResult, ExecutableTrustRequiredError, type ExecutableTrustStatus, FetchError, type FetchRequest, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, type KeyStatus, type ListableBackend, NotCapableError, type Platform, PluginNotFoundError, type PreflightCheck, type PreflightCheckError, type PreflightCheckErrorKind, type PreflightCheckStatus, type PreflightResult, type PresenceCapableBackend, PresenceDeclinedError, type PresenceOperation, type PresenceRequirementOptions, PresenceTimeoutError, REDACTED, RotationInProgressError, type RunDoctorOptions, type ScopedPreflightCheck, type SecretAccessor, type SecretBackend, SecretNotFoundError, type SecretTokenMap, type SetupChoice, SetupError, type SetupOptions, type SetupOptionsBase, type SetupQuestion, type SetupResult, type SignRequest, type SignResult, type SigningAlgorithm, type SigningBackend, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, type SigningPublicKey, TestDoubleMisuseError, TokenExpiredError, TokenRevokedError, type TrustTier, UnknownBackendTypeError, UsageLimitExceededError, type VaultConfig, VaultError, VaultKeeper, type VaultKeeperOptions, type VaultResponse, type VerifyRequest, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
package/dist/index.js CHANGED
@@ -448,6 +448,21 @@ var RotationInProgressError = class extends VaultError {
448
448
  this.name = "RotationInProgressError";
449
449
  }
450
450
  };
451
+ var TestDoubleMisuseError = class extends VaultError {
452
+ /** The name of the test double class that refused to construct. */
453
+ doubleName;
454
+ /**
455
+ * The environment value (e.g. `process.env.NODE_ENV`) that triggered the
456
+ * refusal.
457
+ */
458
+ detectedEnvironment;
459
+ constructor(message, doubleName, detectedEnvironment) {
460
+ super(message);
461
+ this.name = "TestDoubleMisuseError";
462
+ this.doubleName = doubleName;
463
+ this.detectedEnvironment = detectedEnvironment;
464
+ }
465
+ };
451
466
 
452
467
  // src/backend/registry.ts
453
468
  var BackendRegistry = class {
@@ -1366,13 +1381,19 @@ var DpapiBackend = class {
1366
1381
  const entryPath = getEntryPath2(storageDir, id);
1367
1382
  const script = [
1368
1383
  "Add-Type -AssemblyName System.Security",
1369
- `$bytes = [System.Text.Encoding]::UTF8.GetBytes(${JSON.stringify(secret)})`,
1384
+ "$b64 = [Console]::In.ReadToEnd()",
1385
+ "$bytes = [System.Convert]::FromBase64String($b64.Trim())",
1370
1386
  "$entropy = $null",
1371
1387
  "$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser",
1372
1388
  "$encrypted = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $entropy, $scope)",
1373
1389
  `[System.IO.File]::WriteAllBytes(${JSON.stringify(entryPath)}, $encrypted)`
1374
1390
  ].join("; ");
1375
- await execCommand("powershell", ["-NoProfile", "-Command", script]);
1391
+ const secretBuf = Buffer.from(secret, "utf8");
1392
+ const stdinPayload = secretBuf.toString("base64");
1393
+ secretBuf.fill(0);
1394
+ await execCommand("powershell", ["-NoProfile", "-Command", script], {
1395
+ stdin: stdinPayload
1396
+ });
1376
1397
  }
1377
1398
  async retrieve(id) {
1378
1399
  const storageDir = this.#storageDir;
@@ -1429,6 +1450,9 @@ var DpapiBackend = class {
1429
1450
  // src/backend/secret-tool-backend.ts
1430
1451
  var ATTRIBUTE_KEY = "vaultkeeper-id";
1431
1452
  var LABEL_PREFIX = "vaultkeeper: ";
1453
+ function stripSecretToolTrailingNewline(stdout) {
1454
+ return stdout.endsWith("\n") ? stdout.slice(0, -1) : stdout;
1455
+ }
1432
1456
  var SecretToolBackend = class {
1433
1457
  type = "secret-tool";
1434
1458
  displayName = "Linux Secret Service (secret-tool)";
@@ -1445,29 +1469,29 @@ var SecretToolBackend = class {
1445
1469
  }
1446
1470
  async store(id, secret) {
1447
1471
  const label = `${LABEL_PREFIX}${id}`;
1448
- await execCommand("secret-tool", ["store", "--label", label, ATTRIBUTE_KEY, id], {
1472
+ await execCommand("secret-tool", ["store", "--label", label, "--", ATTRIBUTE_KEY, id], {
1449
1473
  stdin: secret
1450
1474
  });
1451
1475
  }
1452
1476
  async retrieve(id) {
1453
- const result = await execCommandFull("secret-tool", ["lookup", ATTRIBUTE_KEY, id]);
1454
- if (result.exitCode !== 0 || result.stdout.trim() === "") {
1477
+ const result = await execCommandFull("secret-tool", ["lookup", "--", ATTRIBUTE_KEY, id]);
1478
+ if (result.exitCode !== 0) {
1455
1479
  throw new SecretNotFoundError(`Secret not found in Secret Service: ${id}`);
1456
1480
  }
1457
- return result.stdout.trim();
1481
+ return stripSecretToolTrailingNewline(result.stdout);
1458
1482
  }
1459
1483
  async delete(id) {
1460
- const result = await execCommandFull("secret-tool", ["clear", ATTRIBUTE_KEY, id]);
1484
+ const result = await execCommandFull("secret-tool", ["clear", "--", ATTRIBUTE_KEY, id]);
1461
1485
  if (result.exitCode !== 0) {
1462
1486
  throw new SecretNotFoundError(`Secret not found in Secret Service: ${id}`);
1463
1487
  }
1464
1488
  }
1465
1489
  async exists(id) {
1466
- const result = await execCommandFull("secret-tool", ["lookup", ATTRIBUTE_KEY, id]);
1467
- return result.exitCode === 0 && result.stdout.trim() !== "";
1490
+ const result = await execCommandFull("secret-tool", ["lookup", "--", ATTRIBUTE_KEY, id]);
1491
+ return result.exitCode === 0;
1468
1492
  }
1469
1493
  async list() {
1470
- const result = await execCommandFull("secret-tool", ["search", ATTRIBUTE_KEY, ""]);
1494
+ const result = await execCommandFull("secret-tool", ["search", "--", ATTRIBUTE_KEY, ""]);
1471
1495
  if (result.exitCode !== 0) {
1472
1496
  return [];
1473
1497
  }
@@ -2925,11 +2949,16 @@ async function createToken(key, claims, options) {
2925
2949
  function isObject2(value) {
2926
2950
  return typeof value === "object" && value !== null && !Array.isArray(value);
2927
2951
  }
2952
+ function isLeasePresence(value) {
2953
+ if (!isObject2(value)) return false;
2954
+ const { op, at, method, backend } = value;
2955
+ return typeof op === "string" && typeof at === "number" && typeof method === "string" && typeof backend === "string";
2956
+ }
2928
2957
  function parseVaultClaims(raw) {
2929
2958
  if (!isObject2(raw)) {
2930
2959
  return void 0;
2931
2960
  }
2932
- const { jti, exp, iat, sub, exe, use, tid, bkd, val, ref } = raw;
2961
+ const { jti, exp, iat, sub, exe, use, tid, bkd, val, ref, kty, kid, kgen, pres } = raw;
2933
2962
  if (typeof jti !== "string") return void 0;
2934
2963
  if (typeof exp !== "number") return void 0;
2935
2964
  if (typeof iat !== "number") return void 0;
@@ -2937,10 +2966,29 @@ function parseVaultClaims(raw) {
2937
2966
  if (typeof exe !== "string") return void 0;
2938
2967
  if (use !== null && typeof use !== "number") return void 0;
2939
2968
  if (tid !== 1 && tid !== 2 && tid !== 3) return void 0;
2940
- if (typeof bkd !== "string") return void 0;
2941
- if (typeof val !== "string") return void 0;
2969
+ if (bkd !== void 0 && typeof bkd !== "string") return void 0;
2970
+ if (val !== void 0 && typeof val !== "string") return void 0;
2942
2971
  if (typeof ref !== "string") return void 0;
2943
- return { jti, exp, iat, sub, exe, use: use ?? null, tid, bkd, val, ref };
2972
+ if (kty !== void 0 && kty !== "secret" && kty !== "signing-key") return void 0;
2973
+ if (kid !== void 0 && typeof kid !== "string") return void 0;
2974
+ if (kgen !== void 0 && typeof kgen !== "number") return void 0;
2975
+ if (pres !== void 0 && !isLeasePresence(pres)) return void 0;
2976
+ return {
2977
+ jti,
2978
+ exp,
2979
+ iat,
2980
+ sub,
2981
+ exe,
2982
+ use: use ?? null,
2983
+ tid,
2984
+ bkd,
2985
+ val,
2986
+ ref,
2987
+ kty,
2988
+ kid,
2989
+ kgen,
2990
+ pres
2991
+ };
2944
2992
  }
2945
2993
  async function decryptToken(key, jwe) {
2946
2994
  let plaintext;
@@ -3021,15 +3069,44 @@ function validateClaims(claims, usedCount = 0) {
3021
3069
  if (claims.exe.trim() === "") {
3022
3070
  throw new VaultError("Invalid token: exe must not be empty");
3023
3071
  }
3024
- if (claims.bkd.trim() === "") {
3025
- throw new VaultError("Invalid token: bkd must not be empty");
3026
- }
3027
- if (claims.val.trim() === "") {
3028
- throw new VaultError("Invalid token: val must not be empty");
3029
- }
3030
3072
  if (claims.ref.trim() === "") {
3031
3073
  throw new VaultError("Invalid token: ref must not be empty");
3032
3074
  }
3075
+ switch (claims.kty) {
3076
+ case "signing-key": {
3077
+ if (claims.val !== void 0) {
3078
+ throw new VaultError("Invalid token: signing lease must not carry a val");
3079
+ }
3080
+ if (claims.kid === void 0 || claims.kid.trim() === "") {
3081
+ throw new VaultError("Invalid token: kid must not be empty");
3082
+ }
3083
+ if (claims.kgen === void 0) {
3084
+ throw new VaultError("Invalid token: kgen is required for a signing lease");
3085
+ }
3086
+ if (!Number.isSafeInteger(claims.kgen) || claims.kgen < 0) {
3087
+ throw new VaultError("Invalid token: kgen must be a non-negative integer");
3088
+ }
3089
+ break;
3090
+ }
3091
+ case "secret":
3092
+ case void 0: {
3093
+ if (claims.bkd === void 0 || claims.bkd.trim() === "") {
3094
+ throw new VaultError("Invalid token: bkd must not be empty");
3095
+ }
3096
+ if (claims.val === void 0 || claims.val.trim() === "") {
3097
+ throw new VaultError("Invalid token: val must not be empty");
3098
+ }
3099
+ if (claims.kid !== void 0 || claims.kgen !== void 0 || claims.pres !== void 0) {
3100
+ throw new VaultError(
3101
+ "Invalid token: secret claim must not carry signing-lease fields (kid/kgen/pres)"
3102
+ );
3103
+ }
3104
+ break;
3105
+ }
3106
+ default: {
3107
+ throw new VaultError(`Invalid token: unrecognized claim kind kty=${String(claims.kty)}`);
3108
+ }
3109
+ }
3033
3110
  if (claims.iat > claims.exp) {
3034
3111
  throw new VaultError("Invalid token: iat must not be after exp");
3035
3112
  }
@@ -4044,6 +4121,16 @@ var VaultKeeper = class _VaultKeeper {
4044
4121
  "This capability token authorizes a signing key, not a secret \u2014 it cannot be read with getSecret(). Use sign() instead."
4045
4122
  );
4046
4123
  }
4124
+ if (claims.kty === "signing-key") {
4125
+ throw new AuthorizationDeniedError(
4126
+ "This capability token authorizes a signing key, not a secret \u2014 it cannot be read with getSecret(). Use sign() instead."
4127
+ );
4128
+ }
4129
+ if (claims.val === void 0) {
4130
+ throw new AuthorizationDeniedError(
4131
+ "This capability token does not carry a secret value \u2014 it is malformed or was not minted as a secret token."
4132
+ );
4133
+ }
4047
4134
  return createSecretAccessor(claims.val);
4048
4135
  }
4049
4136
  /**
@@ -4386,7 +4473,7 @@ var VaultKeeper = class _VaultKeeper {
4386
4473
  // ---------------------------------------------------------------------------
4387
4474
  static #resolveSecrets(token) {
4388
4475
  if (token instanceof CapabilityToken) {
4389
- return _VaultKeeper.#requireSecretClaims(token).val;
4476
+ return _VaultKeeper.#requireSecretValue(token);
4390
4477
  }
4391
4478
  const result = {};
4392
4479
  for (const [name, t] of Object.entries(token)) {
@@ -4395,7 +4482,7 @@ var VaultKeeper = class _VaultKeeper {
4395
4482
  `Invalid capability token for secret "${name}" \u2014 expected a CapabilityToken from authorize()`
4396
4483
  );
4397
4484
  }
4398
- result[name] = _VaultKeeper.#requireSecretClaims(t).val;
4485
+ result[name] = _VaultKeeper.#requireSecretValue(t);
4399
4486
  }
4400
4487
  return result;
4401
4488
  }
@@ -4403,7 +4490,10 @@ var VaultKeeper = class _VaultKeeper {
4403
4490
  * Resolve a token to its secret claims, rejecting a signing-key token.
4404
4491
  *
4405
4492
  * Defense in depth: a signing-key capability must never be injectable as a
4406
- * secret through `fetch()`/`exec()`.
4493
+ * secret through `fetch()`/`exec()`. This rejects both the in-memory
4494
+ * signing-key token (`isSigningClaims`) and a JWE-based signing-key lease
4495
+ * (`kty: 'signing-key'`) — the two capability shapes are discriminated
4496
+ * differently but neither ever carries a secret value.
4407
4497
  */
4408
4498
  static #requireSecretClaims(token) {
4409
4499
  const claims = validateCapabilityToken(token);
@@ -4412,8 +4502,21 @@ var VaultKeeper = class _VaultKeeper {
4412
4502
  "This capability token authorizes a signing key, not a secret \u2014 it cannot be injected into fetch() or exec()."
4413
4503
  );
4414
4504
  }
4505
+ if (claims.kty === "signing-key") {
4506
+ throw new AuthorizationDeniedError(
4507
+ "This capability token authorizes a signing-key lease, not a secret \u2014 it cannot be injected into fetch() or exec()."
4508
+ );
4509
+ }
4415
4510
  return claims;
4416
4511
  }
4512
+ /** Resolve a token to its secret value, rejecting a claims shape with no `val`. */
4513
+ static #requireSecretValue(token) {
4514
+ const claims = _VaultKeeper.#requireSecretClaims(token);
4515
+ if (claims.val === void 0 || claims.val.trim() === "") {
4516
+ throw new AuthorizationDeniedError("This capability token does not authorize a secret value.");
4517
+ }
4518
+ return claims.val;
4519
+ }
4417
4520
  /**
4418
4521
  * Validate a caller-supplied resource name. `kind` names the resource in the
4419
4522
  * error so a signing-key caller is not told about a "secret".
@@ -4575,6 +4678,6 @@ var VaultKeeper = class _VaultKeeper {
4575
4678
  }
4576
4679
  };
4577
4680
 
4578
- export { AccessorConsumedError, AuthorizationDeniedError, BackendLockedError, BackendRegistry, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, ExecutableTrustRequiredError, FetchError, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, NotCapableError, PluginNotFoundError, PresenceDeclinedError, PresenceTimeoutError, REDACTED, RotationInProgressError, SecretNotFoundError, SetupError, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, TokenExpiredError, TokenRevokedError, UnknownBackendTypeError, UsageLimitExceededError, VaultError, VaultKeeper, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
4681
+ export { AccessorConsumedError, AuthorizationDeniedError, BackendLockedError, BackendRegistry, BackendUnavailableError, CapabilityToken, ConfigParseError, ConfigValidationError, DecryptionError, DeviceNotPresentError, ExecError, ExecutableTrustRequiredError, FetchError, FilesystemError, IdentityMismatchError, InvalidAlgorithmError, InvalidKeyMaterialError, InvalidTokenError, KeyRevokedError, KeyRotatedError, NotCapableError, PluginNotFoundError, PresenceDeclinedError, PresenceTimeoutError, REDACTED, RotationInProgressError, SecretNotFoundError, SetupError, SigningKeyAlreadyExistsError, SigningKeyNotFoundError, SigningNotSupportedError, TestDoubleMisuseError, TokenExpiredError, TokenRevokedError, UnknownBackendTypeError, UsageLimitExceededError, VaultError, VaultKeeper, defaultBackendType, getBackendCapabilities, getDefaultConfigDir, getPlatformDefaultConfigDir, isListableBackend, isPresenceCapableBackend, isSigningBackend, loadConfig, platformNativeBackendType, redactSecrets, runDoctor };
4579
4682
  //# sourceMappingURL=index.js.map
4580
4683
  //# sourceMappingURL=index.js.map