@blamejs/pki 0.7.13 → 0.7.14

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 CHANGED
@@ -4,6 +4,19 @@ All notable changes to `@blamejs/pki` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
5
5
  project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## v0.7.14 — 2026-09-11
8
+
9
+ Every option a verify verb decides with is taken once.
10
+
11
+ ### Changed
12
+
13
+ - The pki.smime verbs take their options once at entry. sign, verify, encrypt, decrypt, compress, decompress and buildCertsOnly share one option-checking helper, and it now returns the options it checked rather than leaving the caller's object to be read again by each later pass.
14
+
15
+ ### Security
16
+
17
+ - pki.jose.verify took opts.key seven times: a type gate, the RFC 7638 thumbprint comparison against the jwk the JWS embeds, and the key the signature is verified under. An accessor-backed opts.key could satisfy the comparison with the embedded key and hand a different one to the verification, so a JWS naming a signer the caller did not pin returned a verdict of keySource "opts.key" instead of the jose/key-mismatch refusal a plain object gets. The option is taken once, so the key compared is the key used.
18
+ - pki.webauthn.parseClientData took each expected* option twice: once to decide whether to compare, once for the value compared, and the returned checked field then recorded that the comparison ran. An accessor-backed expectedChallenge could enter the branch on the challenge a ceremony issued and be compared against the challenge the response carried, returning checked.challenge true for a comparison that never ran against the caller's value. expectedType and expectedOrigin behaved the same way. Each option is taken once, so checked answers for the value the caller supplied. pki.webauthn.verify and pki.webauthn.verifyAssertion already copied their inputs and were not affected.
19
+
7
20
  ## v0.7.13 — 2026-09-10
8
21
 
9
22
  The certificate signer refuses a pre-encoded extension whose criticality RFC 5280 fixes.
