@blamejs/blamejs-shop 0.4.91 → 0.4.93

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 (47) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/security-middleware.js +21 -3
  4. package/lib/vendor/MANIFEST.json +49 -41
  5. package/lib/vendor/blamejs/CHANGELOG.md +6 -0
  6. package/lib/vendor/blamejs/SECURITY.md +1 -0
  7. package/lib/vendor/blamejs/api-snapshot.json +208 -2
  8. package/lib/vendor/blamejs/examples/wiki/test/e2e.js +7 -4
  9. package/lib/vendor/blamejs/examples/wiki/test/integration.js +15 -12
  10. package/lib/vendor/blamejs/index.js +2 -0
  11. package/lib/vendor/blamejs/lib/audit-sign.js +34 -1
  12. package/lib/vendor/blamejs/lib/backup/manifest.js +191 -44
  13. package/lib/vendor/blamejs/lib/codepoint-class.js +284 -77
  14. package/lib/vendor/blamejs/lib/framework-error.js +14 -0
  15. package/lib/vendor/blamejs/lib/fsm.js +80 -24
  16. package/lib/vendor/blamejs/lib/log.js +32 -0
  17. package/lib/vendor/blamejs/lib/middleware/rate-limit.js +18 -2
  18. package/lib/vendor/blamejs/lib/middleware/request-id.js +24 -4
  19. package/lib/vendor/blamejs/lib/request-helpers.js +50 -0
  20. package/lib/vendor/blamejs/lib/safe-path.js +24 -10
  21. package/lib/vendor/blamejs/lib/sql.js +133 -0
  22. package/lib/vendor/blamejs/lib/totp.js +98 -33
  23. package/lib/vendor/blamejs/lib/ws-client.js +39 -28
  24. package/lib/vendor/blamejs/package.json +1 -1
  25. package/lib/vendor/blamejs/release-notes/v0.15.21.json +51 -0
  26. package/lib/vendor/blamejs/release-notes/v0.15.22.json +18 -0
  27. package/lib/vendor/blamejs/release-notes/v0.15.23.json +22 -0
  28. package/lib/vendor/blamejs/test/00-primitives.js +80 -0
  29. package/lib/vendor/blamejs/test/_smoke-worker.js +81 -0
  30. package/lib/vendor/blamejs/test/integration/federation-auth.test.js +7 -4
  31. package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +7 -4
  32. package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +91 -0
  33. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +65 -0
  34. package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +58 -0
  35. package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +5 -2
  36. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +9 -1
  37. package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +99 -0
  38. package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +30 -0
  39. package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +9 -6
  40. package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +36 -0
  41. package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +33 -0
  42. package/lib/vendor/blamejs/test/layer-0-primitives/request-id-async-context.test.js +117 -0
  43. package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +64 -0
  44. package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +96 -0
  45. package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +55 -0
  46. package/lib/vendor/blamejs/test/smoke.js +93 -10
  47. package/package.json +1 -1
