@blamejs/core 0.14.20 → 0.14.22

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/index.js +5 -1
  3. package/lib/auth/jar.js +190 -28
  4. package/lib/auth/jwt-external.js +213 -0
  5. package/lib/auth/oauth.js +115 -101
  6. package/lib/auth/oid4vci.js +124 -5
  7. package/lib/auth/oid4vp.js +14 -4
  8. package/lib/break-glass.js +1 -2
  9. package/lib/config.js +28 -31
  10. package/lib/dora.js +8 -5
  11. package/lib/dsr.js +2 -2
  12. package/lib/flag-evaluation-context.js +7 -0
  13. package/lib/guard-html-wcag-aria.js +4 -2
  14. package/lib/guard-html-wcag-forms.js +4 -2
  15. package/lib/guard-html-wcag-tables.js +4 -2
  16. package/lib/guard-html-wcag-tagwalk.js +20 -0
  17. package/lib/guard-html-wcag.js +1 -1
  18. package/lib/honeytoken.js +27 -20
  19. package/lib/http-client.js +3 -4
  20. package/lib/lro.js +3 -4
  21. package/lib/mail-deploy.js +1 -1
  22. package/lib/mail-send-deliver.js +13 -4
  23. package/lib/middleware/api-encrypt.js +140 -13
  24. package/lib/middleware/asyncapi-serve.js +3 -0
  25. package/lib/middleware/csp-report.js +13 -9
  26. package/lib/middleware/deny-response.js +2 -10
  27. package/lib/middleware/health.js +1 -4
  28. package/lib/middleware/openapi-serve.js +3 -0
  29. package/lib/middleware/scim-server.js +297 -19
  30. package/lib/middleware/security-txt.js +1 -2
  31. package/lib/middleware/trace-log-correlation.js +4 -8
  32. package/lib/network-smtp-policy.js +4 -4
  33. package/lib/object-store/sigv4-bucket-ops.js +11 -2
  34. package/lib/observability-tracer.js +1 -1
  35. package/lib/problem-details.js +56 -11
  36. package/lib/pubsub-cluster.js +16 -3
  37. package/lib/queue-sqs.js +20 -2
  38. package/lib/redis-client.js +32 -4
  39. package/lib/safe-redirect.js +16 -2
  40. package/lib/validate-opts.js +34 -0
  41. package/package.json +1 -1
  42. package/sbom.cdx.json +6 -6
package/lib/queue-sqs.js CHANGED
@@ -50,6 +50,7 @@ var httpClient = require("./http-client");
50
50
  var cryptoField = require("./crypto-field");
51
51
  var safeJson = require("./safe-json");
52
52
  var safeUrl = require("./safe-url");
53
+ var validateOpts = require("./validate-opts");
53
54
  var { generateToken } = require("./crypto");
54
55
  var { QueueError } = require("./framework-error");
55
56
 
