@blamejs/core 0.7.62 → 0.7.64

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 CHANGED
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.7.x
10
10
 
11
+ - **0.7.64** (2026-05-06) — HTTP/2 + WebSocket DoS hardening. **HTTP/2 server caps** — `lib/router.js` `http2.createSecureServer` now ships framework-default hardening: `maxConcurrentStreams: 100` (CVE-2023-44487 Rapid Reset cap; Node default was 4294967295), `maxSessionMemory: 10` (MB), `maxHeaderListPairs: 100` (CVE-2024-27983 / CVE-2024-28182 CONTINUATION-flood cap), `maxSettings: 32`, `peerMaxConcurrentStreams: 100`, `unknownProtocolTimeout: 10s` (Slowloris-h2 variant). Operator-supplied `tlsOptions` override any of these. **Slowloris timeouts** — `server.headersTimeout = 60s`, `server.requestTimeout = 5min`, `server.keepAliveTimeout = 5s` set explicitly post-listen. The framework was previously relying on Node-version-shifting defaults; pinning brings older Node releases up to the modern bar. **WebSocket origin default** — *breaking change* (pre-1.0, no compat shim per CLAUDE.md): when `origins` is omitted from `b.websocket.create({ ... })`, the new default is **same-origin enforcement** (Origin header host must match Host header). Pre-0.7.64 the default was "accept all", which is the canonical Cross-Site WebSocket Hijacking (CSWSH) class. Operators needing cross-origin opt in via `origins: "*"` (with audited reason) or `origins: [...allowlist]`. Non-browser clients (no Origin header) continue to bypass — origin enforcement is a browser-class defense.
12
+
13
+ - **0.7.63** (2026-05-06) — gitleaks regex allowlist extended to cover JWT fixtures split across multiple string literals. The v0.7.62 regex only matched the full three-segment JWT compact-serialization shape; source files split long JWT fixtures across literals for line-length, so gitleaks saw individual `eyJ...`-prefixed base64url segments and still flagged them. Added a second allowlist regex matching any `eyJ`-prefixed segment of substantive length (`{20,}`). Same rationale: real signing keys never appear as `eyJ...` base64url tokens — they're PEM / DER / PKCS#8.
14
+
11
15
  - **0.7.62** (2026-05-06) — gitleaks regex allowlist for JWT compact-serialization shape (`eyJ...header.eyJ...payload.signature`). The new `b.guardJwt` and `b.guardAuth` test fixtures legitimately embed JWT-shaped strings as benign + hostile inputs; gitleaks' default `generic-api-key` rule fires on the high-entropy base64url segments and refuses every release tag with the fixtures present. Real signing keys never appear in compact serialization shape — they're PEM / DER / PKCS#8 — so this allowlist doesn't suppress detection of actual key leaks. Allowlist regex added under the existing "Doc-string credential-shaped placeholders" block in `.gitleaks.toml`. No code change.
12
16
 
13
17
  - **0.7.61** (2026-05-06) — eslint cleanup in `lib/guard-regex.js` and `lib/guard-shell.js`. The `no-useless-escape` rule (eslint v9+) flagged unnecessary backslashes inside regex character classes — `*`, `+`, `?`, `[` don't need escaping when they appear inside `[...]`. Behavior unchanged: regex semantics are identical with or without the escapes (the engine treats both forms as the literal character). The framework's CI gate runs eslint with `--max-warnings 0`; this slice unblocks the CI lint job that's been failing on tag pushes since v0.7.53. No operator-facing behavior change.
