@blamejs/core 0.10.9 → 0.10.11
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 +2 -0
- package/index.js +9 -0
- package/lib/audit.js +1 -0
- package/lib/auth/dpop.js +5 -1
- package/lib/auth/fido-mds3.js +19 -3
- package/lib/auth/jwt.js +2 -1
- package/lib/auth/oauth.js +17 -5
- package/lib/auth/status-list.js +7 -1
- package/lib/crypto-hpke-pq.js +187 -0
- package/lib/crypto.js +11 -3
- package/lib/guard-list-id.js +6 -1
- package/lib/jose-jwe-experimental.js +228 -0
- package/lib/mail-server-imap.js +132 -29
- package/lib/mail-server-jmap.js +38 -4
- package/lib/mail-server-managesieve.js +68 -17
- package/lib/mail-server-registry.js +318 -0
- package/lib/mcp.js +12 -2
- package/lib/network-dns.js +9 -0
- package/lib/pagination.js +2 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.mail.serverRegistry
|
|
4
|
+
* @nav Mail
|
|
5
|
+
* @title Mail Server Method Registry
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Shared per-method dispatch registry for the IMAP / JMAP /
|
|
9
|
+
* ManageSieve listener factories. Replaces the hand-rolled
|
|
10
|
+
* `switch (verb)` dispatchers with a single primitive that:
|
|
11
|
+
*
|
|
12
|
+
* - runs the protocol-specific guard chain BEFORE the handler
|
|
13
|
+
* lookup (so operator-supplied overrides cannot bypass
|
|
14
|
+
* `b.guardImapCommand` / `b.guardJmap` / `b.guardManagesieveCommand`),
|
|
15
|
+
* - enforces per-handler resource budgets (`maxHandlerBytes`,
|
|
16
|
+
* `maxHandlerMs`) — refused at registration time if not supplied,
|
|
17
|
+
* - emits a `mail.serverRegistry.method_dispatch` audit event with
|
|
18
|
+
* handler-source (`builtin` | `operator-override`),
|
|
19
|
+
* - rejects registrations of method names outside the per-protocol
|
|
20
|
+
* IANA / RFC catalogue (unless `allowExperimental: true`, which
|
|
21
|
+
* itself audits).
|
|
22
|
+
*
|
|
23
|
+
* Operators wanting to override IMAP `FETCH`, JMAP `Email/query`,
|
|
24
|
+
* ManageSieve `PUTSCRIPT`, etc. supply
|
|
25
|
+
* `opts.overrides: { "FETCH": { fn, maxHandlerBytes, maxHandlerMs } }`
|
|
26
|
+
* to the listener factory and the registry routes dispatch through
|
|
27
|
+
* the override without touching wire-protocol state, audit
|
|
28
|
+
* lifecycle, or the guard substrate.
|
|
29
|
+
*
|
|
30
|
+
* Per-handler resource budgets are required (no auto-defaults) per
|
|
31
|
+
* the framework's security-defaults-on rule: an operator-supplied
|
|
32
|
+
* FETCH override that omits a budget could regress CVE-2024-34055
|
|
33
|
+
* (Cyrus authenticated OOM via unbounded allocation); the
|
|
34
|
+
* stricter-mode contract forces the operator to acknowledge the
|
|
35
|
+
* resource ceiling explicitly.
|
|
36
|
+
*
|
|
37
|
+
* @card
|
|
38
|
+
* Per-method dispatch registry for the mail-server listeners. Guard-chain preserving, per-handler resource-budget required, audit on every dispatch.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
var safeAsync = require("./safe-async");
|
|
42
|
+
var validateOpts = require("./validate-opts");
|
|
43
|
+
var audit = require("./audit");
|
|
44
|
+
var { defineClass } = require("./framework-error");
|
|
45
|
+
|
|
46
|
+
var MailServerRegistryError = defineClass("MailServerRegistryError", { alwaysPermanent: true });
|
|
47
|
+
|
|
48
|
+
// Per-protocol RFC catalogue. Names outside these tables refuse at
|
|
49
|
+
// register() unless `allowExperimental: true`.
|
|
50
|
+
var IMAP_VERBS = Object.freeze({
|
|
51
|
+
CAPABILITY: 1, NOOP: 1, LOGOUT: 1, STARTTLS: 1, AUTHENTICATE: 1,
|
|
52
|
+
LOGIN: 1, ID: 1, ENABLE: 1, SELECT: 1, EXAMINE: 1, CREATE: 1,
|
|
53
|
+
DELETE: 1, RENAME: 1, SUBSCRIBE: 1, UNSUBSCRIBE: 1, LIST: 1,
|
|
54
|
+
NAMESPACE: 1, STATUS: 1, APPEND: 1, IDLE: 1, CHECK: 1, CLOSE: 1,
|
|
55
|
+
UNSELECT: 1, EXPUNGE: 1, SEARCH: 1, FETCH: 1, STORE: 1, COPY: 1,
|
|
56
|
+
MOVE: 1, UID: 1, DONE: 1,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
var MANAGESIEVE_VERBS = Object.freeze({
|
|
60
|
+
AUTHENTICATE: 1, STARTTLS: 1, LOGOUT: 1, CAPABILITY: 1, HAVESPACE: 1,
|
|
61
|
+
PUTSCRIPT: 1, LISTSCRIPTS: 1, SETACTIVE: 1, GETSCRIPT: 1,
|
|
62
|
+
DELETESCRIPT: 1, RENAMESCRIPT: 1, NOOP: 1,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// JMAP method names match the `Type/verb` shape. The catalogue table
|
|
66
|
+
// enumerates the RFC 8620 + RFC 8621 set; operator-registered types
|
|
67
|
+
// must opt in via `allowExperimental: true` with audit emission.
|
|
68
|
+
var JMAP_METHODS = Object.freeze({
|
|
69
|
+
"Core/echo": 1,
|
|
70
|
+
"Mailbox/get": 1, "Mailbox/changes": 1, "Mailbox/query": 1,
|
|
71
|
+
"Mailbox/queryChanges": 1, "Mailbox/set": 1,
|
|
72
|
+
"Thread/get": 1, "Thread/changes": 1,
|
|
73
|
+
"Email/get": 1, "Email/changes": 1, "Email/query": 1,
|
|
74
|
+
"Email/queryChanges": 1, "Email/set": 1, "Email/copy": 1,
|
|
75
|
+
"Email/import": 1, "Email/parse": 1,
|
|
76
|
+
"SearchSnippet/get": 1,
|
|
77
|
+
"Identity/get": 1, "Identity/changes": 1, "Identity/set": 1,
|
|
78
|
+
"EmailSubmission/get": 1, "EmailSubmission/changes": 1,
|
|
79
|
+
"EmailSubmission/query": 1, "EmailSubmission/queryChanges": 1,
|
|
80
|
+
"EmailSubmission/set": 1,
|
|
81
|
+
"VacationResponse/get": 1, "VacationResponse/set": 1,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
var CATALOGUE = Object.freeze({
|
|
85
|
+
imap: IMAP_VERBS,
|
|
86
|
+
jmap: JMAP_METHODS,
|
|
87
|
+
managesieve: MANAGESIEVE_VERBS,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Maximum resource budget caps. Operators cannot raise above these
|
|
91
|
+
// even with explicit values — protects against accidental
|
|
92
|
+
// configuration that lifts the CVE-2024-34055 / CVE-2026-26312 OOM
|
|
93
|
+
// ceiling.
|
|
94
|
+
var MAX_HANDLER_BYTES_CEILING = 256 * 1024 * 1024; // allow:raw-byte-literal — 256 MiB per-handler ceiling
|
|
95
|
+
var MAX_HANDLER_MS_CEILING = 5 * 60 * 1000; // allow:raw-time-literal — 5 minute per-handler ceiling
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @primitive b.mail.serverRegistry.create
|
|
99
|
+
* @signature b.mail.serverRegistry.create(opts)
|
|
100
|
+
* @since 0.10.11
|
|
101
|
+
* @status stable
|
|
102
|
+
* @related b.mail.server.imap.create, b.mail.server.jmap.create, b.mail.server.managesieve.create
|
|
103
|
+
*
|
|
104
|
+
* Build a per-method dispatch registry for one of the mail-server
|
|
105
|
+
* listeners. Returns `{ register, unregister, dispatch, list,
|
|
106
|
+
* has, source, MailServerRegistryError }`.
|
|
107
|
+
*
|
|
108
|
+
* @opts
|
|
109
|
+
* protocol: "imap" | "jmap" | "managesieve",
|
|
110
|
+
* defaults: { [name]: { fn, maxHandlerBytes, maxHandlerMs } },
|
|
111
|
+
* overrides: { [name]: { fn, maxHandlerBytes, maxHandlerMs } },
|
|
112
|
+
* notFoundHandler: function (name, ctx), // optional; returns the protocol's "not configured" reply
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* var reg = b.mail.serverRegistry.create({
|
|
116
|
+
* protocol: "imap",
|
|
117
|
+
* defaults: { CAPABILITY: { fn: _capabilityHandler,
|
|
118
|
+
* maxHandlerBytes: 8 * 1024,
|
|
119
|
+
* maxHandlerMs: 5 * 1000 } },
|
|
120
|
+
* overrides: opts.overrides || {},
|
|
121
|
+
* });
|
|
122
|
+
* await reg.dispatch("CAPABILITY", state, socket, parsed);
|
|
123
|
+
*/
|
|
124
|
+
function create(opts) {
|
|
125
|
+
validateOpts.requireObject(opts, "b.mail.serverRegistry.create",
|
|
126
|
+
MailServerRegistryError, "mail-server-registry/bad-opts");
|
|
127
|
+
validateOpts.requireNonEmptyString(opts.protocol,
|
|
128
|
+
"b.mail.serverRegistry.create: protocol", MailServerRegistryError,
|
|
129
|
+
"mail-server-registry/bad-protocol");
|
|
130
|
+
if (!CATALOGUE[opts.protocol]) {
|
|
131
|
+
throw new MailServerRegistryError("mail-server-registry/unknown-protocol",
|
|
132
|
+
"create: protocol must be 'imap', 'jmap', or 'managesieve' (got '" + opts.protocol + "')");
|
|
133
|
+
}
|
|
134
|
+
var catalogue = CATALOGUE[opts.protocol];
|
|
135
|
+
var entries = Object.create(null);
|
|
136
|
+
|
|
137
|
+
function _validateEntry(name, entry, source) {
|
|
138
|
+
if (!entry || typeof entry !== "object") {
|
|
139
|
+
throw new MailServerRegistryError("mail-server-registry/bad-entry",
|
|
140
|
+
"register: entry for '" + name + "' must be an object");
|
|
141
|
+
}
|
|
142
|
+
if (typeof entry.fn !== "function") {
|
|
143
|
+
throw new MailServerRegistryError("mail-server-registry/bad-handler-fn",
|
|
144
|
+
"register: entry.fn for '" + name + "' must be a function");
|
|
145
|
+
}
|
|
146
|
+
if (typeof entry.maxHandlerBytes !== "number" || !isFinite(entry.maxHandlerBytes) ||
|
|
147
|
+
entry.maxHandlerBytes < 1 || entry.maxHandlerBytes > MAX_HANDLER_BYTES_CEILING ||
|
|
148
|
+
Math.floor(entry.maxHandlerBytes) !== entry.maxHandlerBytes) {
|
|
149
|
+
throw new MailServerRegistryError("mail-server-registry/bad-max-handler-bytes",
|
|
150
|
+
"register: '" + name + "' entry.maxHandlerBytes must be a positive integer ≤ " +
|
|
151
|
+
MAX_HANDLER_BYTES_CEILING + " (got " + entry.maxHandlerBytes + ") — stricter-mode " +
|
|
152
|
+
"registration refuses entries without an explicit budget (defends CVE-2024-34055 / " +
|
|
153
|
+
"CVE-2026-26312 OOM class)");
|
|
154
|
+
}
|
|
155
|
+
if (typeof entry.maxHandlerMs !== "number" || !isFinite(entry.maxHandlerMs) ||
|
|
156
|
+
entry.maxHandlerMs < 1 || entry.maxHandlerMs > MAX_HANDLER_MS_CEILING ||
|
|
157
|
+
Math.floor(entry.maxHandlerMs) !== entry.maxHandlerMs) {
|
|
158
|
+
throw new MailServerRegistryError("mail-server-registry/bad-max-handler-ms",
|
|
159
|
+
"register: '" + name + "' entry.maxHandlerMs must be a positive integer ≤ " +
|
|
160
|
+
MAX_HANDLER_MS_CEILING + " (got " + entry.maxHandlerMs + ")");
|
|
161
|
+
}
|
|
162
|
+
if (!catalogue[name] && entry.allowExperimental !== true) {
|
|
163
|
+
throw new MailServerRegistryError("mail-server-registry/unknown-method",
|
|
164
|
+
"register: '" + name + "' is not in the " + opts.protocol + " catalogue; pass " +
|
|
165
|
+
"allowExperimental: true to opt out of the catalogue gate (audited)");
|
|
166
|
+
}
|
|
167
|
+
if (entry.allowExperimental === true && !catalogue[name]) {
|
|
168
|
+
try {
|
|
169
|
+
audit.safeEmit({
|
|
170
|
+
action: "mail.serverRegistry.experimental_registration",
|
|
171
|
+
outcome: "denied",
|
|
172
|
+
metadata: { protocol: opts.protocol, name: name, source: source,
|
|
173
|
+
severity: "warning" },
|
|
174
|
+
});
|
|
175
|
+
} catch (_e) { /* drop-silent */ }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function register(name, entry) {
|
|
180
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
181
|
+
throw new MailServerRegistryError("mail-server-registry/bad-name",
|
|
182
|
+
"register: name must be a non-empty string");
|
|
183
|
+
}
|
|
184
|
+
var source = entry && entry.source === "operator-override"
|
|
185
|
+
? "operator-override" : "operator-override"; // user-facing register defaults to override
|
|
186
|
+
_validateEntry(name, entry, source);
|
|
187
|
+
entries[name] = {
|
|
188
|
+
fn: entry.fn,
|
|
189
|
+
maxHandlerBytes: entry.maxHandlerBytes,
|
|
190
|
+
maxHandlerMs: entry.maxHandlerMs,
|
|
191
|
+
source: source,
|
|
192
|
+
allowExperimental: entry.allowExperimental === true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _internalRegister(name, entry, source) {
|
|
197
|
+
_validateEntry(name, entry, source);
|
|
198
|
+
entries[name] = {
|
|
199
|
+
fn: entry.fn,
|
|
200
|
+
maxHandlerBytes: entry.maxHandlerBytes,
|
|
201
|
+
maxHandlerMs: entry.maxHandlerMs,
|
|
202
|
+
source: source,
|
|
203
|
+
allowExperimental: entry.allowExperimental === true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Seed defaults first, then operator overrides shadow them.
|
|
208
|
+
if (opts.defaults && typeof opts.defaults === "object") {
|
|
209
|
+
var dnames = Object.keys(opts.defaults);
|
|
210
|
+
for (var di = 0; di < dnames.length; di += 1) {
|
|
211
|
+
_internalRegister(dnames[di], opts.defaults[dnames[di]], "builtin");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (opts.overrides && typeof opts.overrides === "object") {
|
|
215
|
+
var onames = Object.keys(opts.overrides);
|
|
216
|
+
for (var oi = 0; oi < onames.length; oi += 1) {
|
|
217
|
+
_internalRegister(onames[oi], opts.overrides[onames[oi]], "operator-override");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function unregister(name) {
|
|
222
|
+
if (entries[name]) {
|
|
223
|
+
delete entries[name];
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function has(name) { return entries[name] !== undefined; }
|
|
230
|
+
function source(name) { return entries[name] ? entries[name].source : null; }
|
|
231
|
+
|
|
232
|
+
function list() {
|
|
233
|
+
var out = [];
|
|
234
|
+
var names = Object.keys(entries).sort(); // allow:bare-canonicalize-walk — deterministic output ordering
|
|
235
|
+
for (var i = 0; i < names.length; i += 1) {
|
|
236
|
+
out.push({
|
|
237
|
+
name: names[i],
|
|
238
|
+
source: entries[names[i]].source,
|
|
239
|
+
hasBudget: true,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Dispatch a registered method. `name` is the per-protocol verb /
|
|
247
|
+
* method-name; `args` is variadic forwarded to the handler. The
|
|
248
|
+
* registry wraps the handler in `safeAsync.withTimeout` so a
|
|
249
|
+
* runaway handler can't pin the connection. On not-found, returns
|
|
250
|
+
* the protocol's `notFoundHandler` result (or throws if none).
|
|
251
|
+
*/
|
|
252
|
+
function dispatch(name) {
|
|
253
|
+
var argsArr = Array.prototype.slice.call(arguments, 1);
|
|
254
|
+
var entry = entries[name];
|
|
255
|
+
if (!entry) {
|
|
256
|
+
if (typeof opts.notFoundHandler === "function") {
|
|
257
|
+
return opts.notFoundHandler.apply(null, [name].concat(argsArr));
|
|
258
|
+
}
|
|
259
|
+
throw new MailServerRegistryError("mail-server-registry/not-configured",
|
|
260
|
+
"dispatch: '" + name + "' has no registered handler (" + opts.protocol +
|
|
261
|
+
" protocol; supply via opts.defaults or opts.overrides)");
|
|
262
|
+
}
|
|
263
|
+
var t0 = Date.now();
|
|
264
|
+
try {
|
|
265
|
+
audit.safeEmit({
|
|
266
|
+
action: "mail.serverRegistry.method_dispatch",
|
|
267
|
+
outcome: "success",
|
|
268
|
+
metadata: { protocol: opts.protocol, name: name, source: entry.source },
|
|
269
|
+
});
|
|
270
|
+
} catch (_e) { /* drop-silent */ }
|
|
271
|
+
var result;
|
|
272
|
+
try { result = entry.fn.apply(null, argsArr); }
|
|
273
|
+
catch (err) {
|
|
274
|
+
throw new MailServerRegistryError("mail-server-registry/handler-threw",
|
|
275
|
+
"dispatch: '" + name + "' handler threw (" + ((err && err.message) || String(err)) + ")");
|
|
276
|
+
}
|
|
277
|
+
// Wrap promise-returning handlers in safeAsync.withTimeout so a
|
|
278
|
+
// runaway handler can't pin the connection past maxHandlerMs.
|
|
279
|
+
// safeAsync raises its own `async/timeout` error; map it into a
|
|
280
|
+
// typed MailServerRegistryError so the listener catch path sees a
|
|
281
|
+
// single error class.
|
|
282
|
+
if (result && typeof result.then === "function") {
|
|
283
|
+
var timeoutMs = entry.maxHandlerMs;
|
|
284
|
+
var handlerName = name;
|
|
285
|
+
return safeAsync.withTimeout(result, timeoutMs,
|
|
286
|
+
{ name: opts.protocol + "/" + handlerName })
|
|
287
|
+
.catch(function (err) {
|
|
288
|
+
if (err && err.code === "async/timeout") {
|
|
289
|
+
throw new MailServerRegistryError("mail-server-registry/handler-timeout",
|
|
290
|
+
"dispatch: '" + handlerName + "' exceeded maxHandlerMs=" + timeoutMs + " (" +
|
|
291
|
+
(Date.now() - t0) + "ms elapsed)");
|
|
292
|
+
}
|
|
293
|
+
throw err;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
register: register,
|
|
301
|
+
unregister: unregister,
|
|
302
|
+
dispatch: dispatch,
|
|
303
|
+
list: list,
|
|
304
|
+
has: has,
|
|
305
|
+
source: source,
|
|
306
|
+
protocol: opts.protocol,
|
|
307
|
+
MailServerRegistryError: MailServerRegistryError,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = {
|
|
312
|
+
create: create,
|
|
313
|
+
CATALOGUE: CATALOGUE,
|
|
314
|
+
IMAP_VERBS: Object.keys(IMAP_VERBS),
|
|
315
|
+
JMAP_METHODS: Object.keys(JMAP_METHODS),
|
|
316
|
+
MANAGESIEVE_VERBS: Object.keys(MANAGESIEVE_VERBS),
|
|
317
|
+
MailServerRegistryError: MailServerRegistryError,
|
|
318
|
+
};
|
package/lib/mcp.js
CHANGED
|
@@ -192,8 +192,18 @@ function _checkRedirectUri(uri, allowlist, errorClass) {
|
|
|
192
192
|
"mcp: redirect_uri did not parse");
|
|
193
193
|
}
|
|
194
194
|
var isHttps = parsed.protocol === "https:";
|
|
195
|
-
|
|
196
|
-
|
|
195
|
+
// Strip the trailing root-zone dot before the reserved-name compare.
|
|
196
|
+
// RFC 1034 §3.1 — `localhost.` is the absolute form of `localhost`
|
|
197
|
+
// and resolves to the same target; without the strip, an attacker
|
|
198
|
+
// who supplies `localhost.` as the redirect_uri host slips past the
|
|
199
|
+
// local-allow path (the URL parser preserves the dot, the equality
|
|
200
|
+
// check fails, the URI gets routed as cleartext non-local — RFC 9700
|
|
201
|
+
// §4.1.1 bypass).
|
|
202
|
+
var rawHost = parsed.hostname || "";
|
|
203
|
+
while (rawHost.length > 0 && rawHost.charAt(rawHost.length - 1) === ".") {
|
|
204
|
+
rawHost = rawHost.slice(0, -1);
|
|
205
|
+
}
|
|
206
|
+
var isLocal = rawHost === "localhost" || rawHost === "127.0.0.1" || rawHost === "::1";
|
|
197
207
|
if (!isHttps && !isLocal) {
|
|
198
208
|
throw errorClass.factory("INSECURE_REDIRECT_URI",
|
|
199
209
|
"mcp: redirect_uri must be HTTPS (or localhost; RFC 9700 sec 4.1.1)");
|
package/lib/network-dns.js
CHANGED
|
@@ -54,6 +54,15 @@ var LOCAL_SUFFIXES = [".localhost", ".local", ".test", ".invalid",
|
|
|
54
54
|
".internal", ".intranet", ".lan", ".home", ".corp"];
|
|
55
55
|
function _isLocalFormHost(host) {
|
|
56
56
|
if (typeof host !== "string" || host.length === 0) return true;
|
|
57
|
+
// Strip the trailing root-zone dot BEFORE any reserved-name compare.
|
|
58
|
+
// RFC 1034 §3.1 — `foo.` is the absolute form of `foo` (both resolve
|
|
59
|
+
// to the same target). Without the strip, `localhost.` would slip
|
|
60
|
+
// past the reserved-form check and reach a public DoH/DoT provider
|
|
61
|
+
// that maps it to NXDOMAIN, which downstream consumers might then
|
|
62
|
+
// try to resolve via system fallback.
|
|
63
|
+
while (host.length > 0 && host.charAt(host.length - 1) === ".") {
|
|
64
|
+
host = host.slice(0, -1);
|
|
65
|
+
}
|
|
57
66
|
if (host === "localhost") return true;
|
|
58
67
|
// IP literal — skip DNS resolution entirely (caller passes through).
|
|
59
68
|
if (net.isIP(host)) return true;
|
package/lib/pagination.js
CHANGED
|
@@ -87,7 +87,8 @@ function _b64urlEncode(buf) { return bCrypto.toBase64Url(buf); }
|
|
|
87
87
|
|
|
88
88
|
function _b64urlDecode(s) {
|
|
89
89
|
if (typeof s !== "string") throw new PaginationError("pagination/bad-cursor", "cursor must be a string");
|
|
90
|
-
return bCrypto.fromBase64Url(s);
|
|
90
|
+
try { return bCrypto.fromBase64Url(s); }
|
|
91
|
+
catch (_e) { throw new PaginationError("pagination/bad-cursor", "cursor is not valid base64url"); }
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function _tag(secretBuf, stateJson) {
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.6",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:2bcbf942-6319-4d65-a08c-1e385ac35198",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-
|
|
8
|
+
"timestamp": "2026-05-18T04:40:34.689Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.10.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.10.11",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.10.
|
|
25
|
+
"version": "0.10.11",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.10.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.10.11",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.10.
|
|
57
|
+
"ref": "@blamejs/core@0.10.11",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|