@blamejs/blamejs-shop 0.1.37 → 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.
- package/CHANGELOG.md +6 -0
- package/lib/admin.js +205 -42
- package/lib/asset-manifest.json +5 -5
- package/lib/storefront.js +428 -51
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +12 -0
- package/lib/vendor/blamejs/README.md +2 -2
- package/lib/vendor/blamejs/SECURITY.md +1 -0
- package/lib/vendor/blamejs/api-snapshot.json +2 -2
- package/lib/vendor/blamejs/lib/ai-disclosure.js +2 -3
- package/lib/vendor/blamejs/lib/archive-gz.js +5 -3
- package/lib/vendor/blamejs/lib/archive-read.js +101 -35
- package/lib/vendor/blamejs/lib/archive-tar-read.js +86 -31
- package/lib/vendor/blamejs/lib/archive-tar.js +2 -3
- package/lib/vendor/blamejs/lib/auth/fal.js +12 -0
- package/lib/vendor/blamejs/lib/auth/jwt-external.js +15 -11
- package/lib/vendor/blamejs/lib/auth/jwt.js +2 -2
- package/lib/vendor/blamejs/lib/auth/oauth.js +7 -6
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +3 -3
- package/lib/vendor/blamejs/lib/auth/saml.js +15 -12
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +3 -3
- package/lib/vendor/blamejs/lib/backup/index.js +2 -3
- package/lib/vendor/blamejs/lib/calendar.js +9 -2
- package/lib/vendor/blamejs/lib/circuit-breaker.js +5 -4
- package/lib/vendor/blamejs/lib/cose.js +4 -3
- package/lib/vendor/blamejs/lib/crypto-hpke.js +1 -1
- package/lib/vendor/blamejs/lib/crypto-oprf.js +2 -2
- package/lib/vendor/blamejs/lib/crypto.js +2 -2
- package/lib/vendor/blamejs/lib/framework-error.js +2 -1
- package/lib/vendor/blamejs/lib/guard-jwt.js +3 -2
- package/lib/vendor/blamejs/lib/guard-smtp-command.js +2 -2
- package/lib/vendor/blamejs/lib/mail-auth.js +2 -2
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +1 -1
- package/lib/vendor/blamejs/lib/mail-crypto-smime.js +7 -7
- package/lib/vendor/blamejs/lib/mail-crypto.js +1 -1
- package/lib/vendor/blamejs/lib/mail-dav.js +5 -4
- package/lib/vendor/blamejs/lib/mail-deploy.js +3 -2
- package/lib/vendor/blamejs/lib/mail-server-imap.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-jmap.js +10 -10
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +1 -1
- package/lib/vendor/blamejs/lib/mail-server-mx.js +142 -47
- package/lib/vendor/blamejs/lib/mail-server-submission.js +6 -5
- package/lib/vendor/blamejs/lib/mail-store.js +2 -2
- package/lib/vendor/blamejs/lib/mail.js +2 -2
- package/lib/vendor/blamejs/lib/mdoc.js +14 -14
- package/lib/vendor/blamejs/lib/network-dnssec.js +10 -8
- package/lib/vendor/blamejs/lib/network-tls.js +10 -7
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -6
- package/lib/vendor/blamejs/lib/safe-ical.js +12 -12
- package/lib/vendor/blamejs/lib/safe-mime.js +6 -6
- package/lib/vendor/blamejs/lib/safe-sieve.js +1 -1
- package/lib/vendor/blamejs/lib/safe-smtp.js +1 -1
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.13.10.json +44 -0
- package/lib/vendor/blamejs/release-notes/v0.13.11.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.13.12.json +36 -0
- package/lib/vendor/blamejs/release-notes/v0.13.7.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.13.8.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.13.9.json +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +82 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +4 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +31 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +188 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +5 -5
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +166 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-cluster.test.js +25 -18
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +12 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler-exactly-once.test.js +15 -9
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket-channels.test.js +0 -6
- package/package.json +1 -1
|
@@ -1008,7 +1008,7 @@ function create(opts) {
|
|
|
1008
1008
|
throw new OAuthError("auth-oauth/no-id-token", "verifyIdToken: idToken must be a string");
|
|
1009
1009
|
}
|
|
1010
1010
|
var parts = idToken.split(".");
|
|
1011
|
-
// CVE-2026-29000 / CVE-2026-22817
|
|
1011
|
+
// CVE-2026-29000 / CVE-2026-22817 — mirror
|
|
1012
1012
|
// jwt-external's 5-segment JWE refusal. A 5-segment compact
|
|
1013
1013
|
// serialization is a JWE (RFC 7516); verifyIdToken is a JWS verifier
|
|
1014
1014
|
// and a JWE shape reaching here is the confused-deputy class an OP
|
|
@@ -1023,7 +1023,7 @@ function create(opts) {
|
|
|
1023
1023
|
}); } catch (_e) { /* drop-silent — observability sink */ }
|
|
1024
1024
|
throw new OAuthError("auth-oauth/jwe-refused",
|
|
1025
1025
|
"5-segment JWE id_token refused — verifyIdToken only handles JWS " +
|
|
1026
|
-
"(CVE-2026-29000 / CVE-2026-
|
|
1026
|
+
"(CVE-2026-29000 / CVE-2026-22817 / CVE-2026-34950 JWE-bypass class)");
|
|
1027
1027
|
}
|
|
1028
1028
|
if (parts.length !== 3) {
|
|
1029
1029
|
throw new OAuthError("auth-oauth/malformed-jwt", "ID token does not have 3 parts");
|
|
@@ -1039,13 +1039,14 @@ function create(opts) {
|
|
|
1039
1039
|
if (!header || typeof header.alg !== "string") {
|
|
1040
1040
|
throw new OAuthError("auth-oauth/malformed-jwt", "ID token header missing 'alg'");
|
|
1041
1041
|
}
|
|
1042
|
-
//
|
|
1042
|
+
// Alg-allowlist gate (CWE-347 / CWE-757) — refuse unknown alg BEFORE
|
|
1043
|
+
// any key resolution.
|
|
1043
1044
|
// The acceptedAlgorithms list is the operator's posture; an alg
|
|
1044
1045
|
// outside it never reaches the JWKS lookup or node:crypto.verify.
|
|
1045
1046
|
if (acceptedAlgorithms.indexOf(header.alg) === -1) {
|
|
1046
1047
|
throw new OAuthError("auth-oauth/alg-not-accepted",
|
|
1047
1048
|
"ID token signed with '" + header.alg + "' which is not in the accepted-algorithm list " +
|
|
1048
|
-
"(
|
|
1049
|
+
"(alg-allowlist gate — refused before key lookup)");
|
|
1049
1050
|
}
|
|
1050
1051
|
// RFC 7515 §4.1.11 — refuse JWS with `crit` header. Every other
|
|
1051
1052
|
// verifier in the framework (jwt.js, jwt-external.js, dpop.js)
|
|
@@ -1344,8 +1345,8 @@ function create(opts) {
|
|
|
1344
1345
|
}
|
|
1345
1346
|
var iss = u.searchParams.get("iss");
|
|
1346
1347
|
var sid = u.searchParams.get("sid");
|
|
1347
|
-
//
|
|
1348
|
-
// present (defends against an attacker-controlled IdP forging a
|
|
1348
|
+
// OpenID Connect Front-Channel Logout 1.0 §3: `iss` MUST match the
|
|
1349
|
+
// configured issuer when present (defends against an attacker-controlled IdP forging a
|
|
1349
1350
|
// logout for a session at a different IdP). `sid` is required
|
|
1350
1351
|
// when the RP registered with frontchannel_logout_session_required=true;
|
|
1351
1352
|
// we surface it either way and let the operator decide.
|
|
@@ -108,14 +108,14 @@ function _verifyProofJwt(proofJwt, expectedAud, expectedCNonce, expectedClientId
|
|
|
108
108
|
throw new AuthError("auth-oid4vci/wrong-proof-typ",
|
|
109
109
|
"credential issuance: proof JWT typ must be \"openid4vci-proof+jwt\" (got \"" + header.typ + "\")");
|
|
110
110
|
}
|
|
111
|
-
//
|
|
112
|
-
// verify-side work. The supportedAlgs list is the issuer's posture;
|
|
111
|
+
// Alg-allowlist gate (CWE-347 / CWE-757) — refuse unknown / unsupported
|
|
112
|
+
// alg BEFORE any verify-side work. The supportedAlgs list is the issuer's posture;
|
|
113
113
|
// refusing here mirrors the discipline in oauth.verifyIdToken /
|
|
114
114
|
// jwt-external.verifyExternal.
|
|
115
115
|
if (!header.alg || supportedAlgs.indexOf(header.alg) === -1) {
|
|
116
116
|
throw new AuthError("auth-oid4vci/unsupported-proof-alg",
|
|
117
117
|
"credential issuance: proof JWT alg \"" + header.alg + "\" not in issuer-supported set " +
|
|
118
|
-
"(
|
|
118
|
+
"(alg-allowlist gate — refused before key lookup)");
|
|
119
119
|
}
|
|
120
120
|
// AUTH-5 / RFC 7515 §4.1.11 — refuse non-empty `crit`. Pre-v0.9.x
|
|
121
121
|
// silently ignored, letting an attacker-controlled wallet declare
|
|
@@ -453,12 +453,14 @@ function create(opts) {
|
|
|
453
453
|
|
|
454
454
|
// XSW defense — refuse duplicate top-level security-critical
|
|
455
455
|
// elements. SAML XML signature wrapping (XSW) attacks shuffle
|
|
456
|
-
// signed elements alongside unsigned siblings;
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
//
|
|
461
|
-
//
|
|
456
|
+
// signed elements alongside unsigned siblings; a first-match
|
|
457
|
+
// child lookup combined with a signed-element-ID check is
|
|
458
|
+
// vulnerable to a multi-Assertion payload where the verifier
|
|
459
|
+
// signs one element but the consumer reads attributes from
|
|
460
|
+
// another. This is the class behind CVE-2024-45409 (ruby-saml,
|
|
461
|
+
// CVSS 10.0, actively exploited) and CVE-2025-25291/25292
|
|
462
|
+
// (omniauth-saml / ruby-saml namespace-confusion XSW). Reject
|
|
463
|
+
// any Response with more than one of these structural children.
|
|
462
464
|
var statusChildren = _findAllChildren(root, "Status", SAML_NS.protocol);
|
|
463
465
|
if (statusChildren.length > 1) {
|
|
464
466
|
throw new AuthError("auth-saml/duplicate-status",
|
|
@@ -1531,9 +1533,10 @@ function create(opts) {
|
|
|
1531
1533
|
// http://www.w3.org/2009/xmlenc11#rsa-oaep (XMLEnc 1.1 §5.4.2)
|
|
1532
1534
|
//
|
|
1533
1535
|
// AES-CBC content encryption (xmlenc#aes128-cbc / aes256-cbc) is
|
|
1534
|
-
// intentionally REFUSED:
|
|
1535
|
-
//
|
|
1536
|
-
// CBC mode under XMLEnc is exploitable without per-
|
|
1536
|
+
// intentionally REFUSED: the XML-Encryption padding-oracle research
|
|
1537
|
+
// (Jager & Somorovsky, "How to Break XML Encryption", CCS 2011)
|
|
1538
|
+
// demonstrates that CBC mode under XMLEnc is exploitable without per-
|
|
1539
|
+
// content MAC.
|
|
1537
1540
|
// Operators integrating with IdPs that default to CBC (older ADFS /
|
|
1538
1541
|
// Azure AD / Okta / Keycloak / OneLogin) MUST switch the IdP's
|
|
1539
1542
|
// content-encryption setting to AES-128-GCM or AES-256-GCM. The
|
|
@@ -1543,7 +1546,7 @@ function create(opts) {
|
|
|
1543
1546
|
//
|
|
1544
1547
|
// SHA-1 anywhere (rsa-oaep-mgf1p with SHA-1 OAEP DigestMethod,
|
|
1545
1548
|
// xmldsig#sha1 DigestMethod) is also refused — Bleichenbacher /
|
|
1546
|
-
// collision risk plus CVE-2023-49141
|
|
1549
|
+
// collision risk plus CVE-2023-49141 class advisories outweigh
|
|
1547
1550
|
// "interop with stale IdPs". Operators upgrade the IdP's digest
|
|
1548
1551
|
// algorithm to SHA-256+ rather than relax the framework defense.
|
|
1549
1552
|
//
|
|
@@ -1606,7 +1609,7 @@ function _decryptEncryptedAssertion(encAssertion, spPrivateKeyPem) {
|
|
|
1606
1609
|
}
|
|
1607
1610
|
if (oaepHashName === "sha1") {
|
|
1608
1611
|
throw new AuthError("auth-saml/encrypted-weak-oaep-digest",
|
|
1609
|
-
"EncryptedKey OAEP DigestMethod is SHA-1 — refused (CVE-2023-49141
|
|
1612
|
+
"EncryptedKey OAEP DigestMethod is SHA-1 — refused (CVE-2023-49141 class). " +
|
|
1610
1613
|
"Require SHA-256+ on IdP side.");
|
|
1611
1614
|
}
|
|
1612
1615
|
var spKey;
|
|
@@ -1710,7 +1713,7 @@ function _decryptEncryptedAssertion(encAssertion, spPrivateKeyPem) {
|
|
|
1710
1713
|
"(supported: W3C xmlenc11#aes128-gcm, xmlenc11#aes256-gcm, " +
|
|
1711
1714
|
"framework-experimental urn:blamejs:experimental:xmlenc:xchacha20-poly1305). " +
|
|
1712
1715
|
"AES-CBC content encryption is refused — switch the IdP to AES-128-GCM or AES-256-GCM " +
|
|
1713
|
-
"(
|
|
1716
|
+
"(XMLEnc CBC padding-oracle class, Jager & Somorovsky CCS 2011).");
|
|
1714
1717
|
}
|
|
1715
1718
|
return clearBytes.toString("utf8");
|
|
1716
1719
|
}
|
|
@@ -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
|
-
//
|
|
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
|
-
"(
|
|
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-
|
|
@@ -1028,9 +1028,8 @@ module.exports = {
|
|
|
1028
1028
|
* Adapter-driven storage backend. Wraps the bundle directory's file
|
|
1029
1029
|
* tree into per-file key-value pairs routed through an operator-
|
|
1030
1030
|
* supplied byte-store adapter so backup bundles can land anywhere
|
|
1031
|
-
* that exposes the contract
|
|
1032
|
-
* folding
|
|
1033
|
-
* objectStore v0.12.11).
|
|
1031
|
+
* that exposes the contract: local fs (the default), tar / tar.gz
|
|
1032
|
+
* folding, and S3 / MinIO / Azure / GCS objectStore adapters.
|
|
1034
1033
|
*
|
|
1035
1034
|
* The adapter contract (small surface; an `fs` implementation is the
|
|
1036
1035
|
* default + ships in `lib/backup/_adapter-fs.js`):
|
|
@@ -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
|
-
|
|
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
|
|
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/...`).
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
* 2) listing a header label the verifier does not understand is
|
|
42
42
|
* refused (RFC 9052 §3.1) — a crit-bypass defense.
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* Ships COSE_Sign1 (single-signer, attached payload), COSE_Mac0, and
|
|
45
|
+
* COSE_Encrypt0 (single-recipient AEAD). Detached payload, COSE_Sign
|
|
46
|
+
* (multi-signer), and COSE_Encrypt (multi-recipient) are
|
|
47
|
+
* deferred-with-condition (operator demand).
|
|
47
48
|
*
|
|
48
49
|
* @card
|
|
49
50
|
* COSE_Sign1 sign / verify (RFC 9052) over the in-tree CBOR codec —
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
20
|
-
*
|
|
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)
|
|
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
|
|
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.
|
|
1862
|
-
//
|
|
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 (
|
|
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 (
|
|
25
|
-
*
|
|
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
|
-
* -
|
|
91
|
-
*
|
|
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 —
|
|
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
|
-
"(
|
|
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
|
-
* -
|
|
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
|
|
116
|
-
*
|
|
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
|
|
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
|
|
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)
|
|
531
|
-
// the §5.2 community ceiling (receivers commonly cap
|
|
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
|
|
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-
|
|
@@ -95,18 +95,18 @@
|
|
|
95
95
|
* - `urn:ietf:params:jmap:error:accountNotFound`
|
|
96
96
|
* - `urn:ietf:params:jmap:error:serverFail` (opaque last-resort)
|
|
97
97
|
*
|
|
98
|
+
* ## Beyond Core + Mail, this also ships
|
|
99
|
+
*
|
|
100
|
+
* - **Push channel (RFC 8887)** — `eventSourceHandler` (SSE) and
|
|
101
|
+
* `webSocketHandler` (WebSocket, with `StateChange` push).
|
|
102
|
+
* - **Blob upload/download (RFC 8620 §6)** — `uploadHandler` /
|
|
103
|
+
* `downloadHandler`, routing uploads through the guard-* family.
|
|
104
|
+
* - **EmailSubmission/set (RFC 8621 §7.5)** — `emailSubmissionSetHandler`,
|
|
105
|
+
* composing `b.mail.send.deliver`.
|
|
106
|
+
*
|
|
98
107
|
* ## What v1 does NOT ship
|
|
99
108
|
*
|
|
100
|
-
* - **
|
|
101
|
-
* `b.sse` or `b.websocket` to the `pushSubscribe` hook. v1.5
|
|
102
|
-
* bundles a turnkey push handler.
|
|
103
|
-
* - **Blob upload/download endpoints** — operator wires their own
|
|
104
|
-
* `/jmap/upload` / `/jmap/download` handlers; the framework
|
|
105
|
-
* supplies `b.storage` + `b.objectStore` + the guard-* family
|
|
106
|
-
* for the actual upload path.
|
|
107
|
-
* - **EmailSubmission (RFC 8621 §7)** — operator wires the bridge
|
|
108
|
-
* to `b.mail.server.submission`'s outbound agent.
|
|
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
|
|
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
|