@blamejs/core 0.12.15 → 0.12.16

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.12.x
10
10
 
11
+ - v0.12.16 (2026-05-23) — **`b.safeArchive.inspect` auto-unwraps wrap envelopes (parallel to the v0.12.15 extract path).** Mirrors the v0.12.15 auto-unwrap support into `b.safeArchive.inspect`. Operators enumerating entries of a sealed archive get a single inspect() call regardless of envelope shape — pass `opts.recipient` or `opts.passphrase` alongside `source` and the orchestrator unwraps inline before walking the inner format. Missing-key opt surfaces a structured `safe-archive/no-recipient-for-wrap` / `safe-archive/no-passphrase-for-wrap` refusal upfront. Carries the v0.12.15 P1 + P2 fixes (close original source before replacing + forward opts.signal to inner buffer adapter) into the inspect path so the same descriptor-leak + abort-propagation contracts hold. **Added:** *`b.safeArchive.inspect` auto-unwraps `BAWRP` + `BAWPP` envelopes* — The orchestrator's `format: "auto"` sniffer recognises the wrap magics and routes through `b.archive.unwrap` / `b.archive.unwrapWithPassphrase` inline. After unwrap, the inner bytes are wrapped in a buffer adapter + re-sniffed; the resulting summary carries the INNER `format` (`"tar"` / `"zip"` / etc.) — operators querying `summary.format` see the carrier format, not `"wrap-recipient"`. Entry enumeration walks the inner archive after a single key-derivation pass; no temporary file lands on disk.
12
+
11
13
  - v0.12.15 (2026-05-23) — **`b.safeArchive.extract` auto-unwraps v0.12.10 recipient and v0.12.11 passphrase envelopes inline.** The safeArchive orchestrator's `format: "auto"` sniffer recognises `BAWRP` (v0.12.10 recipient) and `BAWPP` (v0.12.11 passphrase) envelope magics and routes through `b.archive.unwrap` / `b.archive.unwrapWithPassphrase` inline before re-sniffing the inner format. Operators pass `opts.recipient` (or `opts.passphrase`) alongside `source` + `destination` and get a single extract() call regardless of envelope shape. Missing the matching key opt surfaces a structured `safe-archive/no-recipient-for-wrap` / `safe-archive/no-passphrase-for-wrap` refusal upfront rather than a downstream crypto error. **Added:** *`b.safeArchive.extract` auto-unwraps wrap envelopes* — The sniffer at byte 0-4 recognises `BAWRP` (returns `format: "wrap-recipient"`) and `BAWPP` (returns `format: "wrap-passphrase"`). The extract path collects the sealed adapter into a Buffer, routes through `b.archive.unwrap` (recipient) or `b.archive.unwrapWithPassphrase` (passphrase), wraps the inner bytes in a buffer adapter, re-sniffs the inner format, and dispatches to the appropriate `b.archive.read.*` reader. A wrap-around-tar.gz envelope round-trips through wrap → unwrap → gunzip → untar with no operator intervention beyond passing the key opt. **Security:** *Missing-key opt refused upfront with structured error* — When the sniffer identifies a wrap envelope but the operator hasn't supplied `opts.recipient` (BAWRP) or `opts.passphrase` (BAWPP), extract refuses with `safe-archive/no-recipient-for-wrap` / `safe-archive/no-passphrase-for-wrap` BEFORE any decryption attempt. Operators wiring extract behind an HTTP boundary get a typed refusal instead of a leaked crypto-level error.
12
14
 
13
15
  - v0.12.14 (2026-05-23) — **`b.archive.sniffEnvelope(bytes)` — identify recipient vs passphrase vs raw payload without attempting decryption.** Small helper closing a gap in the archive-wrap surface. `b.archive.sniffEnvelope(bytes)` reads the first 5 bytes of a buffer and returns one of `"recipient"` (v0.12.10 BAWRP envelope), `"passphrase"` (v0.12.11 BAWPP envelope), or `"none"` (raw payload or unrelated bytes). The sniff does NO cryptographic work — no Argon2id round, no decapsulation, no allocation beyond a 5-byte ASCII compare — so it's safe to call on adversarial input. Operators dispatching between unwrap paths get a clean predicate instead of trial-decrypting under multiple key candidates. **Added:** *`b.archive.sniffEnvelope(bytes)` — magic-byte envelope identifier* — Returns `"recipient"` (BAWRP / v0.12.10 hybrid PQC envelope), `"passphrase"` (BAWPP / v0.12.11 Argon2id + XChaCha20 envelope), or `"none"` (raw archive bytes / unrelated payload). Accepts Buffer + Uint8Array; non-buffer / null / undefined / empty-buffer inputs return `"none"` upfront. Operators wire the result into a switch that dispatches to the matching unwrap primitive — no trial decryption, no per-key candidate attempts.
