@blamejs/core 0.8.51 → 0.8.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 +6 -0
- package/index.js +8 -0
- package/lib/audit.js +4 -0
- package/lib/auth/fido-mds3.js +624 -0
- package/lib/auth/passkey.js +214 -2
- package/lib/auth-bot-challenge.js +1 -1
- package/lib/credential-hash.js +2 -2
- package/lib/framework-error.js +55 -0
- package/lib/guard-cidr.js +2 -1
- package/lib/guard-jwt.js +2 -2
- package/lib/guard-oauth.js +2 -2
- package/lib/http-client-cache.js +916 -0
- package/lib/http-client.js +242 -0
- package/lib/local-db-thin.js +8 -7
- package/lib/mail-arf.js +343 -0
- package/lib/mail-auth.js +265 -40
- package/lib/mail-bimi.js +948 -33
- package/lib/mail-bounce.js +386 -4
- package/lib/mail-mdn.js +424 -0
- package/lib/mail-unsubscribe.js +265 -25
- package/lib/mail.js +403 -21
- package/lib/middleware/bearer-auth.js +1 -1
- package/lib/middleware/clear-site-data.js +122 -0
- package/lib/middleware/dpop.js +1 -1
- package/lib/middleware/index.js +9 -0
- package/lib/middleware/nel.js +214 -0
- package/lib/middleware/security-headers.js +56 -4
- package/lib/middleware/speculation-rules.js +323 -0
- package/lib/mime-parse.js +198 -0
- package/lib/network-dns.js +890 -27
- package/lib/network-tls.js +745 -0
- package/lib/object-store/sigv4.js +54 -0
- package/lib/public-suffix.js +414 -0
- package/lib/safe-buffer.js +7 -0
- package/lib/safe-json.js +1 -1
- package/lib/static.js +120 -0
- package/lib/storage.js +11 -0
- package/lib/vendor/MANIFEST.json +33 -0
- package/lib/vendor/bimi-trust-anchors.pem +33 -0
- package/lib/vendor/public-suffix-list.dat +16376 -0
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
package/lib/auth/passkey.js
CHANGED
|
@@ -121,20 +121,55 @@ async function verifyRegistration(opts) {
|
|
|
121
121
|
_requireString(opts.expectedOrigin, "expectedOrigin");
|
|
122
122
|
_requireString(opts.expectedRPID, "expectedRPID");
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
var rv = await _vendor().verifyRegistrationResponse({
|
|
125
125
|
response: opts.response,
|
|
126
126
|
expectedChallenge: opts.expectedChallenge,
|
|
127
127
|
expectedOrigin: opts.expectedOrigin,
|
|
128
128
|
expectedRPID: opts.expectedRPID,
|
|
129
129
|
requireUserVerification: opts.requireUserVerification !== false,
|
|
130
130
|
});
|
|
131
|
+
// WebAuthn L3 §6.1.3 — surface authenticator-data BE/BS flags as
|
|
132
|
+
// named fields. backupEligible (BE) signals the credential CAN be
|
|
133
|
+
// backed up to a cloud account; backupState (BS) signals it IS
|
|
134
|
+
// currently backed up. Operators key trust decisions on these
|
|
135
|
+
// (single-device passkey → require step-up; multi-device synced
|
|
136
|
+
// passkey → strong signal). The vendor parses authData and exposes
|
|
137
|
+
// credentialDeviceType ("singleDevice" | "multiDevice") and
|
|
138
|
+
// credentialBackedUp (boolean) on registrationInfo; we map them to
|
|
139
|
+
// the spec's flag names and add them to the top-level result so
|
|
140
|
+
// callers don't have to dig through registrationInfo.
|
|
141
|
+
if (rv && rv.registrationInfo) {
|
|
142
|
+
rv.backupEligible = rv.registrationInfo.credentialDeviceType === "multiDevice";
|
|
143
|
+
rv.backupState = rv.registrationInfo.credentialBackedUp === true;
|
|
144
|
+
} else {
|
|
145
|
+
rv = rv || {};
|
|
146
|
+
rv.backupEligible = false;
|
|
147
|
+
rv.backupState = false;
|
|
148
|
+
}
|
|
149
|
+
return rv;
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
// ---- Authentication ----
|
|
134
153
|
|
|
154
|
+
// startAuthentication accepts an optional `mediation` token that the
|
|
155
|
+
// caller passes through verbatim to the browser as
|
|
156
|
+
// `navigator.credentials.get({ publicKey, mediation })`. The descriptor
|
|
157
|
+
// itself doesn't carry mediation — it's a separate argument on the
|
|
158
|
+
// page — but startAuthentication echoes it onto the returned options
|
|
159
|
+
// so the operator's transport (typically a JSON GET) carries it to
|
|
160
|
+
// the page without losing the value. Allowed tokens per the W3C
|
|
161
|
+
// Credential Management spec: "silent" / "optional" / "required" /
|
|
162
|
+
// "conditional". "conditional" enables passkey autofill on
|
|
163
|
+
// <input autocomplete="webauthn">.
|
|
164
|
+
var ALLOWED_MEDIATION = { silent: 1, optional: 1, required: 1, conditional: 1 };
|
|
165
|
+
|
|
135
166
|
async function startAuthentication(opts) {
|
|
136
167
|
if (!opts) throw new AuthError("auth-passkey/missing-opts", "opts is required");
|
|
137
168
|
_requireString(opts.rpId, "rpId");
|
|
169
|
+
if (opts.mediation !== undefined && !ALLOWED_MEDIATION[opts.mediation]) {
|
|
170
|
+
throw new AuthError("auth-passkey/bad-mediation",
|
|
171
|
+
"mediation must be one of silent/optional/required/conditional");
|
|
172
|
+
}
|
|
138
173
|
|
|
139
174
|
var options = await _vendor().generateAuthenticationOptions({
|
|
140
175
|
rpID: opts.rpId,
|
|
@@ -148,9 +183,169 @@ async function startAuthentication(opts) {
|
|
|
148
183
|
} else {
|
|
149
184
|
options.hints = opts.hints;
|
|
150
185
|
}
|
|
186
|
+
if (opts.mediation !== undefined) {
|
|
187
|
+
options.mediation = opts.mediation;
|
|
188
|
+
}
|
|
189
|
+
return options;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// conditionalAuthOptions — convenience wrapper for the passkey-autofill
|
|
193
|
+
// flow (mediation: "conditional"). Browsers require an empty
|
|
194
|
+
// allowCredentials list, presence-only userVerification (so the
|
|
195
|
+
// autofill chip can surface without forcing biometric), and a present
|
|
196
|
+
// challenge. Returns an object shaped for
|
|
197
|
+
// `navigator.credentials.get({ publicKey: <opts>, mediation: "conditional" })`.
|
|
198
|
+
async function conditionalAuthOptions(opts) {
|
|
199
|
+
if (!opts) throw new AuthError("auth-passkey/missing-opts", "opts is required");
|
|
200
|
+
_requireString(opts.rpId, "rpId");
|
|
201
|
+
|
|
202
|
+
var options = await _vendor().generateAuthenticationOptions({
|
|
203
|
+
rpID: opts.rpId,
|
|
204
|
+
// For conditional UI the spec mandates an empty allowCredentials
|
|
205
|
+
// list — discoverable credentials only. Supplying a list here
|
|
206
|
+
// suppresses the autofill chip in current browsers.
|
|
207
|
+
allowCredentials: [],
|
|
208
|
+
userVerification: opts.userVerification || "preferred",
|
|
209
|
+
timeout: opts.timeout,
|
|
210
|
+
extensions: opts.extensions,
|
|
211
|
+
});
|
|
212
|
+
options.mediation = "conditional";
|
|
213
|
+
if (!opts.hints) {
|
|
214
|
+
options.hints = ["client-device", "hybrid"];
|
|
215
|
+
} else {
|
|
216
|
+
options.hints = opts.hints;
|
|
217
|
+
}
|
|
151
218
|
return options;
|
|
152
219
|
}
|
|
153
220
|
|
|
221
|
+
// ---- WebAuthn L3 extension helpers (PRF / largeBlob / credBlob) ----
|
|
222
|
+
//
|
|
223
|
+
// Pre-compute the spec-correct shape so callers don't have to remember
|
|
224
|
+
// (a) what the field is called this year, (b) which inputs travel as
|
|
225
|
+
// base64url vs Uint8Array, (c) which support the {support:"required"}
|
|
226
|
+
// contract. Validation tier: throw at config-time. Misuse here is a
|
|
227
|
+
// coding bug, not a request-shape thing.
|
|
228
|
+
|
|
229
|
+
function _b64urlExtInput(value, name) {
|
|
230
|
+
// Accept a base64url string OR a Buffer / Uint8Array. Normalize the
|
|
231
|
+
// wire shape to base64url (the JSON descriptor ships base64url; the
|
|
232
|
+
// browser turns it into an ArrayBuffer before passing to the
|
|
233
|
+
// authenticator).
|
|
234
|
+
if (typeof value === "string") {
|
|
235
|
+
if (value.length === 0 || !safeBuffer.BASE64URL_RE.test(value)) {
|
|
236
|
+
throw new AuthError("auth-passkey/bad-extension-input",
|
|
237
|
+
name + " must be base64url (no padding) when string");
|
|
238
|
+
}
|
|
239
|
+
return value;
|
|
240
|
+
}
|
|
241
|
+
if (Buffer.isBuffer(value)) {
|
|
242
|
+
return value.toString("base64url");
|
|
243
|
+
}
|
|
244
|
+
if (value instanceof Uint8Array) {
|
|
245
|
+
return Buffer.from(value).toString("base64url");
|
|
246
|
+
}
|
|
247
|
+
throw new AuthError("auth-passkey/bad-extension-input",
|
|
248
|
+
name + " must be base64url string, Buffer, or Uint8Array");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// PRF (Pseudo-Random Function) extension — WebAuthn L3 §10.1.2.
|
|
252
|
+
// Authenticator-bound HKDF source. eval inputs are 32-byte salts; the
|
|
253
|
+
// authenticator returns deterministic 32-byte outputs the operator
|
|
254
|
+
// uses as a key-encryption key (vault unlock, file-encryption seed).
|
|
255
|
+
// Shape: `{ prf: { eval: { first, second? } } }` per extension-id "prf".
|
|
256
|
+
function _prfExt(args) {
|
|
257
|
+
if (!args || !args.eval) {
|
|
258
|
+
throw new AuthError("auth-passkey/missing-eval",
|
|
259
|
+
"extensions.prf({ eval: { first, second? } }) is required");
|
|
260
|
+
}
|
|
261
|
+
if (args.eval.first === undefined || args.eval.first === null) {
|
|
262
|
+
throw new AuthError("auth-passkey/missing-prf-first",
|
|
263
|
+
"extensions.prf eval.first is required");
|
|
264
|
+
}
|
|
265
|
+
var out = { prf: { eval: { first: _b64urlExtInput(args.eval.first, "eval.first") } } };
|
|
266
|
+
if (args.eval.second !== undefined && args.eval.second !== null) {
|
|
267
|
+
out.prf.eval.second = _b64urlExtInput(args.eval.second, "eval.second");
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// largeBlob extension — WebAuthn L3 §10.3.
|
|
273
|
+
// Per-credential opaque blob storage. At registration the operator
|
|
274
|
+
// asks for support: "preferred" | "required". At auth time the
|
|
275
|
+
// operator asks to read OR write, never both in the same assertion.
|
|
276
|
+
function _largeBlobExt(args) {
|
|
277
|
+
if (!args) {
|
|
278
|
+
throw new AuthError("auth-passkey/missing-largeblob",
|
|
279
|
+
"extensions.largeBlob({ support? | read? | write? }) is required");
|
|
280
|
+
}
|
|
281
|
+
var out = { largeBlob: {} };
|
|
282
|
+
var SUPPORT = { preferred: 1, required: 1 };
|
|
283
|
+
var modes = 0;
|
|
284
|
+
if (args.support !== undefined) {
|
|
285
|
+
if (!SUPPORT[args.support]) {
|
|
286
|
+
throw new AuthError("auth-passkey/bad-largeblob-support",
|
|
287
|
+
"extensions.largeBlob support must be 'preferred' or 'required'");
|
|
288
|
+
}
|
|
289
|
+
out.largeBlob.support = args.support;
|
|
290
|
+
modes++;
|
|
291
|
+
}
|
|
292
|
+
if (args.read === true) {
|
|
293
|
+
out.largeBlob.read = true;
|
|
294
|
+
modes++;
|
|
295
|
+
} else if (args.read !== undefined && args.read !== false) {
|
|
296
|
+
throw new AuthError("auth-passkey/bad-largeblob-read",
|
|
297
|
+
"extensions.largeBlob read must be a boolean");
|
|
298
|
+
}
|
|
299
|
+
if (args.write !== undefined && args.write !== null) {
|
|
300
|
+
if (!Buffer.isBuffer(args.write) && !(args.write instanceof Uint8Array)) {
|
|
301
|
+
throw new AuthError("auth-passkey/bad-largeblob-write",
|
|
302
|
+
"extensions.largeBlob write must be a Uint8Array / Buffer");
|
|
303
|
+
}
|
|
304
|
+
out.largeBlob.write = Buffer.from(args.write).toString("base64url");
|
|
305
|
+
modes++;
|
|
306
|
+
}
|
|
307
|
+
if (modes === 0) {
|
|
308
|
+
throw new AuthError("auth-passkey/empty-largeblob",
|
|
309
|
+
"extensions.largeBlob({}) needs support, read, or write");
|
|
310
|
+
}
|
|
311
|
+
if (args.read === true && args.write !== undefined && args.write !== null) {
|
|
312
|
+
throw new AuthError("auth-passkey/conflicting-largeblob",
|
|
313
|
+
"extensions.largeBlob — read and write are mutually exclusive");
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// credBlob extension — WebAuthn L3 §10.5.
|
|
319
|
+
// Server-supplied opaque blob (≤32 bytes per CTAP2.1) bound to the
|
|
320
|
+
// credential at registration. Returned in subsequent assertions.
|
|
321
|
+
// Shape: `{ credBlob: <base64url> }`.
|
|
322
|
+
function _credBlobExt(args) {
|
|
323
|
+
if (!args || args.blob === undefined || args.blob === null) {
|
|
324
|
+
throw new AuthError("auth-passkey/missing-credblob",
|
|
325
|
+
"extensions.credBlob({ blob }) is required");
|
|
326
|
+
}
|
|
327
|
+
var buf;
|
|
328
|
+
if (Buffer.isBuffer(args.blob)) {
|
|
329
|
+
buf = args.blob;
|
|
330
|
+
} else if (args.blob instanceof Uint8Array) {
|
|
331
|
+
buf = Buffer.from(args.blob);
|
|
332
|
+
} else {
|
|
333
|
+
throw new AuthError("auth-passkey/bad-credblob",
|
|
334
|
+
"extensions.credBlob blob must be a Uint8Array / Buffer");
|
|
335
|
+
}
|
|
336
|
+
if (buf.length === 0 || buf.length > 32) { // allow:raw-byte-literal — CTAP2.1 §11.1 credBlob max
|
|
337
|
+
throw new AuthError("auth-passkey/credblob-bad-length",
|
|
338
|
+
"extensions.credBlob blob must be 1-32 bytes (CTAP2.1 §11.1)");
|
|
339
|
+
}
|
|
340
|
+
return { credBlob: buf.toString("base64url") };
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
var extensions = {
|
|
344
|
+
prf: _prfExt,
|
|
345
|
+
largeBlob: _largeBlobExt,
|
|
346
|
+
credBlob: _credBlobExt,
|
|
347
|
+
};
|
|
348
|
+
|
|
154
349
|
async function verifyAuthentication(opts) {
|
|
155
350
|
if (!opts) throw new AuthError("auth-passkey/missing-opts", "opts is required");
|
|
156
351
|
if (!opts.response) {
|
|
@@ -164,7 +359,7 @@ async function verifyAuthentication(opts) {
|
|
|
164
359
|
"opts.credential { id, publicKey, counter? } is required");
|
|
165
360
|
}
|
|
166
361
|
|
|
167
|
-
|
|
362
|
+
var rv = await _vendor().verifyAuthenticationResponse({
|
|
168
363
|
response: opts.response,
|
|
169
364
|
expectedChallenge: opts.expectedChallenge,
|
|
170
365
|
expectedOrigin: opts.expectedOrigin,
|
|
@@ -177,6 +372,21 @@ async function verifyAuthentication(opts) {
|
|
|
177
372
|
},
|
|
178
373
|
requireUserVerification: opts.requireUserVerification !== false,
|
|
179
374
|
});
|
|
375
|
+
// WebAuthn L3 §6.1.3 — same BE/BS surfacing as verifyRegistration.
|
|
376
|
+
// Authentication assertions also carry the BE/BS bits in authData; a
|
|
377
|
+
// credential that registered as single-device but later asserts as
|
|
378
|
+
// multi-device (or vice versa) is a backup-state-changed signal worth
|
|
379
|
+
// auditing at the operator level. We expose the current values so the
|
|
380
|
+
// caller can compare against what they persisted at registration.
|
|
381
|
+
if (rv && rv.authenticationInfo) {
|
|
382
|
+
rv.backupEligible = rv.authenticationInfo.credentialDeviceType === "multiDevice";
|
|
383
|
+
rv.backupState = rv.authenticationInfo.credentialBackedUp === true;
|
|
384
|
+
} else {
|
|
385
|
+
rv = rv || {};
|
|
386
|
+
rv.backupEligible = false;
|
|
387
|
+
rv.backupState = false;
|
|
388
|
+
}
|
|
389
|
+
return rv;
|
|
180
390
|
}
|
|
181
391
|
|
|
182
392
|
// ---- WebAuthn Signal API (W3C draft, 2024) ----
|
|
@@ -265,6 +475,8 @@ module.exports = {
|
|
|
265
475
|
verifyRegistration: verifyRegistration,
|
|
266
476
|
startAuthentication: startAuthentication,
|
|
267
477
|
verifyAuthentication: verifyAuthentication,
|
|
478
|
+
conditionalAuthOptions: conditionalAuthOptions,
|
|
479
|
+
extensions: extensions,
|
|
268
480
|
signalUnknownCredential: signalUnknownCredential,
|
|
269
481
|
signalAllAcceptedCredentials: signalAllAcceptedCredentials,
|
|
270
482
|
signalCurrentUserDetails: signalCurrentUserDetails,
|
|
@@ -136,7 +136,7 @@ function _defaultKeyExtractor(req) {
|
|
|
136
136
|
* @signature b.authBotChallenge.create(opts)
|
|
137
137
|
* @since 0.8.48
|
|
138
138
|
* @status stable
|
|
139
|
-
* @related b.middleware.botGuard
|
|
139
|
+
* @related b.middleware.botGuard
|
|
140
140
|
*
|
|
141
141
|
* Build an adaptive bot-challenge gate. Returns
|
|
142
142
|
* `{ middleware, recordFailure, recordSuccess, check, reset }`.
|
package/lib/credential-hash.js
CHANGED
|
@@ -173,7 +173,7 @@ function _decodeEnvelope(env) {
|
|
|
173
173
|
* @since 0.2.28
|
|
174
174
|
* @status stable
|
|
175
175
|
* @compliance pci-dss, soc2, hipaa
|
|
176
|
-
* @related b.credentialHash.verify, b.credentialHash.needsRehash
|
|
176
|
+
* @related b.credentialHash.verify, b.credentialHash.needsRehash
|
|
177
177
|
*
|
|
178
178
|
* Hash a credential secret into a base64 envelope ready for storage in
|
|
179
179
|
* a `credentialHash` column. Default algorithm is SHAKE256 with a
|
|
@@ -345,7 +345,7 @@ function inspect(envelope) {
|
|
|
345
345
|
* @signature b.credentialHash.needsRehash(envelope, opts?)
|
|
346
346
|
* @since 0.2.28
|
|
347
347
|
* @status stable
|
|
348
|
-
* @related b.credentialHash.hash, b.credentialHash.verify
|
|
348
|
+
* @related b.credentialHash.hash, b.credentialHash.verify
|
|
349
349
|
*
|
|
350
350
|
* Returns `true` when the stored envelope was produced under an
|
|
351
351
|
* algorithm or parameter set that no longer matches the framework
|
package/lib/framework-error.js
CHANGED
|
@@ -378,6 +378,19 @@ var SmtpPolicyError = defineClass("SmtpPolicyError", { alwaysPermane
|
|
|
378
378
|
// record shape, fetch failures, missing keys, alignment issues.
|
|
379
379
|
// Permanent — DNS-config / message-shape errors, not transient.
|
|
380
380
|
var MailAuthError = defineClass("MailAuthError", { alwaysPermanent: true });
|
|
381
|
+
// MailArfError covers RFC 5965 Abuse Reporting Format ingest failures:
|
|
382
|
+
// missing required Feedback-Type / User-Agent fields, malformed
|
|
383
|
+
// multipart/report, message/feedback-report MIME-type mismatch, parse
|
|
384
|
+
// errors. Permanent — the report shape is operator-supplied input.
|
|
385
|
+
var MailArfError = defineClass("MailArfError", { alwaysPermanent: true });
|
|
386
|
+
// MailBimiError covers RFC 9091 BIMI VMC / CMC chain validation
|
|
387
|
+
// + Tiny-PS SVG profile violations: VMC fetch failures, X.509 chain
|
|
388
|
+
// validation failures, subjectAltName URI / BIMI domain mismatch,
|
|
389
|
+
// missing BIMI policy OID (1.3.6.1.5.5.7.3.31 mark verification),
|
|
390
|
+
// Tiny-PS SVG profile violations (root, version, baseProfile, scripts,
|
|
391
|
+
// external refs, viewBox, byte cap). Permanent — every case is a
|
|
392
|
+
// brand / certificate / asset shape error.
|
|
393
|
+
var MailBimiError = defineClass("MailBimiError", { alwaysPermanent: true });
|
|
381
394
|
// SseError covers Server-Sent Events stream-shape violations: newline
|
|
382
395
|
// or CR or NUL injection in event:/id:/data: fields (CVE-2026-33128
|
|
383
396
|
// h3, CVE-2026-29085 Hono, CVE-2026-44217 sse-channel — newline in
|
|
@@ -553,10 +566,47 @@ var DaemonError = defineClass("DaemonError", { alwaysPermane
|
|
|
553
566
|
// framework refuses to coerce. Operators wrap the call in their own
|
|
554
567
|
// retry policy when polling against a flaky CDN.
|
|
555
568
|
var SelfUpdateError = defineClass("SelfUpdateError", { alwaysPermanent: true });
|
|
569
|
+
// MailUnsubscribeError — b.mail.unsubscribe (lib/mail-unsubscribe.js).
|
|
570
|
+
// RFC 8058 / RFC 2369 / RFC 2919 List-* header builder violations:
|
|
571
|
+
// non-https URL in url/help/archive, non-mailto in mailto/owner,
|
|
572
|
+
// invalid list-id shape per RFC 2919 §3, control bytes / over-length
|
|
573
|
+
// header values. alwaysPermanent — every case is operator-misconfig
|
|
574
|
+
// at config-time the framework refuses to coerce.
|
|
575
|
+
var MailUnsubscribeError = defineClass("MailUnsubscribeError", { alwaysPermanent: true });
|
|
576
|
+
// FidoMds3Error — b.auth.fidoMds3 (lib/auth/fido-mds3.js). FIDO MDS3
|
|
577
|
+
// metadata BLOB verification + AAGUID lookup violations: BLOB fetch
|
|
578
|
+
// failure (non-2xx, oversize, network), JWS shape mismatch, certificate
|
|
579
|
+
// chain validation failure against the FIDO Alliance MDS3 root,
|
|
580
|
+
// signature verification failure, payload schema violation
|
|
581
|
+
// (missing entries / nextUpdate / no), nextUpdate parse failure,
|
|
582
|
+
// AAGUID lookup against an authenticator carrying a REVOKED /
|
|
583
|
+
// USER_KEY_PHYSICAL_COMPROMISE / USER_KEY_REMOTE_COMPROMISE status
|
|
584
|
+
// report. alwaysPermanent — every case is configuration / network /
|
|
585
|
+
// signing-shape errors that retry alone won't recover.
|
|
586
|
+
var FidoMds3Error = defineClass("FidoMds3Error", { alwaysPermanent: true });
|
|
587
|
+
// PublicSuffixError — b.publicSuffix (lib/public-suffix.js). Bad
|
|
588
|
+
// domain input at lookup time (non-string, empty, overlong, control-
|
|
589
|
+
// byte-bearing, IDN-normalization failure) and missing-vendored-data
|
|
590
|
+
// at module-init are both alwaysPermanent — every case is operator-
|
|
591
|
+
// shaped (caller passed garbage) or packaging-shaped (vendored .dat
|
|
592
|
+
// missing). Codes: `public-suffix/invalid-domain`,
|
|
593
|
+
// `public-suffix/not-loaded`.
|
|
594
|
+
var PublicSuffixError = defineClass("PublicSuffixError", { alwaysPermanent: true });
|
|
595
|
+
// MailMdnError — b.mailMdn (lib/mail-mdn.js). RFC 3798 / RFC 8098
|
|
596
|
+
// Message Disposition Notification builder + parser violations: bad
|
|
597
|
+
// opts at build/parse, malformed multipart/report shape, missing
|
|
598
|
+
// required fields (Original-Recipient / Final-Recipient / Disposition),
|
|
599
|
+
// disposition / action-mode / sending-mode token allowlist drift,
|
|
600
|
+
// auto-generation refusal when the inbound message demanded user
|
|
601
|
+
// confirmation (RFC 3798 §2.1) and the operator did not opt in.
|
|
602
|
+
// alwaysPermanent — every case is operator-shape or message-shape
|
|
603
|
+
// errors that retry will not recover.
|
|
604
|
+
var MailMdnError = defineClass("MailMdnError", { alwaysPermanent: true });
|
|
556
605
|
|
|
557
606
|
module.exports = {
|
|
558
607
|
FrameworkError: FrameworkError,
|
|
559
608
|
defineClass: defineClass,
|
|
609
|
+
MailUnsubscribeError: MailUnsubscribeError,
|
|
560
610
|
ObjectStoreError: ObjectStoreError,
|
|
561
611
|
LogStreamError: LogStreamError,
|
|
562
612
|
QueueError: QueueError,
|
|
@@ -613,6 +663,8 @@ module.exports = {
|
|
|
613
663
|
ComplianceError: ComplianceError,
|
|
614
664
|
SmtpPolicyError: SmtpPolicyError,
|
|
615
665
|
MailAuthError: MailAuthError,
|
|
666
|
+
MailArfError: MailArfError,
|
|
667
|
+
MailBimiError: MailBimiError,
|
|
616
668
|
SseError: SseError,
|
|
617
669
|
McpError: McpError,
|
|
618
670
|
AiInputError: AiInputError,
|
|
@@ -641,4 +693,7 @@ module.exports = {
|
|
|
641
693
|
ArgParserError: ArgParserError,
|
|
642
694
|
DaemonError: DaemonError,
|
|
643
695
|
SelfUpdateError: SelfUpdateError,
|
|
696
|
+
FidoMds3Error: FidoMds3Error,
|
|
697
|
+
PublicSuffixError: PublicSuffixError,
|
|
698
|
+
MailMdnError: MailMdnError,
|
|
644
699
|
};
|
package/lib/guard-cidr.js
CHANGED
|
@@ -48,6 +48,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
48
48
|
var gateContract = require("./gate-contract");
|
|
49
49
|
var C = require("./constants");
|
|
50
50
|
var numericBounds = require("./numeric-bounds");
|
|
51
|
+
var safeBuffer = require("./safe-buffer");
|
|
51
52
|
var { GuardCidrError } = require("./framework-error");
|
|
52
53
|
|
|
53
54
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
@@ -202,7 +203,7 @@ function _parseIpv6(s) {
|
|
|
202
203
|
var out = [];
|
|
203
204
|
for (var i = 0; i < parts.length; i += 1) {
|
|
204
205
|
var p = parts[i];
|
|
205
|
-
if (
|
|
206
|
+
if (!safeBuffer.IPV6_HEXTET_RE.test(p)) return null;
|
|
206
207
|
out.push(p.toLowerCase());
|
|
207
208
|
}
|
|
208
209
|
return out;
|
package/lib/guard-jwt.js
CHANGED
|
@@ -437,7 +437,7 @@ function _detectIssues(input, opts) {
|
|
|
437
437
|
* @since 0.7.49
|
|
438
438
|
* @status stable
|
|
439
439
|
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
440
|
-
* @related b.guardJwt.sanitize, b.guardJwt.gate
|
|
440
|
+
* @related b.guardJwt.sanitize, b.guardJwt.gate
|
|
441
441
|
*
|
|
442
442
|
* Apply the full guard-jwt threat catalog to a JWT compact-
|
|
443
443
|
* serialization string. Returns `{ ok, issues, refusal? }` per
|
|
@@ -697,7 +697,7 @@ var loadRulePack = _jwtRulePacks.load;
|
|
|
697
697
|
* @since 0.7.49
|
|
698
698
|
* @status stable
|
|
699
699
|
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
700
|
-
* @related b.guardJwt.validate
|
|
700
|
+
* @related b.guardJwt.validate
|
|
701
701
|
*
|
|
702
702
|
* Throw on any `kid` value that contains path-traversal indicators
|
|
703
703
|
* (`..`, `/`, `\`, percent-encoded variants) or non-printable
|
package/lib/guard-oauth.js
CHANGED
|
@@ -349,7 +349,7 @@ function _detectIssues(flow, opts) {
|
|
|
349
349
|
* @since 0.7.49
|
|
350
350
|
* @status stable
|
|
351
351
|
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
352
|
-
* @related b.guardOauth.sanitize, b.guardOauth.gate
|
|
352
|
+
* @related b.guardOauth.sanitize, b.guardOauth.gate
|
|
353
353
|
*
|
|
354
354
|
* Apply the full guard-oauth threat catalog to a flow bundle.
|
|
355
355
|
* Returns `{ ok, issues, refusal? }` per
|
|
@@ -461,7 +461,7 @@ function sanitize(input, opts) {
|
|
|
461
461
|
* @since 0.7.49
|
|
462
462
|
* @status stable
|
|
463
463
|
* @compliance hipaa, pci-dss, gdpr, soc2
|
|
464
|
-
* @related b.guardOauth.validate, b.guardOauth.sanitize
|
|
464
|
+
* @related b.guardOauth.validate, b.guardOauth.sanitize
|
|
465
465
|
*
|
|
466
466
|
* Build a `gateContract.buildGuardGate`-shaped gate that pulls
|
|
467
467
|
* `ctx.oauthFlow` (or `ctx.flow`) and dispatches to `validate`.
|