@blamejs/core 0.9.6 → 0.9.7

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,7 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.9.x
10
10
 
11
+ - v0.9.7 (2026-05-13) — **SECURITY.md: release-tag verification path documented + signed-tag invariant from v0.9.7+**. SECURITY.md gains a "Verifying release authenticity" section documenting how operators verify a release tag's authenticity independently of GitHub's UI. The maintainer Ed25519 SSH signing key fingerprint (`SHA256:5oF/XWhFpMde9TRfEX2GAHiApAq/MXOS4vti5zQbD7g`) is published alongside the public-key retrieval URL (`https://github.com/dotCooCoo.keys`) and a `git tag -v` recipe that bypasses the "Verified" badge. From v0.9.7 onward, every release tag is an annotated SSH-signed tag; the repository's `release-tags` ruleset's `required_signatures` rule refuses any unsigned or lightweight tag push at the server side. Earlier tags (v0.9.6 and prior) remain as lightweight commits and don't verify via `git tag -v`; they continue to verify via the SLSA L3 npm provenance + Sigstore-keyless SBOM signatures already attached to those releases (the `cosign verify-blob` recipe is in the same SECURITY.md section). No framework-surface changes; this release ships the documentation + invariant only.
11
12
  - v0.9.6 (2026-05-12) — **`b.vex` (OASIS CSAF 2.1 VEX) + framework-control compliance posture sweep**. *(PR feedback: CSAF-conformance fixes folded in pre-merge — `cwes` is now a list per §3.2.3.4 instead of a singleton `cwe` field; CWE alone is no longer accepted as a vulnerability identity per §3.2.3.2 (operator supplies `cveId` or `ids[]: [{ systemName, text }]` per §3.2.3.5); TLP allowlist corrected to TLP 2.0 (FIRST 2022) per §3.2.1.12.1.1 — `CLEAR / GREEN / AMBER / AMBER+STRICT / RED` (added the previously-omitted `AMBER+STRICT` restriction tier and removed the legacy TLP 1.0 `WHITE` label, which was renamed `CLEAR` in TLP 2.0). Public opt name `cwe` is now `cweId` to mirror `cveId`; this is a v0.9.6 surface that never shipped to npm so the rename is not a breaking change.)* Closes the framework-side findings from the 2026-05-11 exceptd framework-gap-analysis (49 gaps across CVE-triage / framework-compliance / threat-modeling / AI-security / identity-assurance / crypto-posture / supply-chain / sector-specific). **`b.vex.statement({ cveId, status, productIds, justification?, impactStatement?, references?, firstReleased?, lastUpdated? })`** builds an OASIS CSAF 2.1 §3.2.3 vulnerability statement with `product_status` keyed by status enum (`known_not_affected` / `affected` / `fixed` / `under_investigation`), `flags[].label` for §3.2.2.7 justifications (`component_not_present` / `vulnerable_code_not_present` / `vulnerable_code_not_in_execute_path` / `vulnerable_code_cannot_be_controlled_by_adversary` / `inline_mitigations_already_exist`), and `notes[].text` for impact narrative. Refuses missing CVE/CWE id, malformed CVE shape, unknown status, missing productIds, and `known_not_affected` without justification. **`b.vex.document({ documentId, title, publisher, trackingId, trackingVersion, currentReleaseDate, initialReleaseDate, statements, tlp? })`** assembles the §3.2 CSAF document envelope with category `csaf_vex`, csaf_version `2.1`, publisher category `vendor`, tracking status `final`, and `distribution.tlp.label` (default `CLEAR`; refuses non-TLP labels). **`b.vex.serialize(doc)`** routes through `b.canonicalJson.stringify` for byte-stable sorted-key output then re-indents at 2 spaces for human-diffable artifacts. Exports `STATUS_VALUES` / `JUSTIFICATION_VALUES` / `TLP_LABELS` / `CSAF_VERSION` / `VexError`. **25 new compliance postures** added to `b.compliance.KNOWN_POSTURES` (with matching `POSTURE_DEFAULTS` cascade entries): `nist-800-53` (NIST SP 800-53 Rev 5 control catalog), `nist-ai-rmf-1.0` (NIST AI Risk Management Framework 1.0), `iso-42001-2023` (AI management systems), `iso-23894-2023` (AI risk management guidance), `owasp-llm-top-10-2025` (LLM application risk catalog), `owasp-asvs-v5.0` (Application Security Verification Standard v5.0), `nist-800-218-ssdf` (Secure Software Development Framework), `nist-800-82-r3` (industrial control systems), `nist-800-63b-rev4` (digital identity authenticator guidance), `iec-62443-3-3` (industrial security), `fedramp-rev5-moderate` (federal cloud baseline), `hipaa-security-rule` (45 CFR §164.302-318 administrative + technical safeguards), `hitrust-csf-v11.4` (healthcare common security framework), `nerc-cip-007-6` (bulk electric system cyber asset security), `psd2-rts-sca` (PSD2 Regulatory Technical Standards for Strong Customer Authentication), `swift-cscf-v2026` (SWIFT Customer Security Controls Framework 2026), `slsa-v1.0-build-l3` (SLSA build-track L3 provenance), `vex-csaf-2.1` (the standard `b.vex` emits), `cyclonedx-v1.6` (already shipped via `sbom.cdx.json`), `spdx-v3.0` (SPDX 3.0 software bill of materials), `owasp-wstg-v5` (Web Security Testing Guide v5), `ptes` (Penetration Testing Execution Standard), `nist-800-115` (technical guide to information security testing), `cwe-top-25-2024` (CWE most dangerous software weaknesses 2024), `cis-controls-v8` (Center for Internet Security Critical Controls v8), `cmmc-2.0-level-2` (DoD CMMC Level 2 advanced; complements the existing `cmmc-2.0` posture). Each cascade entry encodes the regime's data-tier mandate (encrypted backups + signed audit chain + TLS 1.3 minimum + vacuum-after-erase where applicable).