package/lib/router.js CHANGED
@@ -626,10 +626,32 @@ class Router {
626
626
  // accept both. enableConnectProtocol: true is what enables h2
627
627
  // WebSocket (RFC 8441) — clients refuse to issue Extended CONNECT
628
628
  // until they see this in the server's SETTINGS frame.
629
+ // Framework-default HTTP/2 hardening — operator-supplied
630
+ // tlsOptions can override any of these.
631
+ //
632
+ // maxConcurrentStreams: cap concurrent streams per session (Node
633
+ // default is 4294967295 — way too high; CVE-2023-44487 Rapid
634
+ // Reset relies on the unbounded default).
635
+ // maxSessionMemory: 10 MB cap per session (Node default; explicit).
636
+ // maxHeaderListPairs: 100 header pairs max (Node default 128;
637
+ // tightened — CVE-2024-27983 / CVE-2024-28182 CONTINUATION
638
+ // flood relies on header-pair amplification).
639
+ // maxSettings: cap SETTINGS-frame entries.
640
+ // peerMaxConcurrentStreams: cap how many streams the peer is
641
+ // willing to accept (limits server-initiated push, which the
642
+ // framework doesn't use).
643
+ // unknownProtocolTimeout: 10s — drop sessions stuck in protocol-
644
+ // detection (Slowloris-h2 variant).
629
645
  server = http2.createSecureServer(Object.assign({
630
- allowHTTP1: true,
631
- ALPNProtocols: ["h2", "http/1.1"],
632
- settings: { enableConnectProtocol: true },
646
+ allowHTTP1: true,
647
+ ALPNProtocols: ["h2", "http/1.1"],
648
+ settings: { enableConnectProtocol: true },
649
+ maxConcurrentStreams: 100, // allow:raw-byte-literal — CVE-2023-44487 Rapid Reset cap
650
+ maxSessionMemory: 10, // allow:raw-byte-literal — MB cap (Node default explicit)
651
+ maxHeaderListPairs: 100, // allow:raw-byte-literal — CVE-2024-27983 CONTINUATION-flood cap
652
+ maxSettings: 32, // allow:raw-byte-literal — SETTINGS-frame entry ceiling
653
+ peerMaxConcurrentStreams: 100, // allow:raw-byte-literal — peer-side stream cap
654
+ unknownProtocolTimeout: C.TIME.seconds(10),
633
655
  }, tlsOptions), requestHandler);
634
656
  } else {
635
657
  // Cleartext path is h1-only. Operators wanting h2c on cleartext
@@ -710,7 +732,23 @@ class Router {
710
732
 
711
733
  if (host) server.listen(port, host, cb);
712
734
  else server.listen(port, cb);
713
- server.timeout = C.TIME.minutes(5);
735
+ // Slowloris / slow-read defenses. Node defaults shifted across
736
+ // versions; the framework pins them explicitly so operators on
737
+ // older Node releases get the modern bar.
738
+ //
739
+ // headersTimeout: 60s — time allotted for the entire request-line
740
+ // + header section. Slowloris's classic posture is a connection
741
+ // that trickles headers indefinitely.
742
+ // requestTimeout: 5min — total wall-clock for a request including
743
+ // body. Body-streaming uploads through fileUpload can take
744
+ // minutes; this is the operator-overridable ceiling.
745
+ // keepAliveTimeout: 5s — idle timeout between requests on a
746
+ // keep-alive connection.
747
+ // server.timeout: 5min — hardware/network timeout (legacy).
748
+ server.headersTimeout = C.TIME.seconds(60);
749
+ server.requestTimeout = C.TIME.minutes(5);
750
+ server.keepAliveTimeout = C.TIME.seconds(5);
751
+ server.timeout = C.TIME.minutes(5);
714
752
  return server;
715
753
  }
716
754
  }
package/lib/websocket.js CHANGED
@@ -221,12 +221,17 @@ function negotiateSubprotocol(req, supported) {
221
221
  // origins shapes:
222
222
  // array — strict allowlist, enforced
223
223
  // "*" — explicit "accept all" (operator opt-in to no checking)
224
- // null/undefined — same as "*" but caller (router) is expected to
225
- // have logged a startup warning. Origin policy is a
226
- // framework-level decision; this primitive doesn't
227
- // re-warn here.
224
+ // null/undefined — DEFAULT: same-origin (Origin host matches Host
225
+ // header). The pre-0.7.64 default was "accept all"
226
+ // flipped here because cross-site WebSocket
227
+ // hijacking (CSWSH) is a real attacker capability
228
+ // against any browser-targeted WebSocket route, and
229
+ // same-origin is the safe default. Operators
230
+ // needing cross-origin opt in explicitly via
231
+ // `origins: "*"` (with audited reason) or
232
+ // `origins: [...allowlist]`.
228
233
  function isOriginAllowed(req, origins) {
229
- if (!origins || origins === "*") return true;
234
+ if (origins === "*") return true;
230
235
  var origin = (req.headers || {}).origin;
231
236
  // Non-browser clients (curl, server-to-server, native apps) don't
232
237
  // send Origin. Origin enforcement only meaningfully applies to
@@ -234,6 +239,17 @@ function isOriginAllowed(req, origins) {
234
239
  // the operator's network ACL / auth middleware, not Origin.
235
240
  if (!origin) return true;
236
241
  if (Array.isArray(origins)) return origins.indexOf(origin) !== -1;
242
+ // Default: same-origin. Compare the Origin header's hostname against
243
+ // the Host header. Operators behind a TLS-terminating LB pass the
244
+ // canonical Host through (or set `origins: [...]` explicitly).
245
+ if (!origins) {
246
+ var host = (req.headers || {}).host;
247
+ if (!host) return false;
248
+ var originHost;
249
+ try { originHost = new URL(origin).host; } // allow:raw-new-url — comparing browser-supplied Origin header against Host; safeUrl.parse adds policy filtering that isn't appropriate for exact host comparison
250
+ catch (_e) { return false; }
251
+ return originHost === host;
252
+ }
237
253
  return false;
238
254
  }
239
255
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.7.62",
3
+ "version": "0.7.64",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -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:dd45b15b-229b-45b9-bfa9-61c962a76fb3",
5
+ "serialNumber": "urn:uuid:186ac1cf-f222-4ea2-81b9-ad8a89f1849e",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-06T00:25:44.109Z",
8
+ "timestamp": "2026-05-06T00:58:07.750Z",
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.7.62",
22
+ "bom-ref": "@blamejs/core@0.7.64",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.7.62",
25
+ "version": "0.7.64",
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.7.62",
29
+ "purl": "pkg:npm/%40blamejs/core@0.7.64",
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.7.62",
57
+ "ref": "@blamejs/core@0.7.64",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]