@blamejs/blamejs-shop 0.1.38 → 0.2.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +3 -3
  3. package/lib/storefront.js +94 -21
  4. package/lib/vendor/MANIFEST.json +2 -2
  5. package/lib/vendor/blamejs/CHANGELOG.md +8 -0
  6. package/lib/vendor/blamejs/README.md +1 -1
  7. package/lib/vendor/blamejs/SECURITY.md +1 -0
  8. package/lib/vendor/blamejs/api-snapshot.json +2 -2
  9. package/lib/vendor/blamejs/lib/archive-read.js +17 -0
  10. package/lib/vendor/blamejs/lib/auth/fal.js +12 -0
  11. package/lib/vendor/blamejs/lib/auth/jwt-external.js +15 -11
  12. package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
  13. package/lib/vendor/blamejs/lib/auth/oauth.js +7 -6
  14. package/lib/vendor/blamejs/lib/auth/oid4vci.js +3 -3
  15. package/lib/vendor/blamejs/lib/auth/saml.js +15 -12
  16. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +3 -3
  17. package/lib/vendor/blamejs/lib/calendar.js +9 -2
  18. package/lib/vendor/blamejs/lib/circuit-breaker.js +5 -4
  19. package/lib/vendor/blamejs/lib/crypto-hpke.js +1 -1
  20. package/lib/vendor/blamejs/lib/crypto-oprf.js +2 -2
  21. package/lib/vendor/blamejs/lib/crypto.js +2 -2
  22. package/lib/vendor/blamejs/lib/framework-error.js +2 -1
  23. package/lib/vendor/blamejs/lib/guard-jwt.js +3 -2
  24. package/lib/vendor/blamejs/lib/guard-smtp-command.js +2 -2
  25. package/lib/vendor/blamejs/lib/mail-auth.js +2 -2
  26. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1 -1
  27. package/lib/vendor/blamejs/lib/mail-crypto-smime.js +7 -7
  28. package/lib/vendor/blamejs/lib/mail-crypto.js +1 -1
  29. package/lib/vendor/blamejs/lib/mail-dav.js +5 -4
  30. package/lib/vendor/blamejs/lib/mail-deploy.js +3 -2
  31. package/lib/vendor/blamejs/lib/mail-server-imap.js +1 -1
  32. package/lib/vendor/blamejs/lib/mail-server-jmap.js +1 -1
  33. package/lib/vendor/blamejs/lib/mail-server-managesieve.js +1 -1
  34. package/lib/vendor/blamejs/lib/mail-server-mx.js +142 -47
  35. package/lib/vendor/blamejs/lib/mail-server-submission.js +3 -3
  36. package/lib/vendor/blamejs/lib/mail-store.js +2 -2
  37. package/lib/vendor/blamejs/lib/mail.js +2 -2
  38. package/lib/vendor/blamejs/lib/network-tls.js +10 -7
  39. package/lib/vendor/blamejs/lib/safe-decompress.js +8 -6
  40. package/lib/vendor/blamejs/lib/safe-ical.js +12 -12
  41. package/lib/vendor/blamejs/lib/safe-mime.js +6 -6
  42. package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
  43. package/lib/vendor/blamejs/lib/safe-smtp.js +1 -1
  44. package/lib/vendor/blamejs/package.json +1 -1
  45. package/lib/vendor/blamejs/release-notes/v0.13.10.json +44 -0
  46. package/lib/vendor/blamejs/release-notes/v0.13.11.json +27 -0
  47. package/lib/vendor/blamejs/release-notes/v0.13.12.json +36 -0
  48. package/lib/vendor/blamejs/release-notes/v0.13.9.json +27 -0
  49. package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +32 -0
  50. package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +4 -4
  51. package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +31 -0
  52. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +100 -7
  53. package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +26 -0
  54. package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +5 -5
  55. package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +1 -1
  56. package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +166 -1
  57. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +25 -18
  58. package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +2 -2
  59. package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +12 -12
  60. package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +15 -9
  61. package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +0 -6
  62. package/package.json +1 -1
@@ -441,15 +441,15 @@ async function verify(presentation, opts) {
441
441
  "verify: malformed JWT header: " + e.message);
