@blamejs/blamejs-shop 0.4.90 → 0.4.92
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 +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/gift-card-ledger.js +8 -2
- package/lib/order.js +1 -0
- package/lib/vendor/MANIFEST.json +49 -41
- package/lib/vendor/blamejs/CHANGELOG.md +6 -0
- package/lib/vendor/blamejs/SECURITY.md +1 -0
- package/lib/vendor/blamejs/api-snapshot.json +208 -2
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +7 -4
- package/lib/vendor/blamejs/examples/wiki/test/integration.js +15 -12
- package/lib/vendor/blamejs/index.js +2 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +34 -1
- package/lib/vendor/blamejs/lib/backup/manifest.js +191 -44
- package/lib/vendor/blamejs/lib/codepoint-class.js +284 -77
- package/lib/vendor/blamejs/lib/framework-error.js +14 -0
- package/lib/vendor/blamejs/lib/fsm.js +80 -24
- package/lib/vendor/blamejs/lib/log.js +32 -0
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +18 -2
- package/lib/vendor/blamejs/lib/middleware/request-id.js +24 -4
- package/lib/vendor/blamejs/lib/request-helpers.js +50 -0
- package/lib/vendor/blamejs/lib/safe-path.js +24 -10
- package/lib/vendor/blamejs/lib/sql.js +133 -0
- package/lib/vendor/blamejs/lib/totp.js +98 -33
- package/lib/vendor/blamejs/lib/ws-client.js +39 -28
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.15.21.json +51 -0
- package/lib/vendor/blamejs/release-notes/v0.15.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.15.23.json +22 -0
- package/lib/vendor/blamejs/test/00-primitives.js +80 -0
- package/lib/vendor/blamejs/test/_smoke-worker.js +81 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +7 -4
- package/lib/vendor/blamejs/test/integration/mail-crypto-smime.test.js +7 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +91 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/defineguard-default-gate-posture-caps.test.js +5 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-replaystore-required.test.js +9 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/fsm.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/money.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/otlp-attr-redaction.test.js +9 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-id-async-context.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +64 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +55 -0
- package/lib/vendor/blamejs/test/smoke.js +93 -10
- 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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
331
|
+
who + ": audit-sign.sign threw: " + ((e && e.message) || String(e)));
|
|
334
332
|
}
|
|
335
|
-
|
|
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
|
|
346
|
-
//
|
|
347
|
-
// fingerprint
|
|
348
|
-
|
|
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 (!
|
|
352
|
-
return { ok: false, reason: "
|
|
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
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
//
|
|
388
|
-
//
|
|
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:
|
|
401
|
-
reason:
|
|
402
|
-
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
|
-
|
|
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,
|