@@ -102,8 +103,25 @@ function create(opts) {
102
103
  var accountId = opts.accountId ? String(opts.accountId) : null;
103
104
  var timeoutMs = opts.timeoutMs;
104
105
  var allowInternal = opts.allowInternal != null ? opts.allowInternal : null;
105
- var visibilityTimeoutSec = Number(opts.visibilityTimeoutSec) || DEFAULT_VISIBILITY_TIMEOUT_SEC;
106
- var waitTimeSec = Number(opts.waitTimeSec) || DEFAULT_WAIT_TIME_SEC;
106
+ // Config-time: a typo (NaN-coercing string / negative / fractional)
107
+ // must surface at create, not silently fall back to the default and ship
108
+ // a mis-tuned lease loop. THROW on present-but-bad; absent keeps default.
109
+ validateOpts.optionalPositiveInt(opts.visibilityTimeoutSec,
110
+ "queue-sqs: visibilityTimeoutSec", QueueError, "INVALID_CONFIG");
111
+ // waitTimeSec=0 is the valid SQS short-poll sentinel (the default), so a
112
+ // positive-int check would wrongly reject it — allow non-negative integers.
113
+ if (opts.waitTimeSec !== undefined &&
114
+ (typeof opts.waitTimeSec !== "number" || !isFinite(opts.waitTimeSec) ||
115
+ opts.waitTimeSec < 0 || Math.floor(opts.waitTimeSec) !== opts.waitTimeSec)) {
116
+ throw _err("INVALID_CONFIG",
117
+ "queue-sqs: waitTimeSec must be a non-negative integer (0 = short-poll), got " +
118
+ (typeof opts.waitTimeSec === "number" ? String(opts.waitTimeSec) : typeof opts.waitTimeSec),
119
+ true);
120
+ }
121
+ var visibilityTimeoutSec = opts.visibilityTimeoutSec !== undefined
122
+ ? opts.visibilityTimeoutSec : DEFAULT_VISIBILITY_TIMEOUT_SEC;
123
+ var waitTimeSec = opts.waitTimeSec !== undefined
124
+ ? opts.waitTimeSec : DEFAULT_WAIT_TIME_SEC;
107
125
 
108
126
  var queueUrlResolver = typeof opts.queueUrlByName === "function"
109
127
  ? opts.queueUrlByName
@@ -169,11 +169,36 @@ function create(opts) {
169
169
  var useTls = opts.tls !== undefined ? !!opts.tls : parsed.tls;
170
170
  var password = opts.password !== undefined ? opts.password : parsed.password;
171
171
  var username = opts.username !== undefined ? opts.username : parsed.username;
172
- var db = opts.db !== undefined ? Number(opts.db) : parsed.db;
173
- var connectTimeoutMs = Number(opts.connectTimeoutMs) || 5000;
174
- var commandTimeoutMs = Number(opts.commandTimeoutMs) || 10000;
172
+ // Config-time entry-point opts: a bad type must fail at create() rather
173
+ // than coerce-or-default silently. connectTimeoutMs:"abc" NaN would
174
+ // otherwise fall through to the default; a negative timeout would sail
175
+ // into setTimeout; maxReconnectAttempts:"abc" → NaN would make the
176
+ // `>= 0` reconnect-cap check below false and SILENTLY disable the bound
177
+ // (unbounded reconnects). db and maxReconnectAttempts must allow 0
178
+ // (db 0 = no SELECT; maxReconnectAttempts 0 = give up immediately).
179
+ if (opts.db !== undefined &&
180
+ (typeof opts.db !== "number" || !Number.isInteger(opts.db) || opts.db < 0)) {
181
+ throw _err("BAD_OPTS",
182
+ "redis.create: opts.db must be a non-negative integer, got " +
183
+ (typeof opts.db === "number" ? String(opts.db) : typeof opts.db));
184
+ }
185
+ if (opts.maxReconnectAttempts !== undefined &&
186
+ (typeof opts.maxReconnectAttempts !== "number" ||
187
+ !Number.isInteger(opts.maxReconnectAttempts) || opts.maxReconnectAttempts < 0)) {
188
+ throw _err("BAD_OPTS",
189
+ "redis.create: opts.maxReconnectAttempts must be a non-negative integer, got " +
190
+ (typeof opts.maxReconnectAttempts === "number"
191
+ ? String(opts.maxReconnectAttempts) : typeof opts.maxReconnectAttempts));
192
+ }
193
+ validateOpts.optionalPositiveInt(opts.connectTimeoutMs,
194
+ "redis.create: opts.connectTimeoutMs", RedisError, "BAD_OPTS");
195
+ validateOpts.optionalPositiveInt(opts.commandTimeoutMs,
196
+ "redis.create: opts.commandTimeoutMs", RedisError, "BAD_OPTS");
197
+ var db = opts.db !== undefined ? opts.db : parsed.db;
198
+ var connectTimeoutMs = opts.connectTimeoutMs !== undefined ? opts.connectTimeoutMs : 5000;
199
+ var commandTimeoutMs = opts.commandTimeoutMs !== undefined ? opts.commandTimeoutMs : 10000;
175
200
  var maxReconnectAttempts = opts.maxReconnectAttempts === undefined ? 10
176
- : Number(opts.maxReconnectAttempts);
201
+ : opts.maxReconnectAttempts;
177
202
  // TLS verification controls. Operators using rediss:// against private
178
203
  // CAs (managed Redis services, on-prem clusters with internal PKI)
179
204
  // pin the trust roots via opts.ca; rejectUnauthorized stays on by
@@ -470,6 +495,9 @@ function create(opts) {
470
495
  pending: pending.length, backlog: backlog.length,
471
496
  reconnect: reconnectAttempt,
472
497
  host: host, port: port, db: db, tls: useTls,
498
+ connectTimeoutMs: connectTimeoutMs,
499
+ commandTimeoutMs: commandTimeoutMs,
500
+ maxReconnectAttempts: maxReconnectAttempts,
473
501
  };
474
502
  },
475
503
  };
