@blamejs/core 0.18.55 → 0.18.57
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 +58 -0
- package/NOTICE +4 -4
- package/README.md +6 -6
- package/lib/audit.js +26 -24
- package/lib/chain-writer.js +17 -0
- package/lib/constants.js +33 -0
- package/lib/mail-server-managesieve.js +15 -5
- package/lib/mail-server-mx.js +201 -9
- package/lib/mail-server-tls.js +32 -9
- package/lib/middleware/csrf-protect.js +37 -20
- package/lib/network-smtp-policy.js +18 -1
- package/lib/network-tls.js +79 -0
- package/lib/session-stores.js +6 -3
- package/lib/vendor/MANIFEST.json +30 -30
- package/lib/vendor/browser/noble-ciphers.mjs +15 -1
- package/lib/vendor/browser/noble-hashes.mjs +12 -4
- package/lib/vendor/browser/noble-post-quantum.mjs +78 -37
- package/lib/vendor/noble-ciphers.cjs +15 -1
- package/lib/vendor/noble-curves.cjs +46 -21
- package/lib/vendor/noble-post-quantum.cjs +184 -75
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/mail-server-tls.js
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
* });
|
|
25
25
|
*
|
|
26
26
|
* var mx = b.mail.server.mx.create({
|
|
27
|
-
*
|
|
27
|
+
* // A getter, not `tlsCtx.secureContext` — see "Picking up a rotation".
|
|
28
|
+
* get tlsContext() { return tlsCtx.secureContext; },
|
|
28
29
|
* ...
|
|
29
30
|
* });
|
|
30
31
|
* ```
|
|
@@ -75,10 +76,20 @@
|
|
|
75
76
|
*
|
|
76
77
|
* // Once per process at boot:
|
|
77
78
|
* var tls = b.mail.server.tls.context({ certFile, keyFile, watch: true });
|
|
78
|
-
* var mx = b.mail.server.mx.create({
|
|
79
|
-
*
|
|
79
|
+
* var mx = b.mail.server.mx.create({
|
|
80
|
+
* get tlsContext() { return tls.secureContext; },
|
|
81
|
+
* ...
|
|
82
|
+
* });
|
|
80
83
|
* ```
|
|
81
84
|
*
|
|
85
|
+
* **Picking up a rotation.** Every listener reads `opts.tlsContext` at the
|
|
86
|
+
* point it needs a context rather than capturing it at construction, so a
|
|
87
|
+
* getter is all a rotation needs: the next connection sees the reloaded
|
|
88
|
+
* context and nothing has to be swapped or restarted. Passing
|
|
89
|
+
* `tlsContext: tls.secureContext` instead copies the context that happened
|
|
90
|
+
* to be current at boot, and that one keeps being served after it expires.
|
|
91
|
+
* There is no listener method to swap a context, and none is needed.
|
|
92
|
+
*
|
|
82
93
|
* The cleartext-refused error message from `b.mail.server.mx` /
|
|
83
94
|
* `b.mail.server.submission` points at this primitive so the
|
|
84
95
|
* operator's boot dead-end becomes a one-line fix.
|
|
@@ -137,12 +148,16 @@ var DEFAULT_POLL_MS = C.TIME.seconds(30);
|
|
|
137
148
|
* keyFile: "/etc/letsencrypt/live/mail.example.com/privkey.pem",
|
|
138
149
|
* watch: true,
|
|
139
150
|
* });
|
|
140
|
-
* //
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
151
|
+
* // Pass a getter so each connection reads the current context. Listeners
|
|
152
|
+
* // read opts.tlsContext at the point of use, so a reload reaches the next
|
|
153
|
+
* // connection with nothing to swap.
|
|
154
|
+
* var mx = b.mail.server.mx.create({
|
|
155
|
+
* get tlsContext() { return tls.secureContext; },
|
|
156
|
+
* localDomains: ["mail.example.com"]
|
|
145
157
|
* });
|
|
158
|
+
* // onReload is for observing a rotation — logging it, re-checking expiry.
|
|
159
|
+
* // Delivering it to the listeners is not something it has to do.
|
|
160
|
+
* tls.onReload(function (newCtx) { void newCtx; });
|
|
146
161
|
*
|
|
147
162
|
* // ... later, on shutdown:
|
|
148
163
|
* tls.stop(); // clears the poll timer
|
|
@@ -154,6 +169,14 @@ var DEFAULT_POLL_MS = C.TIME.seconds(30);
|
|
|
154
169
|
// way to answer the question except by re-deriving the options itself — and a
|
|
155
170
|
// re-derivation agrees with the build right up until someone changes one.
|
|
156
171
|
//
|
|
172
|
+
// The SERVER form, `b.network.tls.serverKeyAgreementGroups`, not the outbound
|
|
173
|
+
// one. A flat colon list is an offer order to a client and an accept SET to a
|
|
174
|
+
// server, so the client list applied here refused every group it did not name
|
|
175
|
+
// — secp256r1 among them, which RFC 8446 §9.1 makes mandatory to implement —
|
|
176
|
+
// and collapsed the HelloRetryRequest ordering that pulls a peer up to a
|
|
177
|
+
// hybrid. The server form is tuples, which accepts every registered ECDHE
|
|
178
|
+
// curve while still preferring a hybrid.
|
|
179
|
+
//
|
|
157
180
|
// The key-agreement preference comes from `b.network.tls.keyAgreementGroups`,
|
|
158
181
|
// which is where the framework's PQC-first policy lives — the ML-KEM hybrids
|
|
159
182
|
// with a classical X25519 fallback — along with the reasoning about which key
|
|
@@ -176,7 +199,7 @@ var DEFAULT_POLL_MS = C.TIME.seconds(30);
|
|
|
176
199
|
// verification posture.
|
|
177
200
|
function _contextOptions(sourceOpts, certPem, keyPem) {
|
|
178
201
|
var base = { cert: certPem, key: keyPem };
|
|
179
|
-
var groups = networkTls().
|
|
202
|
+
var groups = networkTls().serverKeyAgreementGroups(
|
|
180
203
|
sourceOpts ? sourceOpts.ecdhCurve : undefined,
|
|
181
204
|
"b.mail.server.tls.context: opts.ecdhCurve");
|
|
182
205
|
if (groups) base.ecdhCurve = groups;
|
|
@@ -253,7 +253,7 @@ function _writeReject(req, res, message, reason, onDeny, problemMode) {
|
|
|
253
253
|
* protocolResolver: function(req): "http"|"https", // own the HTTPS decision
|
|
254
254
|
* trustProxy: boolean|number, // legacy; refused unless paired with trustedProxies/protocolResolver (spoofable)
|
|
255
255
|
* audit: boolean,
|
|
256
|
-
* skipStateless: boolean, // default false — skip
|
|
256
|
+
* skipStateless: boolean, // default false — skip the token check for cookieless (not-CSRF-able) requests. Turns on the absence of the ambient credential only: an Authorization header is not part of the test, and it never waives `checkOrigin`.
|
|
257
257
|
* onDeny: function(req, res, info): void, // own the 403; info = { status, reason }
|
|
258
258
|
* problemDetails: boolean, // default false — emit RFC 9457 application/problem+json instead of the default JSON envelope
|
|
259
259
|
* }
|
|
@@ -363,17 +363,21 @@ function create(opts) {
|
|
|
363
363
|
// is opt-in rather than silent.
|
|
364
364
|
var requireOriginOpt = opts.requireOrigin === true;
|
|
365
365
|
|
|
366
|
-
// skipStateless — skip token VALIDATION for requests that carry
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
366
|
+
// skipStateless — skip token VALIDATION for requests that carry no Cookie
|
|
367
|
+
// header at all. Such requests are not CSRF-able: CSRF abuses a victim's
|
|
368
|
+
// ambient cookie credential, and a request that sends none has nothing to
|
|
369
|
+
// abuse. The token is still ISSUED on safe methods so a later
|
|
370
|
+
// cookie-authenticated browser flow on the same app works. Default false
|
|
371
|
+
// (strict — every state-changing request is validated). createApp wires its
|
|
372
|
+
// default csrf with this on so mixed browser-form + token-API surfaces don't
|
|
373
|
+
// reject legitimate API clients. Cross-site form CSRF is unaffected: the
|
|
374
|
+
// browser auto-sends the victim's cookies, so the attack request always
|
|
375
|
+
// carries a Cookie header and is validated.
|
|
376
|
+
//
|
|
377
|
+
// An `Authorization` header is NOT part of the test, and a bearer client that
|
|
378
|
+
// also sends an unrelated cookie is validated like anything else. Deciding
|
|
379
|
+
// otherwise needs the auth layer's verdict about which credential
|
|
380
|
+
// authenticated the request, which header presence cannot supply.
|
|
377
381
|
var skipStateless = opts.skipStateless === true;
|
|
378
382
|
|
|
379
383
|
// Per-path exemption (string-prefix / RegExp / skip predicate), validated at
|
|
@@ -548,14 +552,6 @@ function create(opts) {
|
|
|
548
552
|
|
|
549
553
|
if (methods.indexOf(req.method) === -1) return next();
|
|
550
554
|
|
|
551
|
-
// Stateless / token-authenticated requests are not CSRF-able — the
|
|
552
|
-
// token was still issued above for any later browser flow.
|
|
553
|
-
if (skipStateless) {
|
|
554
|
-
var hasAuthHeader = !!(req.headers && req.headers.authorization);
|
|
555
|
-
var hasCookieHeader = !!(req.headers && req.headers.cookie);
|
|
556
|
-
if (hasAuthHeader || !hasCookieHeader) return next();
|
|
557
|
-
}
|
|
558
|
-
|
|
559
555
|
// requireJsonContentType — refuse before the token check.
|
|
560
556
|
if (requireJsonCt) {
|
|
561
557
|
var ct = req.headers && req.headers["content-type"];
|
|
@@ -578,6 +574,27 @@ function create(opts) {
|
|
|
578
574
|
}
|
|
579
575
|
}
|
|
580
576
|
|
|
577
|
+
// Stateless requests are not CSRF-able — the token was still issued above
|
|
578
|
+
// for any later browser flow.
|
|
579
|
+
//
|
|
580
|
+
// The test is the ABSENCE of the ambient credential, and nothing else. CSRF
|
|
581
|
+
// spends a cookie the browser attaches on its own; a request carrying none
|
|
582
|
+
// has nothing to abuse. This used to fire on an `Authorization` header too,
|
|
583
|
+
// which was two mistakes at once. Presence is not authenticity — an
|
|
584
|
+
// attacker composing a cross-site request writes their own headers, so
|
|
585
|
+
// `Authorization: Bearer nonsense` satisfied it by being typed. And the
|
|
586
|
+
// header says nothing about which credential authenticated the request:
|
|
587
|
+
// attachUser with tokenFrom: "both" reads the cookie FIRST, so a request
|
|
588
|
+
// carrying both was authenticated by exactly the ambient credential this
|
|
589
|
+
// gate protects, and skipped the gate on a header nobody had read.
|
|
590
|
+
//
|
|
591
|
+
// It also sits BELOW the origin cross-check now rather than above it. A
|
|
592
|
+
// consumer that asked for checkOrigin asked for something the token compare
|
|
593
|
+
// does not give them, and there is no reading of "stateless" under which a
|
|
594
|
+
// cross-origin state change becomes acceptable — the branch that waived the
|
|
595
|
+
// first line of defence was waiving the second one with it.
|
|
596
|
+
if (skipStateless && !(req.headers && req.headers.cookie)) return next();
|
|
597
|
+
|
|
581
598
|
if (!cookieCfg) {
|
|
582
599
|
// Session-stored mode — operator's tokenLookup is the source.
|
|
583
600
|
expected = opts.tokenLookup(req);
|
|
@@ -716,7 +716,21 @@ async function tlsRptSubmit(report, opts) {
|
|
|
716
716
|
} else if (/^mailto:/i.test(uri)) {
|
|
717
717
|
// Operator-side transport. Surface the prepared body so the
|
|
718
718
|
// operator can hand it to b.mail directly.
|
|
719
|
-
var
|
|
719
|
+
var publishedTarget = uri.slice("mailto:".length);
|
|
720
|
+
// The address is published by the RECEIVING domain in its own
|
|
721
|
+
// `_smtp._tls` record. It is not operator input to this sender, and a
|
|
722
|
+
// peer spelling its hostname absolutely is using the form RFC 1035 §3.1
|
|
723
|
+
// defines — a zone file writes it that way routinely.
|
|
724
|
+
//
|
|
725
|
+
// So fold one trailing dot before validating rather than validating the
|
|
726
|
+
// spelling as published. Strictness normally protects the party doing
|
|
727
|
+
// the refusing; here it costs the OTHER party the report they asked for
|
|
728
|
+
// under RFC 8460 and costs this sender nothing — the entry silently
|
|
729
|
+
// never becomes a report. That is the direction that makes the lenient
|
|
730
|
+
// reading correct. The fold cannot turn a valid address into a
|
|
731
|
+
// different valid one, because a trailing dot is not legal in an
|
|
732
|
+
// addr-spec domain, so a name carrying one has exactly one reading.
|
|
733
|
+
var mailtoTarget = publishedTarget.replace(/\.$/, "");
|
|
720
734
|
// RFC 5322 §3.4.1 addr-spec validation — refuse mailto: rua
|
|
721
735
|
// entries that aren't valid addresses. Pre-v0.8.32 the
|
|
722
736
|
// framework would forward whatever string came after
|
|
@@ -730,6 +744,9 @@ async function tlsRptSubmit(report, opts) {
|
|
|
730
744
|
entry.ok = true;
|
|
731
745
|
entry.mailto = {
|
|
732
746
|
to: mailtoTarget,
|
|
747
|
+
// What the peer actually published, kept beside what was used, so
|
|
748
|
+
// an operator reading the result can see the two are different.
|
|
749
|
+
published: publishedTarget,
|
|
733
750
|
subject: "Report Domain: " + (report["organization-name"] || "") +
|
|
734
751
|
" Submitter: " + (report["organization-name"] || "") +
|
|
735
752
|
" Report-ID: <" + (report["report-id"] || "") + ">",
|
package/lib/network-tls.js
CHANGED
|
@@ -80,6 +80,10 @@ var STATE = {
|
|
|
80
80
|
systemTrust: false,
|
|
81
81
|
baselineFingerprints: null,
|
|
82
82
|
tlsKeyShares: ["X25519MLKEM768", "SecP256r1MLKEM768", "SecP384r1MLKEM1024", "X25519"],
|
|
83
|
+
// False until pqc.setKeyShares is called. The list above is the shipped
|
|
84
|
+
// default, and a listener widens THAT for interoperability; a list the
|
|
85
|
+
// operator chose is taken as written.
|
|
86
|
+
tlsKeySharesConfigured: false,
|
|
83
87
|
};
|
|
84
88
|
|
|
85
89
|
function _normalizePem(pem) {
|
|
@@ -566,6 +570,68 @@ function keyAgreementGroups(override, where) {
|
|
|
566
570
|
return STATE.tlsKeyShares.length > 0 ? STATE.tlsKeyShares.join(":") : null;
|
|
567
571
|
}
|
|
568
572
|
|
|
573
|
+
// The same policy in the shape a LISTENER needs.
|
|
574
|
+
//
|
|
575
|
+
// keyAgreementGroups above returns a flat colon-separated list, which is an
|
|
576
|
+
// offer order on a client and an accept SET on a server — OpenSSL 3.5 reads
|
|
577
|
+
// only `/`-separated tuples as an order. Handing a listener the client form
|
|
578
|
+
// made it refuse every group the list did not name, secp256r1 included, and
|
|
579
|
+
// collapsed the retry ordering that pulls a peer up to a hybrid. So a server
|
|
580
|
+
// takes this instead, and the difference is not a detail a call site should
|
|
581
|
+
// have to remember.
|
|
582
|
+
//
|
|
583
|
+
// An override is honoured verbatim and a malformed one refused, exactly as on
|
|
584
|
+
// the client side: an operator who names their own groups gets them, and gets
|
|
585
|
+
// told when what they named cannot be parsed rather than being silently given
|
|
586
|
+
// the default.
|
|
587
|
+
// The PREFERENCE half is whatever is active — the same STATE.tlsKeyShares the
|
|
588
|
+
// client form reads, so an operator who called pqc.setKeyShares to opt out of
|
|
589
|
+
// hybrids gets listeners that do not prefer them either. Hardcoding the default
|
|
590
|
+
// here would have let a configured policy apply outbound and be silently
|
|
591
|
+
// ignored inbound, which is worse than not having the helper.
|
|
592
|
+
//
|
|
593
|
+
// The fallback tuple only ADDS acceptance; it never changes what is preferred,
|
|
594
|
+
// and anything already in the preference is not repeated into it.
|
|
595
|
+
function serverKeyAgreementGroups(override, where) {
|
|
596
|
+
if (override !== undefined && override !== null) {
|
|
597
|
+
return _groupPreferenceString(override, where || "tls.serverKeyAgreementGroups");
|
|
598
|
+
}
|
|
599
|
+
var preferred = STATE.tlsKeyShares.length > 0
|
|
600
|
+
? STATE.tlsKeyShares.slice()
|
|
601
|
+
: C.TLS_GROUP_PREFERENCE.slice();
|
|
602
|
+
if (preferred.length === 0) return null;
|
|
603
|
+
|
|
604
|
+
// EVERY configured rank is its own tuple, in order.
|
|
605
|
+
//
|
|
606
|
+
// Ordering exists only BETWEEN tuples: groups sharing one are a set the
|
|
607
|
+
// server takes in whatever order the handshake reaches them. So any two
|
|
608
|
+
// preferences packed into a single tuple stop being ranked — a hybrid beside
|
|
609
|
+
// X25519 loses to it whenever a peer offers both, and `["secp384r1",
|
|
610
|
+
// "X25519"]` silently negotiates X25519. One tuple per entry is the only
|
|
611
|
+
// shape that means what the configured list says.
|
|
612
|
+
//
|
|
613
|
+
// The acceptance-only curves come last, as a single tuple: they are there so
|
|
614
|
+
// a conforming peer is never refused, and carry no preference between
|
|
615
|
+
// themselves.
|
|
616
|
+
//
|
|
617
|
+
// They are added ONLY to the shipped default. An operator who called
|
|
618
|
+
// setKeyShares narrowed the policy deliberately — the module's own FIPS
|
|
619
|
+
// example drops the X25519-based groups — and adding those back would let a
|
|
620
|
+
// peer negotiate exactly what they excluded, with the restriction holding
|
|
621
|
+
// outbound and silently not holding inbound. A narrowed listener may refuse
|
|
622
|
+
// a conforming peer; that is the operator's choice to make, and it is
|
|
623
|
+
// visible, where the reverse would not be.
|
|
624
|
+
var named = preferred.map(function (g) { return String(g).toLowerCase(); });
|
|
625
|
+
var fallback = STATE.tlsKeySharesConfigured ? [] :
|
|
626
|
+
C.TLS_SERVER_FALLBACK_CURVES.filter(function (g) {
|
|
627
|
+
return named.indexOf(String(g).toLowerCase()) === -1;
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
var tuples = preferred.slice();
|
|
631
|
+
if (fallback.length > 0) tuples.push(fallback.join(":"));
|
|
632
|
+
return tuples.join("/");
|
|
633
|
+
}
|
|
634
|
+
|
|
569
635
|
function applyToContext(opts) {
|
|
570
636
|
opts = opts || {};
|
|
571
637
|
validateOpts(opts, ["base"], "tls.applyToContext");
|
|
@@ -699,6 +765,12 @@ function setKeyShares(list) {
|
|
|
699
765
|
}
|
|
700
766
|
for (var i = 0; i < list.length; i += 1) _validateKeyShare(list[i]);
|
|
701
767
|
STATE.tlsKeyShares = list.slice();
|
|
768
|
+
// Records that this list is a CHOICE rather than the shipped default. A
|
|
769
|
+
// listener adds interoperability curves to the default so it does not refuse
|
|
770
|
+
// a conforming peer out of the box; it must not add them to a list the
|
|
771
|
+
// operator narrowed on purpose, or a restriction would hold outbound and be
|
|
772
|
+
// silently undone inbound.
|
|
773
|
+
STATE.tlsKeySharesConfigured = true;
|
|
702
774
|
_postureGeneration += 1;
|
|
703
775
|
return getKeyShares();
|
|
704
776
|
}
|
|
@@ -707,6 +779,11 @@ function getKeyShares() { return STATE.tlsKeyShares.slice(); }
|
|
|
707
779
|
|
|
708
780
|
function resetKeyShares() {
|
|
709
781
|
STATE.tlsKeyShares = DEFAULT_PQC_KEY_SHARES.slice();
|
|
782
|
+
// Back to the shipped default in every respect, including whether a listener
|
|
783
|
+
// widens it for interoperability. Restoring the list but keeping the
|
|
784
|
+
// configured flag would leave a reset process serving a different group
|
|
785
|
+
// policy from a fresh one, which is not what a reset means.
|
|
786
|
+
STATE.tlsKeySharesConfigured = false;
|
|
710
787
|
_postureGeneration += 1;
|
|
711
788
|
return getKeyShares();
|
|
712
789
|
}
|
|
@@ -1251,6 +1328,7 @@ function _resetForTest() {
|
|
|
1251
1328
|
STATE.systemTrust = false;
|
|
1252
1329
|
STATE.baselineFingerprints = null;
|
|
1253
1330
|
STATE.tlsKeyShares = DEFAULT_PQC_KEY_SHARES.slice();
|
|
1331
|
+
STATE.tlsKeySharesConfigured = false;
|
|
1254
1332
|
}
|
|
1255
1333
|
|
|
1256
1334
|
// ---- OCSP / OCSP-stapling wrappers around node:tls ----------------
|
|
@@ -4156,6 +4234,7 @@ module.exports = {
|
|
|
4156
4234
|
detectBaselineDrift: detectBaselineDrift,
|
|
4157
4235
|
applyToContext: applyToContext,
|
|
4158
4236
|
keyAgreementGroups: keyAgreementGroups,
|
|
4237
|
+
serverKeyAgreementGroups: serverKeyAgreementGroups,
|
|
4159
4238
|
buildOptions: buildOptions,
|
|
4160
4239
|
getCaPems: getCaPems,
|
|
4161
4240
|
ocsp: ocsp,
|
package/lib/session-stores.js
CHANGED
|
@@ -29,9 +29,12 @@
|
|
|
29
29
|
* schema (sidHash PRIMARY KEY, userId, userIdHash, data, createdAt,
|
|
30
30
|
* expiresAt, lastActivity) plus the indexes session-side queries
|
|
31
31
|
* need (userIdHash for `destroyAllForUser`, expiresAt for
|
|
32
|
-
* `purgeExpired`). Operators typically point `file` at tmpfs
|
|
33
|
-
* `/dev/shm/blamejs-sessions.db`
|
|
34
|
-
* and don't compete with
|
|
32
|
+
* `purgeExpired`). Operators typically point `file` at tmpfs
|
|
33
|
+
* (`/dev/shm/blamejs-sessions.db` on Linux, an in-memory volume on
|
|
34
|
+
* Windows) so session inserts run RAM-fast and don't compete with
|
|
35
|
+
* the main DB's encryption-flush cycle. The path is yours to name —
|
|
36
|
+
* a leading slash is drive-relative on Windows, where `/dev/shm`
|
|
37
|
+
* means `C:\dev\shm` and is ordinary disk.
|
|
35
38
|
*
|
|
36
39
|
* Wire it once at boot, before the first session call:
|
|
37
40
|
*
|
package/lib/vendor/MANIFEST.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"_comment": "Vendored dependencies — no npm runtime packages. Use scripts/vendor-update.sh to update.",
|
|
3
3
|
"packages": {
|
|
4
4
|
"@noble/ciphers": {
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.4.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Paul Miller",
|
|
8
8
|
"source": "https://github.com/paulmillr/noble-ciphers",
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
"browser": "lib/vendor/browser/noble-ciphers.mjs"
|
|
16
16
|
},
|
|
17
17
|
"bundler": "esbuild --format=cjs --platform=node (server), esbuild --format=esm --platform=browser (browser)",
|
|
18
|
-
"bundledAt": "2026-08-
|
|
19
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.
|
|
18
|
+
"bundledAt": "2026-08-27T00:00:00Z",
|
|
19
|
+
"cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.4.0:*:*:*:*:node.js:*:*",
|
|
20
20
|
"hashes": {
|
|
21
|
-
"server": "sha256:
|
|
22
|
-
"browser": "sha256:
|
|
21
|
+
"server": "sha256:acdcf1ba4bde8177cbfcfef22b543022284a1529afb68bc8329303837c8c021e",
|
|
22
|
+
"browser": "sha256:fc422d85a65c5dc1208066b0156e100c4790be3c8791f644b80524c25d4bc3ee"
|
|
23
23
|
},
|
|
24
|
-
"refreshedAt": "2026-08-
|
|
24
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
25
25
|
},
|
|
26
26
|
"@noble/hashes": {
|
|
27
|
-
"version": "2.
|
|
27
|
+
"version": "2.4.0",
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"author": "Paul Miller",
|
|
30
30
|
"source": "https://github.com/paulmillr/noble-hashes",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"browser": "lib/vendor/browser/noble-hashes.mjs"
|
|
44
44
|
},
|
|
45
45
|
"bundler": "esbuild --format=esm --platform=browser (browser only)",
|
|
46
|
-
"bundledAt": "2026-08-
|
|
47
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-hashes:2.
|
|
46
|
+
"bundledAt": "2026-08-27T00:00:00Z",
|
|
47
|
+
"cpe": "cpe:2.3:a:paulmillr:noble-hashes:2.4.0:*:*:*:*:node.js:*:*",
|
|
48
48
|
"hashes": {
|
|
49
|
-
"browser": "sha256:
|
|
49
|
+
"browser": "sha256:0e135cbb496a1b7d15bf36b8c611a8feaad49ced60a03bf916bada78b589aa66"
|
|
50
50
|
},
|
|
51
|
-
"refreshedAt": "2026-08-
|
|
51
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
52
52
|
},
|
|
53
53
|
"@noble/curves": {
|
|
54
|
-
"version": "2.
|
|
54
|
+
"version": "2.4.0",
|
|
55
55
|
"license": "MIT",
|
|
56
56
|
"author": "Paul Miller",
|
|
57
57
|
"source": "https://github.com/paulmillr/noble-curves",
|
|
@@ -65,21 +65,21 @@
|
|
|
65
65
|
"server": "lib/vendor/noble-curves.cjs"
|
|
66
66
|
},
|
|
67
67
|
"bundler": "esbuild --format=cjs --platform=node",
|
|
68
|
-
"bundledAt": "2026-08-
|
|
69
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-curves:2.
|
|
68
|
+
"bundledAt": "2026-08-27T00:00:00Z",
|
|
69
|
+
"cpe": "cpe:2.3:a:paulmillr:noble-curves:2.4.0:*:*:*:*:node.js:*:*",
|
|
70
70
|
"hashes": {
|
|
71
|
-
"server": "sha256:
|
|
71
|
+
"server": "sha256:477359f445eed241ad4ededb84921483c3c97618a160ef78b74bb1a421c17da7"
|
|
72
72
|
},
|
|
73
|
-
"refreshedAt": "2026-08-
|
|
73
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z",
|
|
74
74
|
"components": {
|
|
75
75
|
"@noble/hashes": {
|
|
76
76
|
"url": "https://github.com/paulmillr/noble-hashes",
|
|
77
|
-
"version": "2.
|
|
77
|
+
"version": "2.4.0"
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
"@noble/post-quantum": {
|
|
82
|
-
"version": "0.7.
|
|
82
|
+
"version": "0.7.1",
|
|
83
83
|
"license": "MIT",
|
|
84
84
|
"author": "Paul Miller",
|
|
85
85
|
"source": "https://github.com/paulmillr/noble-post-quantum",
|
|
@@ -108,25 +108,25 @@
|
|
|
108
108
|
"browser": "lib/vendor/browser/noble-post-quantum.mjs"
|
|
109
109
|
},
|
|
110
110
|
"bundler": "esbuild --format=cjs --platform=node (server), esbuild --format=esm --platform=browser (browser)",
|
|
111
|
-
"bundledAt": "2026-08-
|
|
112
|
-
"cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.7.
|
|
111
|
+
"bundledAt": "2026-08-27T00:00:00Z",
|
|
112
|
+
"cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.7.1:*:*:*:*:node.js:*:*",
|
|
113
113
|
"hashes": {
|
|
114
|
-
"server": "sha256:
|
|
115
|
-
"browser": "sha256:
|
|
114
|
+
"server": "sha256:04eaaa4838b43162912c851dd2cab52ab82f56decad8542934171247017a6893",
|
|
115
|
+
"browser": "sha256:30ceef750bc57ae5f59c5aa7454eafb7aad55d30c261a1b5c538a66c569fc031"
|
|
116
116
|
},
|
|
117
|
-
"refreshedAt": "2026-08-
|
|
117
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z",
|
|
118
118
|
"components": {
|
|
119
119
|
"@noble/hashes": {
|
|
120
120
|
"url": "https://github.com/paulmillr/noble-hashes",
|
|
121
|
-
"version": "2.
|
|
121
|
+
"version": "2.4.0"
|
|
122
122
|
},
|
|
123
123
|
"@noble/curves": {
|
|
124
124
|
"url": "https://github.com/paulmillr/noble-curves",
|
|
125
|
-
"version": "2.
|
|
125
|
+
"version": "2.4.0"
|
|
126
126
|
},
|
|
127
127
|
"@noble/ciphers": {
|
|
128
128
|
"url": "https://github.com/paulmillr/noble-ciphers",
|
|
129
|
-
"version": "2.
|
|
129
|
+
"version": "2.4.0"
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
},
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
},
|
|
149
149
|
"runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
|
|
150
150
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
151
|
-
"refreshedAt": "2026-08-
|
|
151
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
152
152
|
},
|
|
153
153
|
"bimi-trust-anchors": {
|
|
154
154
|
"version": "operator-managed",
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
},
|
|
174
174
|
"runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
|
|
175
175
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
176
|
-
"refreshedAt": "2026-08-
|
|
176
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
177
177
|
},
|
|
178
178
|
"publicsuffix-list": {
|
|
179
179
|
"version": "master",
|
|
@@ -193,7 +193,7 @@
|
|
|
193
193
|
},
|
|
194
194
|
"runtime_artifact": "lib/vendor/public-suffix-list.data.js",
|
|
195
195
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
196
|
-
"refreshedAt": "2026-08-
|
|
196
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
197
197
|
},
|
|
198
198
|
"@blamejs/pki": {
|
|
199
199
|
"version": "0.5.31",
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
"hashes": {
|
|
222
222
|
"server": "sha256:8cc54fdfb7ac5277fb30f054f57500a9e77ce6b2d2b8f6846a14766d217770db"
|
|
223
223
|
},
|
|
224
|
-
"refreshedAt": "2026-08-
|
|
224
|
+
"refreshedAt": "2026-08-28T04:49:21.011Z"
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// XChaCha20-Poly1305 — vendored from @noble/ciphers v2.
|
|
1
|
+
// XChaCha20-Poly1305 — vendored from @noble/ciphers v2.4.0 by Paul Miller
|
|
2
2
|
// License: MIT — https://github.com/paulmillr/noble-ciphers
|
|
3
3
|
// Browser build (ESM), bundled with esbuild from the same install as the
|
|
4
4
|
// server bundle beside it. Exports: xchacha20poly1305
|
|
@@ -71,6 +71,17 @@ function byteSwap32(arr) {
|
|
|
71
71
|
return arr;
|
|
72
72
|
}
|
|
73
73
|
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
74
|
+
function overlapBytes(a, b) {
|
|
75
|
+
if (!a.byteLength || !b.byteLength)
|
|
76
|
+
return false;
|
|
77
|
+
return a.buffer === b.buffer && // best we can do, may fail with an obscure Proxy
|
|
78
|
+
a.byteOffset < b.byteOffset + b.byteLength && // a starts before b end
|
|
79
|
+
b.byteOffset < a.byteOffset + a.byteLength;
|
|
80
|
+
}
|
|
81
|
+
function complexOverlapBytes(input, output) {
|
|
82
|
+
if (overlapBytes(input, output) && input.byteOffset < output.byteOffset)
|
|
83
|
+
throw new Error("complex overlap of input and output is not supported");
|
|
84
|
+
}
|
|
74
85
|
function checkOpts(defaults, opts) {
|
|
75
86
|
aobject(defaults, "defaults");
|
|
76
87
|
aobject(opts, "opts");
|
|
@@ -238,7 +249,10 @@ function createCipher(core, opts) {
|
|
|
238
249
|
abytes(nonce, void 0, "nonce");
|
|
239
250
|
abytes(data, void 0, "data");
|
|
240
251
|
const len = data.length;
|
|
252
|
+
const hasOutput = output !== void 0;
|
|
241
253
|
output = getOutput(len, output, false);
|
|
254
|
+
if (hasOutput)
|
|
255
|
+
complexOverlapBytes(data, output);
|
|
242
256
|
anumber(counter);
|
|
243
257
|
if (counter < 0 || counter >= MAX_COUNTER)
|
|
244
258
|
throw new Error("arx: counter overflow");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @noble/hashes v2.
|
|
1
|
+
// @noble/hashes v2.4.0 — vendored from Paul Miller
|
|
2
2
|
// License: MIT — https://github.com/paulmillr/noble-hashes
|
|
3
3
|
// Browser build (ESM), bundled with esbuild. The server side uses node:crypto
|
|
4
4
|
// for these, so there is no .cjs beside it.
|
|
@@ -89,6 +89,14 @@ var aobject = (value, label) => {
|
|
|
89
89
|
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
90
90
|
throw new TypeError((label === "object" ? "" : `"${label}" `) + "expected object, got type=" + typeof value);
|
|
91
91
|
};
|
|
92
|
+
var aopts = (value, label) => {
|
|
93
|
+
aobject(value, label);
|
|
94
|
+
const proto = Object.getPrototypeOf(value);
|
|
95
|
+
if (proto !== Object.prototype && proto !== null)
|
|
96
|
+
throw new TypeError(`"${label}" expected plain object`);
|
|
97
|
+
if (Object.hasOwn(value, "__proto__"))
|
|
98
|
+
throw new TypeError(`"${label}.__proto__" is not allowed`);
|
|
99
|
+
};
|
|
92
100
|
function aexists(instance, checkFinished = true) {
|
|
93
101
|
if (instance.destroyed)
|
|
94
102
|
throw new Error("hash was destroyed");
|
|
@@ -128,10 +136,10 @@ function byteSwap32(arr) {
|
|
|
128
136
|
}
|
|
129
137
|
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
130
138
|
function checkOpts(defaults, opts, title = "opts") {
|
|
131
|
-
|
|
139
|
+
aopts(defaults, "defaults");
|
|
132
140
|
if (opts !== void 0)
|
|
133
|
-
|
|
134
|
-
const merged = Object.assign(defaults, opts);
|
|
141
|
+
aopts(opts, title);
|
|
142
|
+
const merged = Object.assign(/* @__PURE__ */ Object.create(null), defaults, opts);
|
|
135
143
|
return merged;
|
|
136
144
|
}
|
|
137
145
|
function createHasher(hashCons, info = {}) {
|