@@ -330,6 +330,38 @@ async function inspect(opts) {
330
330
  var sniff = await _sniffMagic(source);
331
331
  format = sniff.format;
332
332
  }
333
+ // v0.12.16 — auto-unwrap path for inspect, parallel to the
334
+ // v0.12.15 extract path. Wrap envelopes (BAWRP / BAWPP) are
335
+ // unwrapped inline + re-sniffed so operators can enumerate
336
+ // entries of a sealed archive in a single inspect() call.
337
+ if (format === "wrap-recipient" || format === "wrap-passphrase") {
338
+ var sealedBytes = await _collectSourceBytes(source);
339
+ var inner;
340
+ if (format === "wrap-recipient") {
341
+ if (!opts.recipient) {
342
+ throw new SafeArchiveError("safe-archive/no-recipient-for-wrap",
343
+ "inspect: source is a wrap-recipient envelope (BAWRP) but opts.recipient was not supplied. " +
344
+ "Pass `{ recipient: { privateKey, ecPrivateKey } }` (or peer-cert form) to unwrap inline.");
345
+ }
346
+ inner = archiveWrap().unwrap(sealedBytes, { recipient: opts.recipient });
347
+ } else {
348
+ if (typeof opts.passphrase !== "string" && !Buffer.isBuffer(opts.passphrase)) {
349
+ throw new SafeArchiveError("safe-archive/no-passphrase-for-wrap",
350
+ "inspect: source is a wrap-passphrase envelope (BAWPP) but opts.passphrase was not supplied. " +
351
+ "Pass `{ passphrase: <string|Buffer> }` to unwrap inline.");
352
+ }
353
+ inner = await archiveWrap().unwrapWithPassphrase(sealedBytes, { passphrase: opts.passphrase });
354
+ }
355
+ // v0.12.15 P1 — close the original fs adapter (if string-
356
+ // backed) BEFORE replacing the source reference. v0.12.15 P2
357
+ // — forward opts.signal to the inner buffer adapter.
358
+ if (typeof source.close === "function" && typeof opts.source === "string") {
359
+ try { source.close(); } catch (_e) { /* drop-silent */ }
360
+ }
361
+ source = archiveAdapters().buffer(inner, { signal: opts.signal });
362
+ var innerSniff = await _sniffMagic(source);
363
+ format = innerSniff.format;
364
+ }
333
365
  var reader;
334
366
  if (format === "zip") {
335
367
  reader = archiveRead().zip(source, {
@@ -343,7 +375,7 @@ async function inspect(opts) {
343
375
  });
344
376
  } else {
345
377
  throw new SafeArchiveError("safe-archive/format-unsupported",
346
- "inspect: format=" + JSON.stringify(format) + " — v0.12.8 ships ZIP + tar");
378
+ "inspect: format=" + JSON.stringify(format) + " — v0.12.8 ships ZIP + tar; v0.12.16 auto-unwraps wrap envelopes");
347
379
  }
348
380
  var entries = await reader.inspect();
349
381
  var totalCompressed = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.12.15",
3
+ "version": "0.12.16",
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:73e48489-0fa1-477f-b6f3-eec74e9ba65f",
5
+ "serialNumber": "urn:uuid:bd84dcb6-2b84-41e8-bf62-21fe1685fe31",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-24T03:18:46.285Z",
8
+ "timestamp": "2026-05-24T03:36:28.547Z",
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.12.15",
22
+ "bom-ref": "@blamejs/core@0.12.16",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.12.15",
25
+ "version": "0.12.16",
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.12.15",
29
+ "purl": "pkg:npm/%40blamejs/core@0.12.16",
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.12.15",
57
+ "ref": "@blamejs/core@0.12.16",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]