442
442
  }
443
443
  var alg = headerObj.alg;
444
- // CVE-2026-23993 — refuse unknown / unsupported alg BEFORE any key
445
- // resolution. The shared `_assertAlgKtyMatch` helper repeats this
444
+ // Alg-allowlist gate (CWE-347 / CWE-757) — refuse unknown / unsupported
445
+ // alg BEFORE any key resolution. The shared `_assertAlgKtyMatch` helper repeats this
446
446
  // check after the issuer key is resolved; doing it here too closes
447
447
  // the gap where an issuerKeyResolver with side effects (network
448
448
  // fetch, audit emit) would run even when the alg is unsupported.
449
449
  if (typeof alg !== "string" || SUPPORTED_ALGS.indexOf(alg) === -1) {
450
450
  throw new AuthError("auth-sd-jwt-vc/unsupported-alg",
451
451
  "verify: header alg \"" + alg + "\" not in supported set " +
452
- "(CVE-2026-23993 — refused before key lookup)");
452
+ "(alg-allowlist gate — refused before key lookup)");
453
453
  }
454
454
  // draft-ietf-oauth-sd-jwt-vc §3.1 — typ MUST be `vc+sd-jwt` (or
455
455
  // `dc+sd-jwt` for digital-credential profile). Pre-v0.9.x the absent-
