@blamejs/core 0.7.85 → 0.7.86

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,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.7.x
10
10
 
11
+ - **0.7.86** (2026-05-06) — `b.middleware.csrfProtect({ requireJsonContentType: true })` — strict-fetch mode for JSON-only API surfaces. State-changing requests (POST/PUT/PATCH/DELETE) without `Content-Type: application/json` are refused before the token check with `CSRF: state-changing requests require Content-Type: application/json.` and audit emission `csrf.denied` with `reason: "non-JSON content-type: ..."`. The browser's form-encoded POST shape is the canonical CSRF vector — a malicious page can `<form action="/transfer" method=POST>` a victim into a state-changing request without a preflight. An `application/json` body forces a CORS preflight (the browser refuses to skip it for non-simple Content-Type values), so an attacker without an operator-allowlisted CORS origin can't reach the route at all. Default `false` — operators with HTML form submissions on the same routes (mixed SPA + classic form pages) keep current behavior; pure-fetch API operators opt in.
12
+
11
13
  - **0.7.85** (2026-05-06) — `b.auth.statusList` — OAuth Token Status List (draft-ietf-oauth-status-list-20). The canonical credential-revocation mechanism for SD-JWT VC and OpenID for Verifiable Credentials. An issuer publishes a JWT-wrapped bitstring at a URL; relying parties fetch + check the bit at index N to determine if the credential whose `status_list` claim points at that URL+index is valid / invalid / suspended / application-specific. **`b.auth.statusList.create({ size, bits?, fill? })`** allocates a bit-packed buffer (`bits` ∈ {1, 2, 4, 8} per draft §6.1.1, default 1). The returned object exposes `.set(idx, status)` / `.get(idx)` / `.snapshot()` / `.toJwt({ issuer, subject, privateKey, algorithm, expiresInSec?, ... })`. **`b.auth.statusList.fromJwt(token, { publicKey | keyResolver, algorithms?, expectedIssuer?, ... })`** verifies the JWT through `b.auth.jwt.verify` and returns `{ list, claims }` — the list exposes `.get(idx)` to check status of an individual credential without decompressing into a separate Buffer. The bitstring is zlib-deflated (RFC 1951 raw deflate per draft §6.1.4) before base64url encoding so a million-entry list collapses to ~125 KB on the wire when most bits are zero. Caps the compressed payload at 1 MiB; operators publishing larger lists shard. Status constants exported as `b.auth.statusList.STATUS_{VALID,INVALID,SUSPENDED,APPLICATION_SPECIFIC}`. Foundational for the v0.7.58 EU AI Act + eIDAS 2.0 wallet slice.
12
14
 
13
15
  - **0.7.84** (2026-05-06) — `b.crypto.sri(content, { algorithm? })` — Subresource Integrity hash builder per W3C SRI 1.0. Operators emit `<script integrity="sha384-...">` and `<link integrity="sha384-...">` to defend against CDN compromise + ISP MITM injection — the browser refuses to load a resource whose actual hash diverges from the integrity attribute. Default algorithm is `sha384` (W3C §3.2 — collision margin without sha512's 64-byte overhead); `sha256` and `sha512` also accepted, anything else refused. Accepts `Buffer` / `Uint8Array` / `string` / array of those — array inputs emit multiple space-separated integrity tokens per W3C §3.3 multi-integrity (browser picks the strongest it recognizes). Returns the standard `sha###-<base64>` format ready to paste into the `integrity=""` attribute.
@@ -228,7 +228,7 @@ function create(opts) {
228
228
 
229
229
  validateOpts(opts, [
230
230
  "cookie", "tokenLookup", "fieldName", "headerName", "methods", "audit",
231
- "trustProxy", "checkOrigin", "allowedOrigins",
231
+ "trustProxy", "checkOrigin", "allowedOrigins", "requireJsonContentType",
232
232
  ], "middleware.csrfProtect");
233
233
  var trustProxy = opts.trustProxy === true || typeof opts.trustProxy === "number"
234
234
  ? opts.trustProxy : false;
@@ -264,6 +264,19 @@ function create(opts) {
264
264
  var allowedOrigins = Array.isArray(opts.allowedOrigins)
265
265
  ? opts.allowedOrigins.slice() : null;
266
266
 
267
+ // requireJsonContentType — strict-fetch mode for JSON-only API
268
+ // surfaces. State-changing requests without `Content-Type:
269
+ // application/json` are refused before the token check. The browser's
270
+ // form-encoded POST shape is the canonical CSRF vector — a malicious
271
+ // page can <form action="/transfer" method=POST> a victim into a
272
+ // state-changing request without a preflight; an `application/json`
273
+ // body forces a CORS preflight (the browser refuses to skip it for
274
+ // non-simple Content-Type values), so an attacker without an
275
+ // operator-allowlisted CORS origin can't reach the route at all.
276
+ // Operators with HTML form submissions on the same routes (mixed
277
+ // SPA + classic form pages) leave this opt-out (default).
278
+ var requireJsonCt = opts.requireJsonContentType === true;
279
+
267
280
  // Cookie issuance config (only when opts.cookie is set).
268
281
  var cookieCfg = null;
269
282
  if (hasCookie) {
@@ -353,6 +366,16 @@ function create(opts) {
353
366
 
354
367
  if (methods.indexOf(req.method) === -1) return next();
355
368
 
369
+ // requireJsonContentType — refuse before the token check.
370
+ if (requireJsonCt) {
371
+ var ct = req.headers && req.headers["content-type"];
372
+ var bare = (typeof ct === "string" ? ct.split(";")[0].trim().toLowerCase() : "");
373
+ if (bare !== "application/json") {
374
+ _emitDenied(req, "non-JSON content-type: " + (bare || "<absent>"));
375
+ return _writeReject(res, "CSRF: state-changing requests require Content-Type: application/json.");
376
+ }
377
+ }
378
+
356
379
  // Origin / Referer cross-check (defense-in-depth alongside the
357
380
  // double-submit token). Refuses cross-origin state-changing
358
381
  // requests even when the token is valid (e.g. operator-mistaken
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.7.85",
3
+ "version": "0.7.86",
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:c3167b3e-7af4-4368-b131-46f9d9c44ccc",
5
+ "serialNumber": "urn:uuid:256e94d1-19a3-4626-8930-d068f5ff939f",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-06T06:22:16.402Z",
8
+ "timestamp": "2026-05-06T06:31:01.232Z",
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.85",
22
+ "bom-ref": "@blamejs/core@0.7.86",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.7.85",
25
+ "version": "0.7.86",
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.85",
29
+ "purl": "pkg:npm/%40blamejs/core@0.7.86",
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.85",
57
+ "ref": "@blamejs/core@0.7.86",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]