@blamejs/core 0.18.39 → 0.18.41

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.
@@ -74,6 +74,38 @@ function _err(code, message) {
74
74
  return new PublicSuffixError(code, message);
75
75
  }
76
76
 
77
+ // The index of the first character that cannot appear in a host name, or -1.
78
+ //
79
+ // Control / NUL / whitespace bytes, DEL, and the URL-structural delimiters
80
+ // domainToASCII silently TRUNCATES at — "/" (0x2F), "?" (0x3F), "#" (0x23),
81
+ // "\" (0x5C) reduce "example.com/evil" to "example.com" rather than failing,
82
+ // which would let a hostile host masquerade as a trusted prefix. ":" / "@" /
83
+ // "[" / "]" already make domainToASCII return "", but they are rejected here
84
+ // too so every non-host character fails closed rather than silently.
85
+ //
86
+ // Exported because it is the framework's definition of "a character that may
87
+ // appear in a host", and the DNS wire encoder needs the same answer. When it
88
+ // had its own — a shorter one — `b.network.dns` encoded `a\u0000.com` and
89
+ // `example.com/evil` into query labels that this module refuses outright.
90
+ function _firstNonHostCharacter(name) {
91
+ for (var i = 0; i < name.length; i += 1) {
92
+ var cp = name.charCodeAt(i);
93
+ if (cp < 0x21 || cp === 0x7f ||
94
+ cp === 0x2f || cp === 0x3f || cp === 0x23 || cp === 0x5c || // / ? # \
95
+ cp === 0x3a || cp === 0x40 || cp === 0x5b || cp === 0x5d) { // : @ [ ]
96
+ return i;
97
+ }
98
+ }
99
+ return -1;
100
+ }
101
+
102
+ // The characters UTS #46 treats as a label separator, and therefore the ones
103
+ // that can mark the root of an absolute name. Scanned against Node's own
104
+ // mapping and found to be exactly this set across the BMP and the SMP.
105
+ function _isRootMarker(ch) {
106
+ return ch === "." || ch === "。" || ch === "." || ch === "。";
107
+ }
108
+
77
109
  // _normalizeInput — lowercase + IDN-normalize a candidate domain.
78
110
  // Returns a plain ASCII (punycode) string with no leading/trailing
79
111
  // dots and no empty labels. Throws PublicSuffixError on bad shape so
@@ -87,36 +119,41 @@ function _normalizeInput(domain) {
87
119
  throw _err("public-suffix/invalid-domain",
88
120
  "publicSuffix: domain must not be empty");
89
121
  }
90
- if (domain.length > 253) {
91
- // RFC 1035 §2.3.4 253 octets max for the wire form (255 minus
92
- // length-byte + null). Anything longer is structurally invalid.
93
- throw _err("public-suffix/invalid-domain",
94
- "publicSuffix: domain exceeds 253-octet RFC 1035 limit");
95
- }
96
- // Strip a single trailing dot (FQDN form). Multiple trailing dots,
97
- // leading dots, or embedded empty labels remain rejected below.
122
+ // Strip a single trailing root marker (FQDN form) BEFORE measuring. The
123
+ // absolute and relative spellings of one name encode to identical wire
124
+ // bytes, so measuring the marker would refuse a maximum-length name written
125
+ // absolutely while accepting it written relatively.
126
+ //
127
+ // Which character marks the root is not fixed: UTS #46 maps U+3002, U+FF0E
128
+ // and U+FF61 to "." as well, so `münchen.example。` is absolute too.
129
+ // Recognising all four here rather than only the ASCII dot is what lets the
130
+ // `rootStripped` guard below hold — exactly one marker comes off in total,
131
+ // and a second is an empty final label rather than something to remove.
132
+ //
133
+ // A marker that only APPEARS during conversion — because an IDNA-ignored
134
+ // code point trailed it — is handled by the post-conversion strip below,
135
+ // which the same guard keeps mutually exclusive with this one.
98
136
  var s = domain.toLowerCase();