@@ -354,7 +354,14 @@ function validate(jsCal) {
354
354
  * // → { "@type":"Event", uid:"a@b", updated:"2026-05-21T10:00:00Z", ... }
355
355
  */
356
356
  function fromIcal(text, opts) {
357
- var ast = safeIcal.parse(text, opts || {});
357
+ // Forward parser options to b.safeIcal.parse. Accept BOTH the
358
+ // documented nested form (`{ safeIcalOpts: { profile, caps, ... } }`)
359
+ // AND the historically-working top-level form (`{ profile: "balanced" }`)
360
+ // so neither caller regresses; the nested form wins on conflict. The
361
+ // `safeIcalOpts` wrapper key itself is stripped before forwarding.
362
+ var icalOpts = Object.assign({}, opts || {}, (opts && opts.safeIcalOpts) || {});
363
+ delete icalOpts.safeIcalOpts;
364
+ var ast = safeIcal.parse(text, icalOpts);
358
365
  var events = (ast && ast.vcalendar && ast.vcalendar.vevent) || [];
359
366
  var todos = (ast && ast.vcalendar && ast.vcalendar.vtodo) || [];
360
367
  var journals = (ast && ast.vcalendar && ast.vcalendar.vjournal) || [];
@@ -515,7 +522,7 @@ function toIcal(jsCal, opts) {
515
522
  * timestamps in the operator's `[from, to]` window. Returns an array
516
523
  * of ISO 8601 UTC strings (`yyyy-mm-ddTHH:MM:SSZ`). Bounded by
517
524
  * `MAX_EXPAND_INSTANCES` (4096) + `MAX_EXPAND_SPAN_MS` (10 years) to
518
- * defend against CVE-2024-39687-class recurrence-bomb expansion.
525
+ * defend against the RRULE recurrence-bomb expansion class.
519
526
  *
520
527
  * v1 supports FREQ=DAILY/WEEKLY/MONTHLY/YEARLY with INTERVAL, COUNT,
521
528
  * UNTIL. BYDAY / BYMONTH / BYMONTHDAY / BYWEEKNO / BYYEARDAY /
@@ -42,10 +42,11 @@ var retryHelper = require("./retry");
42
42
  * with the framework's `create(opts)` vocabulary.
43
43
  *
44
44
  * The `CIRCUIT_OPEN` error code is a pre-v1 artifact — every other
45
- * framework error class uses namespaced codes (`retry/...`). The
46
- * rename is deferred to v0.10 with a deprecation cycle so existing
47
- * operators who match `err.code === "CIRCUIT_OPEN"` aren't broken
48
- * in a patch.
45
+ * framework error class uses namespaced codes (`retry/...`). It is
46
+ * kept through the pre-1.0 line so existing operators who match
47
+ * `err.code === "CIRCUIT_OPEN"` aren't broken in a patch; the rename
48
+ * to a namespaced code lands at v1.0 alongside the namespaced-error
49
+ * sweep, with a deprecation warning shipping a minor ahead.
49
50
  *
50
51
  * @opts
51
52
  * name: string, // identifier used in audit + state-change events
@@ -5,7 +5,7 @@
5
5
  * Suite (PQC-first per framework crypto policy):
6
6
  * KEM: ML-KEM-1024 (FIPS 203) — post-quantum encapsulation
7
7
  * KDF: HKDF-SHA3-512
8
- * AEAD: ChaCha20-Poly1305 (RFC 7539)
8
+ * AEAD: ChaCha20-Poly1305 (RFC 8439, obsoletes RFC 7539)
9
9
  *
10
10
  * The classical HPKE suites in RFC 9180 §7 (DHKEM with X25519 / P-256 /
11
11
  * P-384 / P-521 + HKDF-SHA256/384/512 + AES-GCM/ChaCha20) are NOT
@@ -30,14 +30,14 @@
30
30
  * <code>suite(name)</code> returns the suite for one of the RFC 9497
31
31
  * ciphersuites — <code>ristretto255-sha512</code> (the Privacy Pass
32
32
  * default), <code>p256-sha256</code>, <code>p384-sha384</code>, or
33
- * <code>p521-sha512</code> — each exposing the three modes. Group and
33
+ * <code>p521-sha512</code> — each exposing both shipped modes. Group and
34
34
  * hash-to-curve operations come from the vendored <code>@noble/curves</code>.
35
35
  * Byte arguments are <code>Uint8Array</code> / <code>Buffer</code>;
36
36
  * returned elements and outputs are <code>Uint8Array</code>.
37
37
  *
38
38
  * @card
39
39
  * RFC 9497 Oblivious PRFs — learn <code>F(key, input)</code> without the
40
- * server seeing the input (oprf / voprf / poprf modes; ristretto255 / P-256
40
+ * server seeing the input (oprf / voprf modes; ristretto255 / P-256
41
41
  * / P-384 / P-521 suites). The primitive behind Privacy Pass, password
42
42
  * hardening, and private set intersection.
43
43
  */
@@ -789,7 +789,7 @@ function toBase64Url(buf) {
789
789
  *
790
790
  * Strict mode (default) refuses non-canonical input — chars outside
791
791
  * the RFC 4648 §5 alphabet, length-mod-4-of-1, mixed `+/` from
792
- * standard base64, trailing garbage. Defends a CVE-2022-0235-class
792
+ * standard base64, trailing garbage. Defends a CVE-2022-0235 class
793
793
  * footgun where Node's permissive decoder silently tolerated
794
794
  * tampered JWT signatures. Operators with a documented lossy legacy
795
795
  * payload opt out per call via `{ strict: false }`.
@@ -817,7 +817,7 @@ function fromBase64Url(s, opts) {
817
817
  // OAuth `state` round-tripping) MUST reject non-canonical / malformed
818
818
  // input. The Node base64url decoder silently tolerates trailing
819
819
  // garbage, mixed `+/` from standard base64, missing padding errors,
820
- // and length-mod-4 shapes — CVE-2022-0235-class footgun. Strict mode
820
+ // and length-mod-4 shapes — CVE-2022-0235 class footgun. Strict mode
821
821
  // (the default) refuses anything outside the RFC 4648 §5 alphabet +
822
822
  // length rules. Operators with a known-lossy legacy payload pass
823
823
  // `{ strict: false }` to opt out per call.
@@ -290,7 +290,8 @@ var GuardTimeError = defineClass("GuardTimeError", { alwaysPermane
290
290
  var GuardMimeError = defineClass("GuardMimeError", { alwaysPermanent: true });
291
291
  // GuardJwtError covers JWT identifier violations: shape malformation
292
292
  // (not 3 base64url segments), alg=none refuse (canonical CVE-class —
293
- // CVE-2015-9235 jsonwebtoken / CVE-2018-0114 java-jwt), alg-allowlist
293
+ // CVE-2015-9235 jsonwebtoken alg:none / CVE-2018-0114 Cisco node-jose
294
+ // embedded-JWK key confusion), alg-allowlist
294
295
  // drift, kid path-traversal (operator keyResolver path-injection
295
296
  // class), typ confusion, oversized header / payload / signature,
296
297
  // exp / nbf / iat sanity, missing required claims, unknown crit
@@ -16,8 +16,9 @@
16
16
  *
17
17
  * Algorithm-confusion defense: `alg=none` is universally refused
18
18
  * at every profile (RFC 7518 §3.6 explicit-no-signature, the
19
- * canonical CVE-2015-9235 jsonwebtoken / CVE-2018-0114 java-jwt
20
- * class). The operator-supplied `allowedAlgs` allowlist defaults
19
+ * canonical CVE-2015-9235 jsonwebtoken alg:none / CVE-2018-0114
20
+ * Cisco node-jose embedded-JWK confusion class). The
21
+ * operator-supplied `allowedAlgs` allowlist defaults
21
22
  * to the framework's PQC-first set (ML-DSA-87 / ML-DSA-65 /
22
23
  * ML-DSA-44 / SLH-DSA-SHAKE-256{f,s} / SLH-DSA-SHA2-256{f,s} /
23
24
  * EdDSA / ES* / RS* / PS*) so HS256-against-RSA-public-key
@@ -493,7 +493,7 @@ function gate(opts) {
493
493
  *
494
494
  * Scan a DATA-body byte buffer for the SMTP smuggling shape per
495
495
  * CVE-2023-51764 (Postfix), CVE-2023-51765 (Sendmail), CVE-2023-51766
496
- * (Exim), CVE-2024-32178 (.NET System.Net.Mail). RFC 5321 §2.3.8
496
+ * (Exim). RFC 5321 §2.3.8
497
497
  * mandates canonical CRLF line termination; the smuggling exploit
498
498
  * relies on parsers that accept `\n.\n` (bare LF before / after the
499
499
  * dot) as an alternate body terminator and then resume parsing the
@@ -517,7 +517,7 @@ function detectBodySmuggling(buf) {
517
517
  throw new GuardSmtpCommandError("guard-smtp-command/bad-input",
518
518
  "detectBodySmuggling: input must be a Buffer");
519
519
  }
520
- // The CVE-2023-51764 / 51765 / 51766 / 2024-32178 class is any
520
+ // The CVE-2023-51764 / 51765 / 51766 class is any
521
521
  // dot-line whose line boundary is anything OTHER than canonical
522
522
  // \r\n on BOTH sides of the dot. The canonical-and-only terminator
523
523
  // is `\r\n.\r\n`. Every other shape that some receiver might honor
@@ -1858,8 +1858,8 @@ function dmarcParseAggregateReport(input, opts) {
1858
1858
  // "stream is malformed" (operator-level diagnostic) so audit/
1859
1859
  // alert wiring can react differently. Node surfaces the bomb
1860
1860
  // case with ERR_BUFFER_TOO_LARGE / "Output length exceeded the
1861
- // limit" / the explicit `maxOutputLength` code. CVE-class:
1862
- // CVE-2024-zlib decompression amplification.
1861
+ // limit" / the explicit `maxOutputLength` code. Defends the
1862
+ // decompression-amplification class (CWE-409 / CVE-2025-0725).
1863
1863
  var msg = (e && e.message) || String(e);
1864
1864
  var isBomb = (e && (e.code === "ERR_BUFFER_TOO_LARGE" ||
1865
1865
  e.code === "ERR_OUT_OF_RANGE")) ||
@@ -663,7 +663,7 @@ function _parseSignaturePacket(packetBytes) {
663
663
  if (hashAlg !== HASH_ALG_SHA256 && hashAlg !== HASH_ALG_SHA512) {
664
664
  throw new MailCryptoError("mail-crypto/pgp/bad-hash",
665
665
  "hash alg " + hashAlg + " refused; only SHA-256 (8) and SHA-512 (10) are accepted. " +
666
- "SHA-1 (id=2) refused per SHAttered (CVE-2017-9006-class).");
666
+ "SHA-1 (id=2) refused per SHAttered (2017 SHA-1 collision).");
667
667
  }
668
668
  var hashedSubLen = body.readUInt16BE(4);
669
669
  if (6 + hashedSubLen > body.length) {
@@ -21,9 +21,9 @@
21
21
  * second part as base64-encoded DER.
22
22
  *
23
23
  * Posture (when the surface lights up):
24
- * - Refuses SHA-1 as the signature hash (CVE-2017-9006-class
25
- * PKCS#7 collision attacks against legacy S/MIME) and as the
26
- * certificate signature algorithm.
24
+ * - Refuses SHA-1 as the signature hash (SHAttered, 2017 — practical
25
+ * SHA-1 collision; RFC 8551 §2.5 mandates SHA-256+ for S/MIME) and
26
+ * as the certificate signature algorithm.
27
27
  * - Refuses RSA keys < 2048 bits (RFC 8301 §3.1 — same posture
28
28
  * as the rest of the mail surface).
29
29
  * - Refuses MD5 anywhere (the historical S/MIME-v2 default; long
@@ -87,8 +87,8 @@
87
87
  * CVE citations:
88
88
  * - CVE-2017-17688 / CVE-2017-17689 (EFAIL — S/MIME variant; informs
89
89
  * the encrypt+decrypt deferral when that surface lights up)
90
- * - CVE-2017-9006 (PKCS#7 / S/MIME signature-validation bypass
91
- * classinforms the SHA-1 refusal posture)
90
+ * - SHAttered (2017 practical SHA-1 collision) + RFC 8551 §2.5 (SHA-256
91
+ * floor for S/MIME) inform the SHA-1 signature-hash refusal posture
92
92
  * - CVE-2018-5407 (PortSmash — informs the side-channel hardening
93
93
  * posture when private operations land in v2)
94
94
  */
@@ -109,7 +109,7 @@ var MailCryptoError = defineClass("MailCryptoError", { alwaysPermanent: true });
109
109
  // hand-copying strings. These reflect RFC 8551 §2.5 + RFC 8301 floors.
110
110
  var RSA_MIN_BITS = 2048; // allow:raw-byte-literal — RFC 8301 §3.1
111
111
  var ALLOWED_HASHES = ["sha256", "sha384", "sha512"];
112
- var REFUSED_HASHES = ["md5", "sha1"]; // allow:raw-byte-literal — CVE-2017-9006-class
112
+ var REFUSED_HASHES = ["md5", "sha1"]; // allow:raw-byte-literal — SHAttered / RFC 8551 §2.5
113
113
 
114
114
  // PROFILES + COMPLIANCE_POSTURES — the framework's standard cross-
115
115
  // primitive contract. sign() and verify() (live since v0.10.16) read
@@ -708,7 +708,7 @@ function checkCert(opts) {
708
708
  throw new MailCryptoError("mail-crypto/smime/refused-hash",
709
709
  "cert signature algorithm '" + sigAlgName +
710
710
  "' refused — SHA-1 / MD5 in cert signatures is forbidden " +
711
- "(CVE-2017-9006-class). Acceptable hashes: " + ALLOWED_HASHES.join(", "));
711
+ "(SHAttered SHA-1 collision; RFC 8551 §2.5). Acceptable hashes: " + ALLOWED_HASHES.join(", "));
712
712
  }
713
713
  }
714
714
 
@@ -62,7 +62,7 @@
62
62
  *
63
63
  * CVE citations:
64
64
  * - CVE-2017-17688 / CVE-2017-17689 (EFAIL)
65
- * - CVE-2017-9006 (PKCS#7 / S/MIME signature-validation bypass class)
65
+ * - SHAttered (2017 SHA-1 collision) + RFC 8551 §2.5 — SHA-1 signature-hash refusal
66
66
  */
67
67
 
68
68
  var pgp = require("./mail-crypto-pgp");
@@ -112,8 +112,9 @@
112
112
  * ## CVE defense composition
113
113
  *
114
114
  * - `b.safeIcal` rejects RRULE COUNT > 10000 / BYxxx list > 24 →
115
- * defends CVE-2024-39687 (ical4j RRULE recursion / Outlook
116
- * calendar bomb) on the PUT path.
115
+ * defends the ical4j RRULE-recursion / recurrence-expansion DoS
116
+ * class (unbounded RRULE expansion exhausts CPU/memory) on the
117
+ * PUT path.
117
118
  * - `b.xmlC14n.parse` rejects DOCTYPE / ENTITY in the
118
119
  * PROPFIND / REPORT body → defends XXE / billion-laughs on the
119
120
  * query path.
@@ -125,7 +126,7 @@
125
126
  * mount under their HTTP router. Composes b.safeIcal / b.safeVcard
126
127
  * for PUT-body validation, b.xmlC14n.parse for PROPFIND / REPORT
127
128
  * bodies. Per-principal URL isolation; operator-supplied storage
128
- * backend. Defends CVE-2024-39687 at the PUT boundary.
129
+ * backend. Defends the RRULE-recursion expansion-DoS class at the PUT boundary.
129
130
  */
130
131
 
131
132
  var lazyRequire = require("./lazy-require");
@@ -749,7 +750,7 @@ function create(opts) {
749
750
  return _refuseStatus(res, 400, "PUT requires a component path");
750
751
  }
751
752
  var bodyBuf = await _readBodyBytes(req);
752
- // Validate iCal body via safeIcal — defends CVE-2024-39687 at the
753
+ // Validate iCal body via safeIcal — defends the RRULE-recursion expansion-DoS class at the
753
754
  // ingest boundary.
754
755
  try {
755
756
  safeIcal.parse(bodyBuf, {
@@ -527,8 +527,9 @@ function autoDiscoverXml(opts) {
527
527
  // brotli or the in-progress UTA-draft requires it.
528
528
 
529
529
  // Hard caps — defensive against CVE-2025-0725 (libcurl/zlib
530
- // integer overflow), CVE-2024-zlib decompression amplification, and
531
- // the §5.2 community ceiling (receivers commonly cap at 10 MiB).
530
+ // integer overflow) and the decompression-amplification class
531
+ // (CWE-409), plus the §5.2 community ceiling (receivers commonly cap
532
+ // at 10 MiB).
532
533
  var TLSRPT_MAX_COMPRESSED_BYTES = C.BYTES.mib(4); // allow:raw-byte-literal — 4 MiB compressed cap per §5.2 community practice
533
534
  var TLSRPT_MAX_DECOMPRESSED_BYTES = C.BYTES.mib(32); // allow:raw-byte-literal — 32 MiB decompressed cap (operators override via opts)
534
535
  var TLSRPT_MAX_RATIO = 50; // allow:raw-byte-literal — 50:1 compression ratio refusal
@@ -53,7 +53,7 @@
53
53
  * pipelined command queued before TLS is refused with
54
54
  * `BAD Pipelined post-STARTTLS not permitted`.
55
55
  *
56
- * - **Literal-injection (CVE-2018-19518 INC IMAP class)** —
56
+ * - **Literal-injection / command-continuation smuggling** —
57
57
  * `{n}` literal continuation MUST come on a line of its own
58
58
  * (per `b.guardImapCommand.detectLiteralSmuggling`); oversize
59
59
  * literals refused (default 64 MiB); LITERAL+ (RFC 7888) non-
@@ -106,7 +106,7 @@
106
106
  *
107
107
  * ## What v1 does NOT ship
108
108
  *
109
- * - **Calendars / Contacts (RFC 9610)**, **Sieve (RFC 9404)**,
109
+ * - **Calendars / Contacts (RFC 9610)**, **Sieve (RFC 9661)**,
110
110
  * **MDN (RFC 9007)** — opt-in capabilities.
111
111
  *
112
112
  * @card
@@ -95,7 +95,7 @@
95
95
  *
96
96
  * - **CHECKSCRIPT** (RFC 5804 §2.12) — parse-only verb. Operators
97
97
  * who want it compose `b.safeSieve.validate` directly via JMAP
98
- * `SieveScript/validate` (RFC 9404). The MTA-side ManageSieve
98
+ * `SieveScript/validate` (RFC 9661). The MTA-side ManageSieve
99
99
  * surface is `PUTSCRIPT` + `HAVESPACE`; CHECKSCRIPT adds a third
100
100
  * entry point with no operator demand yet.
101
101
  * - **UNAUTHENTICATE** (RFC 5804 §2.14) — exotic. Operators close