package/lib/jose.js CHANGED
@@ -319,6 +319,11 @@ var _VERIFY_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, profile: 1
319
319
  async function verify(jws, opts) {
320
320
  opts = opts || {};
321
321
  guard.identifier.assertKnownKeys(opts, _VERIFY_KEYS, E, "jose/bad-input", "unknown pki.jose.verify option ");
322
+ /** @internal opts.key is examined several times: the type gate, the RFC 7638 thumbprint comparison
323
+ * against the embedded jwk, and the key the signature is verified under. An option reached through
324
+ * an accessor answers each read separately, so the key the comparison ran on need not be the key
325
+ * the verification used. Take each option once. */
326
+ opts = guard.identifier.snapshotOptions(opts, _VERIFY_KEYS);
322
327
  if (!jws || typeof jws !== "object" || _isArray(jws)) throw E("jose/bad-jws", "a flattened JWS object is required");
323
328
  if (_hasOwn(jws, "signatures")) throw E("jose/bad-jws", "the multi-signature signatures member is forbidden");
324
329
  if (_hasOwn(jws, "header")) throw E("jose/bad-jws", "the unprotected header member is forbidden");
package/lib/smime.js CHANGED
@@ -64,11 +64,15 @@ var DECRYPT_OPTS = Object.assign(Object.create(null), { recipientIndex: 1, maxIt
64
64
  var COMPRESS_OPTS = Object.assign(Object.create(null), { entity: 1, contentType: 1, level: 1 });
65
65
  var DECOMPRESS_OPTS = Object.assign(Object.create(null), { maxOutputBytes: 1 });
66
66
 
67
+ /** @internal Checks the option names and RETURNS the options taken once. A verb reads one option to
68
+ * decide whether a rule applies and reads it again for the value the rule uses, and an option
69
+ * reached through an accessor answers each read separately. Callers assign the return value. */
67
70
  function _knownOpts(opts, known, verb) {
68
71
  guard.identifier.assertKnownKeys(opts, known, _err, "smime/bad-input", function (k) {
69
72
  return "unknown option " + JSON.stringify(k) + " for pki.smime." + verb + " -- accepted: " +
70
73
  Object.keys(known).sort().join(", ");
71
74
  });
75
+ return guard.identifier.snapshotOptions(opts, known);
72
76
  }
73
77
 
74
78
  function _isPkcs7(type, kind) {
@@ -500,7 +504,7 @@ function _base64Body(der) {
500
504
  */
501
505
  async function sign(content, signers, opts) {
502
506
  opts = opts || {};
503
- _knownOpts(opts, SIGN_OPTS, "sign");
507
+ opts = _knownOpts(opts, SIGN_OPTS, "sign");
504
508
  var hp = opts.protectHeaders === true;
505
509
  var hpList = hp ? _hpHeaderList(opts.headers) : null;
506
510
  var outerList = hp ? _outerHeaderList(hpList, "clear", opts.hcp) : null;
@@ -612,7 +616,7 @@ function _capped(msg) {
612
616
  */
613
617
  async function verify(message, opts) {
614
618
  opts = opts || {};
615
- _knownOpts(opts, VERIFY_OPTS, "verify");
619
+ opts = _knownOpts(opts, VERIFY_OPTS, "verify");
616
620
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
617
621
  var ct = ent.contentType;
618
622
  var vOpts = {};
@@ -720,7 +724,7 @@ function _cmsEncryptOpts(opts) {
720
724
  */
721
725
  async function encrypt(content, recipients, opts) {
722
726
  opts = opts || {};
723
- _knownOpts(opts, ENCRYPT_OPTS, "encrypt");
727
+ opts = _knownOpts(opts, ENCRYPT_OPTS, "encrypt");
724
728
  var hp = opts.protectHeaders === true;
725
729
  var hpList = hp ? _hpHeaderList(opts.headers) : null;
726
730
  var outerList = hp ? _outerHeaderList(hpList, "cipher", opts.hcp) : null;
@@ -776,7 +780,7 @@ async function encrypt(content, recipients, opts) {
776
780
  */
777
781
  async function decrypt(message, keyMaterial, opts) {
778
782
  opts = opts || {};
779
- _knownOpts(opts, DECRYPT_OPTS, "decrypt");
783
+ opts = _knownOpts(opts, DECRYPT_OPTS, "decrypt");
780
784
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
781
785
  var ct = ent.contentType;
782
786
  if (!_isPkcs7(ct.type, "mime")) throw _err("smime/unsupported-type", "not an encrypted S/MIME message (Content-Type " + JSON.stringify(ct.type) + ")");
@@ -821,7 +825,7 @@ async function decrypt(message, keyMaterial, opts) {
821
825
  */
822
826
  async function compress(content, opts) {
823
827
  opts = opts || {};
824
- _knownOpts(opts, COMPRESS_OPTS, "compress");
828
+ opts = _knownOpts(opts, COMPRESS_OPTS, "compress");
825
829
  var entity = _entityBytes(content, opts);
826
830
  var cOpts = {};
827
831
  if (opts.level !== undefined) cOpts.level = opts.level;
@@ -855,7 +859,7 @@ async function compress(content, opts) {
855
859
  */
856
860
  async function decompress(message, opts) {
857
861
  opts = opts || {};
858
- _knownOpts(opts, DECOMPRESS_OPTS, "decompress");
862
+ opts = _knownOpts(opts, DECOMPRESS_OPTS, "decompress");
859
863
  var ent = mime.parse(message, SmimeError, "smime/bad-mime");
860
864
  var ct = ent.contentType;
861
865
  if (!_isPkcs7(ct.type, "mime")) throw _err("smime/unsupported-type", "not a compressed S/MIME message (Content-Type " + JSON.stringify(ct.type) + ")");
@@ -901,7 +905,7 @@ var CERTS_ONLY_OPTS = Object.assign(Object.create(null), { crls: 1 });
901
905
  */
902
906
  function buildCertsOnly(certs, opts) {
903
907
  opts = opts || {};
904
- _knownOpts(opts, CERTS_ONLY_OPTS, "buildCertsOnly");
908
+ opts = _knownOpts(opts, CERTS_ONLY_OPTS, "buildCertsOnly");
905
909
  var der = _toBuf(cms.certsOnly(certs, { crls: opts.crls }));
906
910
  return _capped(Buffer.concat([_pkcs7MimeHead("certs-only", "smime.p7c"), _base64Body(der), mime.CRLF]));
907
911
  }
package/lib/webauthn.js CHANGED
@@ -1275,6 +1275,11 @@ function parseClientData(bytes, opts) {
1275
1275
  opts = opts || {};
1276
1276
  if (!_isPlainObject(opts)) throw _err("webauthn/bad-input", "opts must be an object");
1277
1277
  guard.identifier.assertKnownKeys(opts, _CLIENT_DATA_OPTS, _err, "webauthn/bad-input", "opts has an unknown key ");
1278
+ /** @internal Each expected* option is read to decide whether to compare and read again for the
1279
+ * value compared, and `checked` then records that the comparison ran. An option reached through an
1280
+ * accessor answers each read separately, so `checked` could describe a comparison against a value
1281
+ * the caller never supplied. Take each option once. */
1282
+ opts = guard.identifier.snapshotOptions(opts, _CLIENT_DATA_OPTS);
1278
1283
  bytes = _bytesArg(bytes, "clientDataJSON");
1279
1284
  var doc = guard.json.parse(Buffer.from(bytes), _err, {
1280
1285
  maxBytes: constants.LIMITS.JSON_MAX_BYTES, maxDepth: constants.LIMITS.JSON_MAX_DEPTH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.7.13",
3
+ "version": "0.7.14",
4
4
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
package/sbom.cdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.5",
5
- "serialNumber": "urn:uuid:32a9603e-875f-4326-b00f-f40f8927e720",
5
+ "serialNumber": "urn:uuid:bf9440b0-2e9b-45e1-8551-36b17188bd96",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-09-11T01:36:27.751Z",
8
+ "timestamp": "2026-09-11T02:22:59.455Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/pki@0.7.13",
22
+ "bom-ref": "@blamejs/pki@0.7.14",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.7.13",
25
+ "version": "0.7.14",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
29
- "purl": "pkg:npm/%40blamejs/pki@0.7.13",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.7.14",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/pki@0.7.13",
57
+ "ref": "@blamejs/pki@0.7.14",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]