12
13
  - v0.9.5 (2026-05-12) — **Fix-up for v0.9.3 + v0.9.4 audit-derived primitives** (five reported reachability/contract bugs). (1) **`b.middleware.dpop` `trustForwardedHeaders` was unreachable** — the v0.9.4 X-Forwarded-* trust gate added the option to `_reconstructHtu` but the `create()` validateOpts whitelist still rejected unknown keys. Operators behind a trusted reverse proxy got `unknown-option` instead of the documented opt-in, leaving valid DPoP proofs failing htu matching. The whitelist now includes `trustForwardedHeaders`. (2) **`b.auth.jwt.verifyExternal` `allowKidlessJwks` was unreachable** — same shape, fixed the same way. (3) **OAuth `allowKidlessJwks` didn't reach token-exchange flows** — pre-v0.9.5 the opt was per-`verifyIdToken`-call, but `_normalizeTokens()` (called from `exchangeCode` / `pollDeviceCode` / `exchangeToken` / `refreshAccessToken`) passed a reduced `{ nonce, skipNonceCheck }` shape that dropped the operator opt. Surface promoted to client-level: pass `b.auth.oauth.create({ allowKidlessJwks: true })` once and it threads through every code path that lands on the verifier. The per-call `vopts.allowKidlessJwks` continues to work for direct `verifyIdToken` callers. (4) **`b.auth.oauth.refreshAccessToken` `checkAndInsert` return-value contract inverted** — pre-v0.9.5 interpreted `true` as "already seen → replay" but the framework-wide `checkAndInsert` contract (`b.nonceStore`, `b.auth.jwt`) is the opposite: `true` = unseen-and-now-inserted (first sighting), `false` = already-present (replay). Operators reusing an existing `b.nonceStore`-style backend got every first refresh attempt rejected as token theft, breaking normal refresh flows. The handler now normalizes `inserted === false` → `alreadySeen = true`, consistent with the rest of the framework. (5) **`b.auth.ciba` `_intervalState` memory leak on error paths** — pre-v0.9.5 entries were only deleted on successful token issuance; denied / expired auth requests, and ping/push delivery modes that never call `pollToken` successfully, left permanent entries causing unbounded growth in long-running processes. Now entries carry an `expireAtMs` derived from the IdP-supplied `expires_in` of the auth_req_id, and an opportunistic sweep runs on every `_registerInitialInterval` call (no separate timer needed). Terminal CIBA errors (`expired_token` / `access_denied` / `invalid_grant` / `transaction_failed`) also delete the entry immediately on the error path.
13
14
  - v0.9.4 (2026-05-12) — **Audit hardening slice 4: kid-less JWKS lookup refusal + OCSP nonce CT compare + OAuth scope strict-split + DPoP `X-Forwarded-Proto` trust gate**. Closes the remaining MEDIUM-tier findings from the 2026-05-11 auth audit. **`b.auth.oauth.verifyIdToken` + `b.auth.jwt.verifyExternal` kid-less JWKS lookup refusal** — pre-v0.9.4 both verifiers fell back to `keys[0]` when the token carried NO `kid` and the JWKS had exactly one key. This is a latent vector during JWKS rotation: an attacker shipping a kid-less token gets the lone-key path during the window the rotated-out key is still cached at the IdP but the rotated-in key is already published. Every modern IdP includes `kid`; the framework now refuses kid-less tokens unconditionally. Operators with non-conforming IdPs that genuinely emit kid-less tokens opt out via `vopts.allowKidlessJwks: true`. **`b.network.tls` OCSP nonce constant-time compare** — `evaluateOcspResponse`'s `expectedNonce` match migrated from `Buffer.equals` to `b.crypto.timingSafeEqual` for module-wide consistency with the Merkle-root / NTS-cookie / cert-fingerprint paths that already use `timingSafeEqual`. **`b.auth.oauth` scope strict whitespace split** — RFC 6749 §3.3 says `scope` is space-separated, ONLY `U+0020`. Pre-v0.9.4 `raw.scope.split(/\s+/)` matched U+0085 NEL, U+00A0 NBSP, etc., so a hostile AS returning `scope: "admin<NEL>read"` would surface as `["admin", "read"]` and the operator's scope allowlist saw two distinct scopes. Now splits on single-space only; empty pieces filtered out. **`b.middleware.dpop` `X-Forwarded-*` trust gate** — `_reconstructHtu` previously read `X-Forwarded-Proto` / `X-Forwarded-Host` unconditionally; an attacker who can hit the origin directly while spoofing `X-Forwarded-Proto: https` could trick the middleware into building an `https` htu that the DPoP proof was signed for, when the origin is actually serving HTTP (RFC 9449 §4.3 says the htu MUST be the absolute URL the request was sent to). The default now derives proto/host from the socket; operators with a confirmed-trusted front proxy opt in via `opts.trustForwardedHeaders: true`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
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.6",
5
- "serialNumber": "urn:uuid:1564ed32-1ca1-46e6-be60-6ea469f08940",
5
+ "serialNumber": "urn:uuid:4587a803-c639-4aa0-ba4d-6b9bb2ad5845",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-12T13:18:41.980Z",
8
+ "timestamp": "2026-05-13T05:39:58.426Z",
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.9.6",
22
+ "bom-ref": "@blamejs/core@0.9.7",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.9.6",
25
+ "version": "0.9.7",
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.9.6",
29
+ "purl": "pkg:npm/%40blamejs/core@0.9.7",
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.9.6",
57
+ "ref": "@blamejs/core@0.9.7",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]