99
- if (s.charCodeAt(s.length - 1) === 46 /* "." */) {
137
+ var rootStripped = false;
138
+ if (_isRootMarker(s.charAt(s.length - 1))) {
100
139
  s = s.slice(0, -1);
140
+ rootStripped = true;
101
141
  if (s.length === 0) {
102
142
  throw _err("public-suffix/invalid-domain",
103
143
  "publicSuffix: domain must not be a bare dot");
104
144
  }
105
145
  }
106
- // Reject control / null / whitespace bytes AND the URL-structural delimiters
107
- // domainToASCII silently TRUNCATES at "/" (0x2F), "?" (0x3F), "#" (0x23),
108
- // "\" (0x5C) reduce "example.com/evil" to "example.com" rather than failing,
109
- // which would let a hostile host masquerade as a trusted prefix. ":" / "@" /
110
- // "[" / "]" already make domainToASCII return "" (caught below), but reject
111
- // them here too so every non-host character fails closed, not silently.
112
- for (var i = 0; i < s.length; i += 1) {
113
- var cp = s.charCodeAt(i);
114
- if (cp < 0x21 || cp === 0x7f ||
115
- cp === 0x2f || cp === 0x3f || cp === 0x23 || cp === 0x5c || // / ? # \
116
- cp === 0x3a || cp === 0x40 || cp === 0x5b || cp === 0x5d) { // : @ [ ]
117
- throw _err("public-suffix/invalid-domain",
118
- "publicSuffix: domain contains a control byte or URL delimiter");
119
- }
146
+ if (s.length > 253) {
147
+ // A cheap bound on the INPUT, so a pathological string is refused before
148
+ // conversion. It is not the authoritative check: an internationalized name
149
+ // grows when it becomes A-labels, so the real test is on the converted form
150
+ // below. Every ASCII name is settled here, since conversion leaves it as-is.
151
+ throw _err("public-suffix/invalid-domain",
152
+ "publicSuffix: domain exceeds 253-octet RFC 1035 limit");
153
+ }
154
+ if (_firstNonHostCharacter(s) !== -1) {
155
+ throw _err("public-suffix/invalid-domain",
156
+ "publicSuffix: domain contains a control byte or URL delimiter");
120
157
  }
121
158
  // IDN-normalize — non-ASCII labels become xn--… via Node's UTS #46
122
159
  // implementation. Empty string back means the input was malformed
@@ -126,11 +163,55 @@ function _normalizeInput(domain) {
126
163
  throw _err("public-suffix/invalid-domain",
127
164
  "publicSuffix: domain failed IDN normalization");
128
165
  }
129
- // No empty labels (`foo..bar`) and no leading dot.
166
+ // No empty labels (`foo..bar`) and no leading dot. This runs BEFORE the
167
+ // root-marker strip below, so a name carrying an empty final label cannot be
168
+ // turned into a valid one by removing a dot: `münchen.example。。` converts to
169
+ // a trailing `..` and is refused here rather than quietly becoming a
170
+ // different, real domain.
130
171
  if (ascii.indexOf("..") !== -1 || ascii.charCodeAt(0) === 46) {
131
172
  throw _err("public-suffix/invalid-domain",
132
173
  "publicSuffix: domain contains empty label");
133
174
  }
175
+ // RFC 1035 §2.3.4 — 253 octets max for the wire form (255 minus the leading
176
+ // length byte and the root's null). This is the AUTHORITATIVE check, and it
177
+ // has to run on the converted name: an internationalized label grows into its
178
+ // `xn--` form, so five 44-character labels are 224 characters going in and
179
+ // 254 octets coming out, with every individual label a legal 50. Measuring
180
+ // the input handed the caller a name that cannot be put on the wire, and a
181
+ // caller cannot tell — it looks like any other domain, and the DMARC walk
182
+ // stepped over the unqueryable target and applied an ancestor's policy.
183
+ //
184
+ // A trailing root marker is still present at this point and does not count:
185
+ // the wire form carries the root as a zero-length label, not a character.
186
+ var withoutRoot = ascii.charCodeAt(ascii.length - 1) === 46
187
+ ? ascii.length - 1 : ascii.length;
188
+ if (withoutRoot > 253) {
189
+ throw _err("public-suffix/invalid-domain",
190
+ "publicSuffix: domain exceeds 253-octet RFC 1035 limit once converted " +
191
+ "to A-labels (" + withoutRoot + " octets)");
192
+ }
193
+ // The root marker is stripped here when it was not an ASCII dot on the way
194
+ // in. UTS #46 maps U+3002, U+FF0E and U+FF61 to ".", so `münchen.example。`
195
+ // arrives with no trailing dot and leaves the conversion with one. Returning
196
+ // that from a function whose contract is to strip the trailing dot leaves
197
+ // every caller to compensate, and the ones that do not compare two spellings
198
+ // of the same absolute name as different names.
199
+ //
200
+ // At most ONE root marker is removed in total. A dot still here after one was
201
+ // already taken off means the name ended in two of them — an empty final
202
+ // label — and stripping the second would hand the caller a different, valid
203
+ // domain than the one they asked about.
204
+ if (ascii.charCodeAt(ascii.length - 1) === 46 /* "." */) {
205
+ if (rootStripped) {
206
+ throw _err("public-suffix/invalid-domain",
207
+ "publicSuffix: domain contains empty label");
208
+ }
209
+ ascii = ascii.slice(0, -1);
210
+ if (ascii.length === 0) {
211
+ throw _err("public-suffix/invalid-domain",
212
+ "publicSuffix: domain must not be a bare dot");
213
+ }
214
+ }
134
215
  return ascii;
135
216
  }
136
217
 
@@ -462,4 +543,9 @@ module.exports = {
462
543
  canonicalDomain: canonicalDomain,
463
544
  isPublicSuffix: isPublicSuffix,
464
545
  lookupSource: lookupSource,
546
+ _firstNonHostCharacter: _firstNonHostCharacter,
547
+ // Exported for the same reason as the character predicate above: this is the
548
+ // framework's answer to "which characters mark the root of an absolute name",
549
+ // and a second copy of the set drifts from it.
550
+ _isRootMarker: _isRootMarker,
465
551
  };
package/lib/seeders.js CHANGED
@@ -452,11 +452,11 @@ function create(opts) {
452
452
  function status(callerOpts) {
453
453
  callerOpts = callerOpts || {};
454
454
  _validateEnv("seeders.status: env", callerOpts.env);
455
- var db = _resolveDb(opts);
456
- _ensureTables(db);
455
+ var conn = _resolveDb(opts);
456
+ _ensureTables(conn);
457
457
  var env = callerOpts.env;
458
458
  var loaded = _loadAllForEnv(dir, env);
459
- var applied = _appliedRows(db, env);
459
+ var applied = _appliedRows(conn, env);
460
460
  var appliedNames = new Set(applied.map(function (r) { return r.name; }));
461
461
  var pending = loaded.ordered.filter(function (n) {
462
462
  var mod = loaded.modByName[n];
@@ -491,8 +491,8 @@ function create(opts) {
491
491
  }
492
492
  }
493
493
 
494
- var db = _resolveDb(opts);
495
- _ensureTables(db);
494
+ var conn = _resolveDb(opts);
495
+ _ensureTables(conn);
496
496
 
497
497
  var loaded = _loadAllForEnv(dir, env);
498
498
 
@@ -504,11 +504,11 @@ function create(opts) {
504
504
  var startedAt = clock();
505
505
  observability().safeEvent("seeders.run.start", 1, { env: env, count: loaded.ordered.length });
506
506
 
507
- var holder = _acquireLock(db, lockStaleAfterMs, clock);
507
+ var holder = _acquireLock(conn, lockStaleAfterMs, clock);
508
508
  try {
509
- var appliedSelBuilt = sql.select(_seedersTable(), _sqlOpts(db))
509
+ var appliedSelBuilt = sql.select(_seedersTable(), _sqlOpts(conn))
510
510
  .columns(["name"]).where("env", env).toSql();
511
- var appliedSelStmt = db.prepare(appliedSelBuilt.sql);
511
+ var appliedSelStmt = conn.prepare(appliedSelBuilt.sql);
512
512
  var appliedSet = new Set(
513
513
  appliedSelStmt.all.apply(appliedSelStmt, appliedSelBuilt.params)
514
514
  .map(function (r) { return r.name; })
@@ -539,26 +539,26 @@ function create(opts) {
539
539
  // Per-seed transaction: SQLite txns are sync, but the seed's
540
540
  // run() may be async — runInTransactionAsync wraps BEGIN/COMMIT
541
541
  // around the awaited body and rolls back this seed only on failure.
542
- await dbSchema.runInTransactionAsync(db, async function () {
543
- await mod.run(db, ctx);
542
+ await dbSchema.runInTransactionAsync(conn, async function () {
543
+ await mod.run(conn, ctx);
544
544
  var nowIso = new Date(clock()).toISOString();
545
545
  var writeBuilt;
546
546
  if (alreadyApplied && mod.rerunnable) {
547
- writeBuilt = sql.update(_seedersTable(), _sqlOpts(db))
547
+ writeBuilt = sql.update(_seedersTable(), _sqlOpts(conn))
548
548
  .set({ appliedAt: nowIso, description: mod.description || "",
549
549
  rerunnable: mod.rerunnable ? 1 : 0 })
550
550
  .where("env", env).where("name", name).toSql();
551
551
  } else if (alreadyApplied && force) {
552
- writeBuilt = sql.update(_seedersTable(), _sqlOpts(db))
552
+ writeBuilt = sql.update(_seedersTable(), _sqlOpts(conn))
553
553
  .set({ appliedAt: nowIso, description: mod.description || "" })
554
554
  .where("env", env).where("name", name).toSql();
555
555
  } else {
556
- writeBuilt = sql.insert(_seedersTable(), _sqlOpts(db))
556
+ writeBuilt = sql.insert(_seedersTable(), _sqlOpts(conn))
557
557
  .values({ env: env, name: name, description: mod.description || "",
558
558
  appliedAt: nowIso, rerunnable: mod.rerunnable ? 1 : 0 })
559
559
  .toSql();
560
560
  }
561
- var writeStmt = db.prepare(writeBuilt.sql);
561
+ var writeStmt = conn.prepare(writeBuilt.sql);
562
562
  writeStmt.run.apply(writeStmt, writeBuilt.params);
563
563
  }, {
564
564
  onRollbackFail: function (rollbackErr) {
@@ -629,7 +629,7 @@ function create(opts) {
629
629
  }
630
630
  return result;
631
631
  } finally {
632
- _releaseLock(db, holder);
632
+ _releaseLock(conn, holder);
633
633
  }
634
634
  }
635
635
 
package/lib/session.js CHANGED
@@ -54,6 +54,7 @@ var validateOpts = require("./validate-opts");
54
54
  var cluster = require("./cluster");
55
55
  var clusterStorage = require("./cluster-storage");
56
56
  var C = require("./constants");
57
+ var cookies = require("./cookies");
57
58
  var { generateToken, sha3Hash } = require("./crypto");
58
59
  var cryptoField = require("./crypto-field");
59
60
  var frameworkSchema = require("./framework-schema");
@@ -410,7 +411,9 @@ function _hashFingerprint(sid, inputs) {
410
411
  * data: { roles: ["admin"] },
411
412
  * ttlMs: b.constants.TIME.hours(8),
412
413
  * });
413
- * res.setHeader("Set-Cookie", "sid=" + s.token + "; HttpOnly; Secure; SameSite=Strict");
414
+ * b.cookies.appendSetCookie(res, b.cookies.serialize("sid", s.token, {
415
+ * httpOnly: true, secure: true, sameSite: "Strict", path: "/",
416
+ * }));
414
417
  * // → { token: "9f2c…", expiresAt: 1735689600000 }
415
418
  */
416
419
  // Anonymous-session prefix. b.session.create({ anonymous: true })
@@ -770,7 +773,9 @@ async function verify(token, verifyOpts) {
770
773
  *
771
774
  * @example
772
775
  * await b.session.destroy(req.cookies.sid);
773
- * res.setHeader("Set-Cookie", "sid=; HttpOnly; Max-Age=0");
776
+ * b.cookies.appendSetCookie(res, b.cookies.serialize("sid", "", {
777
+ * httpOnly: true, sameSite: "Strict", path: "/", maxAge: 0,
778
+ * }));
774
779
  * res.end("logged out");
775
780
  * // → true
776
781
  */
@@ -798,13 +803,29 @@ async function destroy(token) {
798
803
  * this composes the secure-default logout the middleware otherwise had to be
799
804
  * mounted by hand. Returns whether a session was destroyed. Leader-only.
800
805
  *
806
+ * A browser deletes a cookie by MATCHING the expiry cookie's name, path and
807
+ * domain against the one in its jar, and it refuses a `Secure` cookie
808
+ * altogether when the response came over plain HTTP. So the expiry cookie has
809
+ * to describe the same scope the session cookie was written with, or the
810
+ * logout leaves it in place. Pass the `req` and the scheme is resolved through
811
+ * `b.requestHelpers.trustedProtocol` (a forwarded scheme counts only from a
812
+ * peer you declared trusted); pass `secure` to state it outright. With neither,
813
+ * the cookie is `Secure` — the secure default, unchanged.
814
+ *
801
815
  * @opts
802
- * cookieName: string, // default: "sid" — the session cookie to expire
803
- * types: string[], // default: the W3C Clear-Site-Data directive set
816
+ * cookieName: string, // default: "sid" — the session cookie to expire
817
+ * types: string[], // default: the W3C Clear-Site-Data directive set
818
+ * req: object, // resolve Secure from this request's scheme
819
+ * secure: boolean, // default: true — state the scheme outright
820
+ * sameSite: string, // default: "Strict" — Strict / Lax / None
821
+ * path: string, // default: "/" — must match the cookie's Path
822
+ * domain: string, // must match the cookie's Domain, if it had one
823
+ * trustedProxies: string | string[], // CIDRs, for the `req` scheme resolve
824
+ * protocolResolver: function(req), // own the scheme decision instead
804
825
  *
805
826
  * @example
806
827
  * app.post("/logout", async function (req, res) {
807
- * await b.session.logout(res, req.cookies.sid);
828
+ * await b.session.logout(res, req.cookies.sid, { req: req });
808
829
  * res.end("logged out");
809
830
  * });
810
831
  * // → emits Clear-Site-Data + expires the sid cookie + destroys the session
@@ -814,7 +835,19 @@ async function logout(res, token, opts) {
814
835
  throw new SessionError("session/bad-res",
815
836
  "b.session.logout: res must be an HTTP response with setHeader()");
816
837
  }
838
+ // The expiry cookie is queued with b.cookies.appendSetCookie, which needs to
839
+ // READ the response as well as write it. Assert that contract here, with the
840
+ // other validation, rather than discovering it at queue time: the queue
841
+ // happens after destroy(), so a throw there would leave the session revoked,
842
+ // Clear-Site-Data queued, no expiry cookie and a failed request. The
843
+ // response's shape is the caller's and fixed for the process, so it is
844
+ // knowable before any of that.
845
+ cookies.assertAppendable(res);
817
846
  opts = opts || {};
847
+ validateOpts(opts, [
848
+ "cookieName", "types", "req", "secure", "sameSite", "path", "domain",
849
+ "trustedProxies", "protocolResolver",
850
+ ], "b.session.logout");
818
851
  var cookieName = opts.cookieName === undefined ? "sid" : opts.cookieName;
819
852
  if (typeof cookieName !== "string" || cookieName.length === 0) {
820
853
  throw new SessionError("session/bad-cookie-name",
@@ -826,6 +859,19 @@ async function logout(res, token, opts) {
826
859
  // unknown directive throws here, queuing nothing.
827
860
  var clearSiteDataValue = csd.headerValue(types, "b.session.logout");
828
861
 
862
+ // Same ordering for the cookie: b.cookies.serialize validates the name, the
863
+ // attributes and the RFC 6265bis prefix invariants, so it is a throwing call
864
+ // and must run before the row is revoked — otherwise a `__Host-` typo leaves
865
+ // the session destroyed and the browser still holding its cookie.
866
+ var expiryCookie = cookies.serialize(cookieName, "", {
867
+ httpOnly: true,
868
+ secure: _logoutCookieSecure(opts, cookieName),
869
+ sameSite: opts.sameSite === undefined ? "Strict" : opts.sameSite,
870
+ path: opts.path === undefined ? "/" : opts.path,
871
+ domain: opts.domain,
872
+ maxAge: 0,
873
+ });
874
+
829
875
  // Revoke the server-side session FIRST. If destroy() throws (a follower
830
876
  // failing cluster.requireLeader(), or a store/DB error), no client-wipe
831
877
  // headers have been queued — an error response can't then expire the
@@ -836,12 +882,55 @@ async function logout(res, token, opts) {
836
882
  // Now wipe the client-side state: W3C Clear-Site-Data (cookies /
837
883
  // storage / cache) + expire the session cookie (belt-and-suspenders with the
838
884
  // "cookies" directive, and effective even if the client ignores the header).
885
+ // Append rather than set: a route that already queued a cookie of its own
886
+ // (a rotated CSRF token, a locale) keeps it.
839
887
  res.setHeader("Clear-Site-Data", clearSiteDataValue);
840
- res.setHeader("Set-Cookie",
841
- cookieName + "=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=0");
888
+ cookies.appendSetCookie(res, expiryCookie);
842
889
  return destroyed;
843
890
  }
844
891
 
892
+ // Whether logout's expiry cookie carries Secure. Explicit `secure` wins; a
893
+ // `req` resolves through the peer-gated protocol helper; with neither the
894
+ // answer is the secure default. A browser drops a Secure cookie arriving over
895
+ // plain HTTP, so answering "true" for an HTTP deployment does not fail safe —
896
+ // it fails to clear the cookie at all.
897
+ function _logoutCookieSecure(opts, cookieName) {
898
+ if (opts.secure !== undefined) {
899
+ if (typeof opts.secure !== "boolean") {
900
+ throw new SessionError("session/bad-secure",
901
+ "b.session.logout: opts.secure must be a boolean");
902
+ }
903
+ // An explicit `false` against a `__Host-`/`__Secure-` name is a
904
+ // contradiction, and serialize() refuses it. That is deliberate: the
905
+ // operator stated both halves, it fails on the first logout in every
906
+ // environment, and no request can provoke or avoid it.
907
+ return opts.secure;
908
+ }
909
+ // A `__Host-` / `__Secure-` name is a statement about the COOKIE, not about
910
+ // this request: a browser will only ever have stored such a cookie on a
911
+ // secure origin, so on a cleartext request there is nothing of that name to
912
+ // clear. Letting the request's scheme resolve `secure` to false here would
913
+ // make serialize() refuse the name — and the cookie is built BEFORE the row
914
+ // is revoked, so that refusal would abort the logout entirely and whoever
915
+ // chose the scheme would decide whether the session died. The prefix wins.
916
+ var lowerName = cookieName.toLowerCase();
917
+ if (lowerName.indexOf("__host-") === 0 || lowerName.indexOf("__secure-") === 0) {
918
+ return true;
919
+ }
920
+ if (opts.req === undefined) return true;
921
+ if (opts.req === null || typeof opts.req !== "object") {
922
+ // trustedProtocol answers "http" for a non-request rather than throwing, so
923
+ // a mistyped `req` would quietly drop Secure. Refuse it instead.
924
+ throw new SessionError("session/bad-req",
925
+ "b.session.logout: opts.req must be an HTTP request object");
926
+ }
927
+ var resolver = requestHelpers.trustedProtocol({
928
+ trustedProxies: opts.trustedProxies,
929
+ protocolResolver: opts.protocolResolver,
930
+ });
931
+ return resolver.resolve(opts.req) === "https";
932
+ }
933
+
845
934
  async function _deleteBySidHash(sidHash) {
846
935
  var built = sql.delete(_sessionSqlTable(), _sessionSqlOpts())
847
936
  .where("sidHash", sidHash)
@@ -1057,7 +1146,9 @@ async function touch(token, opts) {
1057
1146
  * reason: "mfa",
1058
1147
  * });
1059
1148
  * if (rotated) {
1060
- * res.setHeader("Set-Cookie", "sid=" + rotated.token + "; HttpOnly; Secure; SameSite=Strict");
1149
+ * b.cookies.appendSetCookie(res, b.cookies.serialize("sid", rotated.token, {
1150
+ * httpOnly: true, secure: true, sameSite: "Strict", path: "/",
1151
+ * }));
1061
1152
  * }
1062
1153
  * // → { token: "7a1e…", expiresAt: 1735689600000 }
1063
1154
  */
@@ -21,7 +21,7 @@
21
21
  "server": "sha256:f3325f480cb8eb814fcb0baaa19336cbbf2b993f48624c6aa9600ffd69d0be5e",
22
22
  "browser": "sha256:0ffd91540bcb586a29b56e52ee1c29df69097b50776beb4036a07558f7a4e12e"
23
23
  },
24
- "refreshedAt": "2026-08-19T07:22:41.105Z"
24
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
25
25
  },
26
26
  "@noble/hashes": {
27
27
  "version": "2.3.0",
@@ -48,7 +48,7 @@
48
48
  "hashes": {
49
49
  "browser": "sha256:dfe4b7ae3c9880e388c8da4b68f44742b229b53afacd1e674179527e33da62b0"
50
50
  },
51
- "refreshedAt": "2026-08-19T07:22:41.105Z"
51
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
52
52
  },
53
53
  "@noble/curves": {
54
54
  "version": "2.3.0",
@@ -70,7 +70,7 @@
70
70
  "hashes": {
71
71
  "server": "sha256:b5fe88d1ea780d0581dee6145d666f89d46fc9531b5db35db2e5b16627840890"
72
72
  },
73
- "refreshedAt": "2026-08-19T07:22:41.105Z",
73
+ "refreshedAt": "2026-08-20T04:54:41.606Z",
74
74
  "components": {
75
75
  "@noble/hashes": {
76
76
  "url": "https://github.com/paulmillr/noble-hashes",
@@ -114,7 +114,7 @@
114
114
  "server": "sha256:fab7ebe5737793862c473444f4ee5912f79dd1edec86683acbb4eecbca0f5892",
115
115
  "browser": "sha256:cae1d5bbdc7184b202b6ca68df6e1db7b0d0f668c77809ded189ca7f271accc9"
116
116
  },
117
- "refreshedAt": "2026-08-19T07:22:41.105Z",
117
+ "refreshedAt": "2026-08-20T04:54:41.606Z",
118
118
  "components": {
119
119
  "@noble/hashes": {
120
120
  "url": "https://github.com/paulmillr/noble-hashes",
@@ -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-19T07:22:41.105Z"
151
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
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-19T07:22:41.105Z"
176
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
177
177
  },
178
178
  "publicsuffix-list": {
179
179
  "version": "master",
@@ -186,17 +186,17 @@
186
186
  "data_js": "lib/vendor/public-suffix-list.data.js"
187
187
  },
188
188
  "bundler": "curl https://publicsuffix.org/list/public_suffix_list.dat",
189
- "bundledAt": "2026-08-17T00:00:00Z",
189
+ "bundledAt": "2026-08-19T00:00:00Z",
190
190
  "hashes": {
191
- "server": "sha256:f7e24ebb8ef6a366c52d2abc66275f68f64255f2650cda0c506035a92cff049a",
192
- "data_js": "sha256:4b54a31c8697ee4509a3e1efbee3d155f276a29e15a135d550874024f4780266"
191
+ "server": "sha256:75142784c0308c8f7cd27f15fca80b9ddc09aeec05ae51c2f54fd0958351d9ad",
192
+ "data_js": "sha256:2435668e6d9964d95b283e5796938aa7c626ea0ca1eed4af8b1abec034f1761b"
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-19T07:22:41.105Z"
196
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
197
197
  },
198
198
  "@blamejs/pki": {
199
- "version": "0.5.11",
199
+ "version": "0.5.16",
200
200
  "license": "Apache-2.0",
201
201
  "author": "blamejs",
202
202
  "source": "https://github.com/blamejs/pki",
@@ -217,11 +217,11 @@
217
217
  },
218
218
  "bundler": "esbuild --format=cjs --platform=node --external:crypto --external:node:crypto",
219
219
  "bundledAt": "2026-08-19T00:00:00Z",
220
- "cpe": "cpe:2.3:a:blamejs:pki:0.5.11:*:*:*:*:node.js:*:*",
220
+ "cpe": "cpe:2.3:a:blamejs:pki:0.5.16:*:*:*:*:node.js:*:*",
221
221
  "hashes": {
222
- "server": "sha256:dfcaa10473eb0980958b1c6a4019114c69260191899edd5dc596605f21543249"
222
+ "server": "sha256:96861fcf18c319d2e984cf48aaa0e496e903e447c491c6efcee677e15e87c24e"
223
223
  },
224
- "refreshedAt": "2026-08-19T07:22:41.105Z"
224
+ "refreshedAt": "2026-08-20T04:54:41.606Z"
225
225
  }
226
226
  }
227
227
  }