@@ -314,47 +314,39 @@ function serialize(manifest) {
314
314
  // or SLH-DSA-SHAKE-256f — whichever audit-sign was initialized with).
315
315
  // The signature covers the manifest's canonical bytes WITHOUT the
316
316
  // signature field; appending it does not change the signed payload.
317
- function sign(manifest) {
318
- var v = validate(manifest);
319
- if (!v.ok) {
320
- throw new BackupManifestError("backup-manifest/invalid",
321
- "sign: " + v.errors.join("; "));
322
- }
317
+ // Sign caller-supplied canonical bytes with the audit-sign keypair, returning a
318
+ // detached signature block. Shared by sign() (v1 manifest schema) and
319
+ // signBytes() (schema-agnostic). `payload` is a Buffer (signed verbatim) or a
320
+ // string (signed as UTF-8) — auditSign.sign normalizes both identically.
321
+ function _signPayload(payload, who) {
323
322
  var signer = auditSign();
324
323
  if (!signer || typeof signer.sign !== "function") {
325
324
  throw new BackupManifestError("backup-manifest/no-signer",
326
- "sign: audit-sign module is not available; call b.auditSign.init() first");
325
+ who + ": audit-sign module is not available; call b.auditSign.init() first");
327
326
  }
328
- var payload = signingPayload(manifest);
329
327
  var signatureBytes;
330
328
  try { signatureBytes = signer.sign(payload); }
331
329
  catch (e) {
332
330
  throw new BackupManifestError("backup-manifest/sign-failed",
333
- "sign: audit-sign.sign threw: " + ((e && e.message) || String(e)));
331
+ who + ": audit-sign.sign threw: " + ((e && e.message) || String(e)));
334
332
  }
335
- manifest.signature = {
333
+ return {
336
334
  algorithm: signer.getAlgorithm(),
337
335
  publicKey: signer.getPublicKey(),
338
336
  fingerprint: signer.getPublicKeyFingerprint(),
339
337
  value: signatureBytes.toString("base64"),
340
338
  signedAt: new Date().toISOString(),
341
339
  };
342
- return manifest;
343
340
  }
344
341
 
345
- // Verify a previously-signed manifest. Returns { ok, reason?,
346
- // fingerprint? }. Caller policy decides whether a missing or
347
- // fingerprint-mismatched signature is fatal — verifyManifestSignature
348
- // in lib/backup/index.js wraps this with operator-facing semantics.
349
- function verifySignature(manifest, opts) {
342
+ // Verify `payload` against a detached signature block (the shape _signPayload
343
+ // returns). Shared by verifySignature() and verifyBytes(). Returns
344
+ // { ok, reason?, fingerprint? }; enforces opts.expectedFingerprint pinning.
345
+ function _verifyPayloadAgainstBlock(payload, sig, opts) {
350
346
  opts = opts || {};
351
- if (!manifest || typeof manifest !== "object") {
352
- return { ok: false, reason: "manifest must be an object" };
353
- }
354
- if (!manifest.signature || typeof manifest.signature !== "object") {
355
- return { ok: false, reason: "manifest has no signature block" };
347
+ if (!sig || typeof sig !== "object") {
348
+ return { ok: false, reason: "signature block must be an object" };
356
349
  }
357
- var sig = manifest.signature;
358
350
  if (typeof sig.algorithm !== "string" || sig.algorithm.length === 0) {
359
351
  return { ok: false, reason: "signature.algorithm is required" };
360
352
  }
@@ -364,28 +356,50 @@ function verifySignature(manifest, opts) {
364
356
  if (typeof sig.value !== "string" || sig.value.length === 0) {
365
357
  return { ok: false, reason: "signature.value is required" };
366
358
  }
367
- // Caller may pin the expected fingerprint operators tracking key
368
- // rotation pass the active audit-sign fingerprint and refuse any
369
- // bundle signed under a different historical key.
370
- if (typeof opts.expectedFingerprint === "string" &&
371
- opts.expectedFingerprint.length > 0 &&
372
- sig.fingerprint !== opts.expectedFingerprint) {
373
- return {
374
- ok: false,
375
- reason: "signature.fingerprint=" + sig.fingerprint +
376
- " does not match expectedFingerprint=" + opts.expectedFingerprint,
377
- fingerprint: sig.fingerprint,
378
- };
359
+ // The fingerprint of the key the signature is ACTUALLY verified under —
360
+ // recomputed from sig.publicKey, never the block's self-asserted
361
+ // sig.fingerprint (which an attacker controls). Used for the pin check below
362
+ // and returned on success so the caller sees the real key, not a claim.
363
+ var derivedFingerprint = null;
364
+ try {
365
+ var signerForFp = auditSign();
366
+ if (signerForFp && typeof signerForFp.fingerprintOf === "function") {
367
+ derivedFingerprint = signerForFp.fingerprintOf(sig.publicKey);
368
+ }
369
+ } catch (fpErr) {
370
+ return { ok: false, reason: "could not derive fingerprint from publicKey: " +
371
+ ((fpErr && fpErr.message) || String(fpErr)) };
372
+ }
373
+ // Caller may pin the expected fingerprint — operators tracking key rotation
374
+ // pass the active audit-sign fingerprint and refuse any payload signed under
375
+ // a different historical key. CRITICAL: the pin is checked against the
376
+ // fingerprint RECOMPUTED from the block's own publicKey, NOT the block's
377
+ // self-asserted `fingerprint` field. An attacker can sign arbitrary bytes
378
+ // with their own key and set `fingerprint` to the trusted value; binding the
379
+ // pin to fingerprintOf(publicKey) — the key the signature is actually
380
+ // verified under — closes that substitution (the signature can only verify
381
+ // under publicKey, and publicKey must hash to the trusted fingerprint).
382
+ if (typeof opts.expectedFingerprint === "string" && opts.expectedFingerprint.length > 0) {
383
+ if (derivedFingerprint === null) {
384
+ return { ok: false, reason: "fingerprint pinning requires audit-sign.fingerprintOf (unavailable)" };
385
+ }
386
+ if (derivedFingerprint !== opts.expectedFingerprint) {
387
+ return {
388
+ ok: false,
389
+ reason: "publicKey fingerprint=" + derivedFingerprint +
390
+ " does not match expectedFingerprint=" + opts.expectedFingerprint,
391
+ fingerprint: derivedFingerprint,
392
+ };
393
+ }
379
394
  }
380
- var payload = signingPayload(manifest);
381
395
  var sigBuf;
382
396
  try { sigBuf = Buffer.from(sig.value, "base64"); }
383
397
  catch (_e) {
384
398
  return { ok: false, reason: "signature.value is not valid base64" };
385
399
  }
386
- // Use audit-sign.verify when available (handles algorithm dispatch
387
- // identically to the signer); fall back to nodeCrypto.verify for
388
- // verifier processes that don't init audit-sign.
400
+ // Use audit-sign.verify when available (handles algorithm dispatch identically
401
+ // to the signer); fall back to nodeCrypto.verify for verifier processes that
402
+ // don't init audit-sign.
389
403
  var ok;
390
404
  try {
391
405
  var signer = auditSign();
@@ -393,23 +407,154 @@ function verifySignature(manifest, opts) {
393
407
  ok = signer.verify(payload, sigBuf, sig.publicKey);
394
408
  } else {
395
409
  ok = require("node:crypto").verify(null,
396
- Buffer.from(payload, "utf8"), sig.publicKey, sigBuf);
410
+ Buffer.isBuffer(payload) ? payload : Buffer.from(payload, "utf8"), sig.publicKey, sigBuf);
397
411
  }
398
412
  } catch (e) {
399
413
  return {
400
- ok: false,
401
- reason: "verify threw: " + ((e && e.message) || String(e)),
402
- fingerprint: sig.fingerprint,
414
+ ok: false,
415
+ reason: "verify threw: " + ((e && e.message) || String(e)),
416
+ fingerprint: sig.fingerprint,
403
417
  };
404
418
  }
405
419
  if (!ok) {
406
420
  return {
407
421
  ok: false,
408
422
  reason: "signature did not verify under provided publicKey",
409
- fingerprint: sig.fingerprint,
423
+ fingerprint: derivedFingerprint || sig.fingerprint,
410
424
  };
411
425
  }
412
- return { ok: true, fingerprint: sig.fingerprint };
426
+ // Return the fingerprint of the key the signature actually verified under
427
+ // (recomputed), not the block's self-asserted value.
428
+ return { ok: true, fingerprint: derivedFingerprint || sig.fingerprint };
429
+ }
430
+
431
+ /**
432
+ * @primitive b.backupManifest.sign
433
+ * @signature b.backupManifest.sign(manifest)
434
+ * @since 0.6.0
435
+ * @status stable
436
+ * @related b.backupManifest.verifySignature, b.backupManifest.signBytes, b.auditSign.sign
437
+ *
438
+ * Sign a v1 backup manifest in place with the audit-sign keypair, attaching a
439
+ * detached `signature` block over the manifest's canonical bytes (the
440
+ * serialization WITHOUT the signature field, so appending it doesn't change
441
+ * the signed payload). Validates the manifest against the v1 schema first;
442
+ * throws `backup-manifest/invalid` on a malformed manifest and
443
+ * `backup-manifest/no-signer` when `b.auditSign.init()` hasn't run. For a
444
+ * schema-agnostic alternative see `signBytes`.
445
+ *
446
+ * @example
447
+ * b.backupManifest.sign(manifest);
448
+ * manifest.signature.fingerprint; // the signing key's fingerprint
449
+ */
450
+ function sign(manifest) {
451
+ var v = validate(manifest);
452
+ if (!v.ok) {
453
+ throw new BackupManifestError("backup-manifest/invalid",
454
+ "sign: " + v.errors.join("; "));
455
+ }
456
+ manifest.signature = _signPayload(signingPayload(manifest), "sign");
457
+ return manifest;
458
+ }
459
+
460
+ /**
461
+ * @primitive b.backupManifest.signBytes
462
+ * @signature b.backupManifest.signBytes(canonicalBytes)
463
+ * @since 0.15.21
464
+ * @status stable
465
+ * @related b.backupManifest.verifyBytes, b.backupManifest.sign, b.auditSign.sign
466
+ *
467
+ * Sign caller-supplied canonical bytes with the framework's audit-sign keypair,
468
+ * returning a detached signature block — the schema-agnostic counterpart of
469
+ * `sign()`. Where `sign()` is bound to the v1 manifest schema (it `validate()`s
470
+ * the whole `{ version, framework, files[] }` shape before signing), this signs
471
+ * any bytes a consumer canonicalizes itself, so a bespoke backup-header /
472
+ * manifest format reuses the same post-quantum signing keypair + fingerprint
473
+ * pinning without adopting the framework schema.
474
+ *
475
+ * `canonicalBytes` is a Buffer (signed verbatim) or a string (signed as UTF-8).
476
+ * Returns `{ algorithm, publicKey, fingerprint, value, signedAt }`. Requires
477
+ * `b.auditSign.init()` (throws `backup-manifest/no-signer` otherwise).
478
+ *
479
+ * @example
480
+ * var sig = b.backupManifest.signBytes(myCanonicalHeaderBuffer);
481
+ * // store sig alongside the header; later:
482
+ * var v = b.backupManifest.verifyBytes(myCanonicalHeaderBuffer, sig,
483
+ * { expectedFingerprint: b.auditSign.getPublicKeyFingerprint() });
484
+ * // v.ok === true
485
+ */
486
+ function signBytes(canonicalBytes) {
487
+ if (typeof canonicalBytes !== "string" && !Buffer.isBuffer(canonicalBytes)) {
488
+ throw new BackupManifestError("backup-manifest/bad-input",
489
+ "signBytes: canonicalBytes must be a string or Buffer");
490
+ }
491
+ return _signPayload(canonicalBytes, "signBytes");
492
+ }
493
+
494
+ // Verify a previously-signed manifest. Returns { ok, reason?,
495
+ // fingerprint? }. Caller policy decides whether a missing or
496
+ // fingerprint-mismatched signature is fatal — verifyManifestSignature
497
+ // in lib/backup/index.js wraps this with operator-facing semantics.
498
+ /**
499
+ * @primitive b.backupManifest.verifySignature
500
+ * @signature b.backupManifest.verifySignature(manifest, opts)
501
+ * @since 0.6.0
502
+ * @status stable
503
+ * @related b.backupManifest.sign, b.backupManifest.verifyBytes
504
+ *
505
+ * Verify a signed v1 backup manifest's detached `signature` block over its
506
+ * canonical bytes. Returns `{ ok, reason?, fingerprint? }` — never throws — so
507
+ * a caller decides whether a missing / mismatched signature is fatal. Pass
508
+ * `opts.expectedFingerprint` to pin the active audit-sign key and refuse a
509
+ * manifest signed under a different (rotated) key. For a schema-agnostic
510
+ * alternative see `verifyBytes`.
511
+ *
512
+ * @opts
513
+ * expectedFingerprint: string, // refuse a manifest whose fingerprint differs
514
+ *
515
+ * @example
516
+ * var v = b.backupManifest.verifySignature(manifest,
517
+ * { expectedFingerprint: b.auditSign.getPublicKeyFingerprint() });
518
+ * if (!v.ok) throw new Error("untrusted backup: " + v.reason);
519
+ */
520
+ function verifySignature(manifest, opts) {
521
+ if (!manifest || typeof manifest !== "object") {
522
+ return { ok: false, reason: "manifest must be an object" };
523
+ }
524
+ if (!manifest.signature || typeof manifest.signature !== "object") {
525
+ return { ok: false, reason: "manifest has no signature block" };
526
+ }
527
+ return _verifyPayloadAgainstBlock(signingPayload(manifest), manifest.signature, opts);
528
+ }
529
+
530
+ /**
531
+ * @primitive b.backupManifest.verifyBytes
532
+ * @signature b.backupManifest.verifyBytes(canonicalBytes, signatureBlock, opts?)
533
+ * @since 0.15.21
534
+ * @status stable
535
+ * @related b.backupManifest.signBytes, b.backupManifest.verifySignature
536
+ *
537
+ * Verify caller-supplied canonical bytes against a detached signature block
538
+ * produced by `signBytes()` — the schema-agnostic counterpart of
539
+ * `verifySignature()`. Returns `{ ok, reason?, fingerprint? }`. Pass
540
+ * `opts.expectedFingerprint` to pin the active audit-sign key and refuse a
541
+ * block signed under a different (rotated / historical) key. Falls back to
542
+ * `node:crypto.verify` when audit-sign isn't initialized, so a downstream
543
+ * verifier process can check a signature without holding the signing key.
544
+ *
545
+ * @opts
546
+ * expectedFingerprint: string, // refuse a block whose fingerprint differs
547
+ *
548
+ * @example
549
+ * var v = b.backupManifest.verifyBytes(headerBytes, sigBlock,
550
+ * { expectedFingerprint: trustedFingerprint });
551
+ * if (!v.ok) throw new Error("untrusted header: " + v.reason);
552
+ */
553
+ function verifyBytes(canonicalBytes, signatureBlock, opts) {
554
+ if (typeof canonicalBytes !== "string" && !Buffer.isBuffer(canonicalBytes)) {
555
+ return { ok: false, reason: "verifyBytes: canonicalBytes must be a string or Buffer" };
556
+ }
557
+ return _verifyPayloadAgainstBlock(canonicalBytes, signatureBlock, opts);
413
558
  }
414
559
 
415
560
  function parse(jsonStr) {
@@ -440,8 +585,10 @@ module.exports = {
440
585
  serialize: serialize,
441
586
  parse: parse,
442
587
  sign: sign,
588
+ signBytes: signBytes,
443
589
  signingPayload: signingPayload,
444
590
  verifySignature: verifySignature,
591
+ verifyBytes: verifyBytes,
445
592
  FORMAT_VERSION: FORMAT_VERSION,
446
593
  FRAMEWORK_NAME: FRAMEWORK_NAME,
447
594
  VALID_KINDS: VALID_KINDS,