@@ -76,8 +76,21 @@ function resolve(rawTarget, opts) {
76
76
  // Full URL — parse and check against allowlist.
77
77
  var allowedOrigins = Array.isArray(opts.allowedOrigins) ? opts.allowedOrigins : null;
78
78
  var allowedHosts = Array.isArray(opts.allowedHosts) ? opts.allowedHosts : null;
79
- if (!allowedOrigins && !allowedHosts) {
80
- // Operator gave no allowlist refuse all full URLs (the safe default).
79
+
80
+ // The application's own origin (opts.base) is same-origin by
81
+ // definition, so a full URL pointing at it is safe even when the
82
+ // operator supplied no explicit allowedOrigins / allowedHosts. Derive
83
+ // the origin from base and treat it as an implicitly-allowed origin.
84
+ var baseOrigin = null;
85
+ if (typeof opts.base === "string" && opts.base.length > 0) {
86
+ try {
87
+ baseOrigin = safeUrl.parse(opts.base, { allowedProtocols: safeUrl.ALLOW_HTTP_TLS }).origin;
88
+ } catch (_e) { baseOrigin = null; }
89
+ }
90
+
91
+ if (!allowedOrigins && !allowedHosts && baseOrigin === null) {
92
+ // Operator gave no allowlist and no usable base — refuse all full
93
+ // URLs (the safe default).
81
94
  return fallback;
82
95
  }
83
96
 
@@ -85,6 +98,7 @@ function resolve(rawTarget, opts) {
85
98
  try { parsed = safeUrl.parse(rawTarget, { allowedProtocols: safeUrl.ALLOW_HTTP_TLS }); }
86
99
  catch (_e) { return fallback; }
87
100
 
101
+ if (baseOrigin !== null && parsed.origin === baseOrigin) return rawTarget;
88
102
  if (allowedOrigins) {
89
103
  for (var i = 0; i < allowedOrigins.length; i += 1) {
90
104
  if (parsed.origin === allowedOrigins[i]) return rawTarget;
@@ -393,6 +393,39 @@ function makeNamespacedEmitters(prefix, deps) {
393
393
  return { audit: audit, metric: metric };
394
394
  }
395
395
 
396
+ // assignOwnEnumerable — copy a source object's own enumerable keys onto a
397
+ // target, skipping the prototype-pollution sentinels (__proto__ /
398
+ // constructor / prototype) and any caller-named reserved keys. Several
399
+ // primitives that merge operator-supplied free-form fields onto a
400
+ // spec-built object (JOSE claim sets, JWS protected headers, attestation
401
+ // extra-claims) previously open-coded the identical
402
+ // `for (k of Object.keys(src)) { if (sentinel) continue; if (reserved)
403
+ // continue; dst[k] = src[k]; }` loop. Centralizing the proto-safe walk
404
+ // keeps the merge contract in one place. Reserved keys win — they are NOT
405
+ // overwritten — so the caller's spec-built fields can never be shadowed by
406
+ // a same-named operator key. Returns the target.
407
+ function assignOwnEnumerable(target, source, reservedKeys) {
408
+ if (!source || typeof source !== "object") return target;
409
+ var reserved = Object.create(null);
410
+ if (reservedKeys) for (var r = 0; r < reservedKeys.length; r += 1) reserved[reservedKeys[r]] = true;
411
+ var keys = Object.keys(source);
412
+ var entries = [];
413
+ for (var i = 0; i < keys.length; i += 1) {
414
+ var k = keys[i];
415
+ if (k === "__proto__" || k === "constructor" || k === "prototype") continue;
416
+ if (reserved[k]) continue;
417
+ entries.push([k, source[k]]);
418
+ }
419
+ // Staged through entries + Object.assign so the copy contains no
420
+ // computed-name property write at all: Object.fromEntries creates own
421
+ // data properties (it cannot walk the prototype chain), and the
422
+ // sentinel skip above means the staging object carries no
423
+ // __proto__/constructor/prototype key for Object.assign's [[Set]] to
424
+ // trip over. Same observable result as a key-by-key copy, with the
425
+ // arbitrary-property-write shape removed instead of merely guarded.
426
+ return Object.assign(target, Object.fromEntries(entries));
427
+ }
428
+
396
429
  // observabilityShape — operator-supplied `opts.observability` must
397
430
  // expose an `event` function. Parallel to auditShape; the n=1 catalog
398
431
  // tracks both inline-shape regexes.
@@ -426,3 +459,4 @@ module.exports.requireMethods = requireMethods;
426
459
  module.exports.applyDefaults = applyDefaults;
427
460
  module.exports.makeAuditEmitter = makeAuditEmitter;
428
461
  module.exports.makeNamespacedEmitters = makeNamespacedEmitters;
462
+ module.exports.assignOwnEnumerable = assignOwnEnumerable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.14.20",
3
+ "version": "0.14.22",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
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.5",
5
- "serialNumber": "urn:uuid:e7a00583-46e2-4fd3-a1bf-9cc0789bed7f",
5
+ "serialNumber": "urn:uuid:6a454186-ef0a-43dd-8780-6900d4f1daf5",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-06-02T23:52:58.690Z",
8
+ "timestamp": "2026-06-05T17:23:40.587Z",
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.14.20",
22
+ "bom-ref": "@blamejs/core@0.14.22",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.14.20",
25
+ "version": "0.14.22",
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.14.20",
29
+ "purl": "pkg:npm/%40blamejs/core@0.14.22",
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.14.20",
57
+ "ref": "@blamejs/core@0.14.22",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]