@blamejs/blamejs-shop 0.5.19 → 0.5.20

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 (110) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/lib/admin.js +35 -40
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/externaldb-d1.js +31 -21
  5. package/lib/payment.js +103 -58
  6. package/lib/referrals.js +10 -6
  7. package/lib/security-middleware.js +39 -12
  8. package/lib/site-redirects.js +44 -121
  9. package/lib/storefront.js +19 -21
  10. package/lib/vendor/MANIFEST.json +107 -97
  11. package/lib/vendor/blamejs/CHANGELOG.md +180 -0
  12. package/lib/vendor/blamejs/NOTICE +14 -10
  13. package/lib/vendor/blamejs/README.md +17 -5
  14. package/lib/vendor/blamejs/index.js +2 -0
  15. package/lib/vendor/blamejs/lib/acme.js +9 -9
  16. package/lib/vendor/blamejs/lib/api-snapshot.js +35 -16
  17. package/lib/vendor/blamejs/lib/app-shutdown.js +1 -1
  18. package/lib/vendor/blamejs/lib/archive-adapters.js +1 -1
  19. package/lib/vendor/blamejs/lib/archive.js +13 -24
  20. package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -1
  21. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +3 -6
  22. package/lib/vendor/blamejs/lib/auth/ciba.js +32 -3
  23. package/lib/vendor/blamejs/lib/auth/oauth.js +65 -8
  24. package/lib/vendor/blamejs/lib/auth/password.js +1 -1
  25. package/lib/vendor/blamejs/lib/auth/saml.js +21 -3
  26. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +3 -3
  27. package/lib/vendor/blamejs/lib/backup/index.js +5 -5
  28. package/lib/vendor/blamejs/lib/case-fold-classes.js +491 -0
  29. package/lib/vendor/blamejs/lib/codepoint-class.js +131 -0
  30. package/lib/vendor/blamejs/lib/constants.js +79 -0
  31. package/lib/vendor/blamejs/lib/db-declare-view.js +8 -2
  32. package/lib/vendor/blamejs/lib/error-page.js +20 -1
  33. package/lib/vendor/blamejs/lib/external-db.js +5 -1
  34. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +24 -6
  35. package/lib/vendor/blamejs/lib/flag.js +28 -2
  36. package/lib/vendor/blamejs/lib/framework-schema.js +14 -3
  37. package/lib/vendor/blamejs/lib/guard-regex.js +2345 -89
  38. package/lib/vendor/blamejs/lib/http-client.js +217 -40
  39. package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
  40. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
  41. package/lib/vendor/blamejs/lib/mail-dav.js +26 -26
  42. package/lib/vendor/blamejs/lib/mail-deploy.js +10 -10
  43. package/lib/vendor/blamejs/lib/mail-scan.js +3 -0
  44. package/lib/vendor/blamejs/lib/mail-store.js +7 -1
  45. package/lib/vendor/blamejs/lib/mail.js +5 -1
  46. package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -2
  47. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +1 -1
  48. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -1
  49. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +3 -2
  50. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
  51. package/lib/vendor/blamejs/lib/middleware/compression.js +1 -1
  52. package/lib/vendor/blamejs/lib/middleware/cors.js +1 -1
  53. package/lib/vendor/blamejs/lib/middleware/csp-report.js +5 -5
  54. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +2 -1
  55. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +2 -2
  56. package/lib/vendor/blamejs/lib/middleware/deny-response.js +2 -1
  57. package/lib/vendor/blamejs/lib/middleware/dpop.js +2 -2
  58. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +2 -1
  59. package/lib/vendor/blamejs/lib/middleware/flag-context.js +17 -0
  60. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +10 -8
  61. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +3 -2
  62. package/lib/vendor/blamejs/lib/middleware/require-aal.js +3 -2
  63. package/lib/vendor/blamejs/lib/middleware/require-auth.js +4 -3
  64. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +4 -3
  65. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +3 -2
  66. package/lib/vendor/blamejs/lib/middleware/require-methods.js +3 -2
  67. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +3 -2
  68. package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -1
  69. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -1
  70. package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
  71. package/lib/vendor/blamejs/lib/network-dns.js +3 -3
  72. package/lib/vendor/blamejs/lib/network-nts.js +35 -14
  73. package/lib/vendor/blamejs/lib/network-proxy.js +42 -4
  74. package/lib/vendor/blamejs/lib/network-smtp-policy.js +1 -1
  75. package/lib/vendor/blamejs/lib/network-tls.js +341 -11
  76. package/lib/vendor/blamejs/lib/nist-crosswalk.js +2 -2
  77. package/lib/vendor/blamejs/lib/ntp-check.js +30 -19
  78. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -1
  79. package/lib/vendor/blamejs/lib/object-store/gcs.js +1 -1
  80. package/lib/vendor/blamejs/lib/object-store/http-put.js +2 -1
  81. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -5
  82. package/lib/vendor/blamejs/lib/object-store/sigv4.js +2 -2
  83. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +4 -2
  84. package/lib/vendor/blamejs/lib/openapi.js +1 -1
  85. package/lib/vendor/blamejs/lib/pqc-agent.js +4 -7
  86. package/lib/vendor/blamejs/lib/problem-details.js +2 -1
  87. package/lib/vendor/blamejs/lib/redis-client.js +12 -1
  88. package/lib/vendor/blamejs/lib/regex-linear.js +1349 -0
  89. package/lib/vendor/blamejs/lib/render.js +531 -7
  90. package/lib/vendor/blamejs/lib/request-helpers.js +181 -5
  91. package/lib/vendor/blamejs/lib/router.js +37 -13
  92. package/lib/vendor/blamejs/lib/safe-async.js +142 -0
  93. package/lib/vendor/blamejs/lib/safe-icap.js +3 -0
  94. package/lib/vendor/blamejs/lib/safe-sql.js +15 -3
  95. package/lib/vendor/blamejs/lib/self-update.js +2 -2
  96. package/lib/vendor/blamejs/lib/testing.js +54 -7
  97. package/lib/vendor/blamejs/lib/validate-opts.js +56 -0
  98. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +62 -25
  99. package/lib/vendor/blamejs/lib/vendor/blamejs-pki.cjs +2004 -142
  100. package/lib/vendor/blamejs/lib/vendor/browser/noble-ciphers.mjs +727 -0
  101. package/lib/vendor/blamejs/lib/vendor/browser/noble-hashes.mjs +1070 -0
  102. package/lib/vendor/blamejs/lib/vendor/browser/noble-post-quantum.mjs +1090 -0
  103. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +510 -227
  104. package/lib/vendor/blamejs/lib/webhook-hmac.js +19 -2
  105. package/lib/vendor/blamejs/lib/webhook.js +1 -1
  106. package/lib/vendor/blamejs/lib/ws-client.js +24 -2
  107. package/lib/vendor/blamejs/package.json +1 -1
  108. package/lib/vendor/blamejs/sbom.cdx.json +6 -6
  109. package/lib/webhook-receiver.js +43 -73
  110. package/package.json +1 -1
@@ -94,6 +94,7 @@ function _requireNonEmptyString(val, name) {
94
94
  * sigField: string, // default: "v1"
95
95
  * alg: string, // default: "hmac-sha256" (also "hmac-sha512")
96
96
  * toleranceSec: number, // default: 300 (5 minutes)
97
+ * now: number, // epoch ms for the replay window; default: Date.now()
97
98
  *
98
99
  * @example
99
100
  * var v = b.webhookHmac.verify({
@@ -107,7 +108,8 @@ function verify(opts) {
107
108
  opts = validateOpts.requireObject(opts, "webhookHmac.verify",
108
109
  WebhookHmacError, "webhook-hmac/bad-opts");
109
110
  validateOpts(opts,
110
- ["header", "rawBody", "secret", "profile", "tsField", "sigField", "alg", "toleranceSec"],
111
+ ["header", "rawBody", "secret", "profile", "tsField", "sigField", "alg", "toleranceSec",
112
+ "now"],
111
113
  "webhookHmac.verify");
112
114
 
113
115
  // Resolve profile → field/alg defaults; explicit opts win.
@@ -141,6 +143,21 @@ function verify(opts) {
141
143
  WebhookHmacError, "webhook-hmac/bad-tolerance");
142
144
  var tolerance = typeof opts.toleranceSec === "number" ? opts.toleranceSec : DEFAULT_TOLERANCE_SEC;
143
145
 
146
+ // Replay window measured against an injectable clock (epoch milliseconds,
147
+ // the shape b.cwt.verify already takes), defaulting to wall time. A caller
148
+ // that owns its own time source -- a receiver built around an injected
149
+ // clock, a forensic replay of a captured delivery, a deterministic
150
+ // simulation -- otherwise has to abandon this primitive and rebuild the
151
+ // scheme by hand, since its window check and this one disagree the moment
152
+ // the injected clock is not now.
153
+ //
154
+ // An injected clock rather than a skip-the-check flag: the replay defense
155
+ // stays inside the primitive, and omitting the option falls back to wall
156
+ // time, so forgetting it fails closed instead of silently accepting an
157
+ // arbitrarily old delivery.
158
+ numericBounds.requirePositiveFiniteIntIfPresent(opts.now, "now",
159
+ WebhookHmacError, "webhook-hmac/bad-now");
160
+
144
161
  // Parse "t=<ts>,v1=<sig>,v1=<rotated>" — comma-separated k=v. Collect the ts
145
162
  // and EVERY sigField value; ignore any other version keys.
146
163
  var tsRaw = null;
@@ -169,7 +186,7 @@ function verify(opts) {
169
186
  throw new WebhookHmacError("webhook-hmac/bad-timestamp",
170
187
  "verify: '" + tsField + "' is not a positive integer");
171
188
  }
172
- var nowSec = Math.floor(Date.now() / 1000);
189
+ var nowSec = Math.floor((typeof opts.now === "number" ? opts.now : Date.now()) / 1000);
173
190
  var skew = Math.abs(nowSec - ts);
174
191
  if (skew > tolerance) {
175
192
  throw new WebhookHmacError("webhook-hmac/timestamp-skew",
@@ -485,7 +485,7 @@ function signer(opts) {
485
485
  }, retryOpts);
486
486
  var statusCode = (res && (res.statusCode || res.status)) || 0;
487
487
  _emitEvent("webhook.send", 1, {
488
- outcome: statusCode >= 200 && statusCode < 300 ? "success" : "failure",
488
+ outcome: C.HTTP.success(statusCode) ? "success" : "failure",
489
489
  status: statusCode,
490
490
  host: hostLabel,
491
491
  });
@@ -381,7 +381,23 @@ class WsClient extends EventEmitter {
381
381
  (parsed.protocol === "wss:" ? 443 : 80); // TLS / HTTP default port
382
382
  var host = parsed.hostname;
383
383
 
384
- function _onError(err) { self._handleSocketError(err); }
384
+ // Filled in on the wss:// path below with the options the dial actually
385
+ // used. dialTlsOpts is merged OVER the shared posture, so a caller that
386
+ // narrowed ecdhCurve or moved minVersion negotiated on something the
387
+ // shared object does not describe, and diagnosing against the shared one
388
+ // would answer for a connection that was never made.
389
+ var effectiveTls = null;
390
+
391
+ function _onError(err) {
392
+ // A wss:// handshake refused by the posture arrives as a bare OpenSSL
393
+ // alert; name the cause before it reaches the caller's handler.
394
+ networkTls().annotateOutboundFailure(err, {
395
+ host: host,
396
+ port: port,
397
+ tlsOpts: effectiveTls || {},
398
+ });
399
+ self._handleSocketError(err);
400
+ }
385
401
 
386
402
  // Pin the connect to the SSRF-validated IPs returned by
387
403
  // ssrfGuard.checkUrl — closes the DNS-rebinding TOCTOU window where
@@ -413,6 +429,12 @@ class WsClient extends EventEmitter {
413
429
  port: port,
414
430
  rejectUnauthorized: true,
415
431
  }, networkTls().outboundPosture(), dialTlsOpts || {});
432
+ // The posture advertises RFC 8879 certificate compression, which is a
433
+ // TLS 1.3 extension. A caller who capped this dial lower asked for none
434
+ // of it, and Node refuses the whole options object rather than ignoring
435
+ // an extension it cannot use.
436
+ networkTls()._stripUnreachableCertCompression(tlsOpts, dialTlsOpts);
437
+ effectiveTls = tlsOpts;
416
438
  // RFC 6066 §3: the TLS SNI ServerName MUST be a hostname, never an IP
417
439
  // literal — Node throws ERR_INVALID_ARG_VALUE when servername is an IP,
418
440
  // which made wss:// to any IP-literal target unusable. Send SNI only for
@@ -541,7 +563,7 @@ class WsClient extends EventEmitter {
541
563
  return;
542
564
  }
543
565
  var status = parseInt(match[1], 10);
544
- if (status !== 101) { // HTTP 101
566
+ if (status !== C.HTTP.STATUS.SWITCHING_PROTOCOLS) { // HTTP 101
545
567
  // Body bytes after the header section are the server's
546
568
  // explanation. Surface them on the error so callers can branch
547
569
  // on the status code and inspect the body without re-parsing
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.18.17",
3
+ "version": "0.18.21",
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:64b32cc6-e5c4-4968-a1b3-33ca68975bed",
5
+ "serialNumber": "urn:uuid:885de67e-71f9-4512-83e7-3538ecfd9d88",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-08-08T17:14:55.062Z",
8
+ "timestamp": "2026-08-10T21:28:58.997Z",
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.18.17",
22
+ "bom-ref": "@blamejs/core@0.18.21",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.18.17",
25
+ "version": "0.18.21",
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.18.17",
29
+ "purl": "pkg:npm/%40blamejs/core@0.18.21",
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.18.17",
57
+ "ref": "@blamejs/core@0.18.21",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]
@@ -44,12 +44,14 @@
44
44
  * - `b.crypto.namespaceHash` — secret-at-rest fingerprint.
45
45
  * - `b.crypto.sha3Hash` — body fingerprint for audit.
46
46
  * - `b.crypto.generateBytes` — secret plaintext draw.
47
- * - `b.crypto.timingSafeEqual` — constant-time hex compare on
48
- * secret-hash + signature match.
49
- * - `b.webhook.sign` — HMAC-SHA-256 computation under
50
- * the Stripe-shape algorithm
51
- * (`t=<ts>,v1=<hex>`); we extract
52
- * the hex portion.
47
+ * - `b.crypto.timingSafeEqual` — constant-time hex compare on the
48
+ * secret-hash match.
49
+ * - `b.webhookHmac.verify` — the whole `t=<ts>,v1=<hex>` scheme:
50
+ * HMAC-SHA-256 over `<timestamp>.<raw-body>`,
51
+ * the replay window (on this receiver's
52
+ * injected clock and the source row's
53
+ * tolerance), and a constant-time compare
54
+ * across every signature the header carries.
53
55
  * - `b.uuid.v7` — row ids.
54
56
  *
55
57
  * Surface:
@@ -305,34 +307,6 @@ function _hashSecret(canonical) {
305
307
  return b.crypto.namespaceHash(SECRET_NAMESPACE, canonical);
306
308
  }
307
309
 
308
- // Extract the v1 hex tag from a `t=<ts>,v1=<hex>` Stripe-shape header
309
- // produced by b.webhook.sign. We pin to this composition rather than
310
- // reach for node:crypto so the receiver inherits whatever HMAC-SHA-256
311
- // hardening lives in the vendored blamejs surface.
312
- function _hmacSha256Hex(secretPlaintext, timestampSeconds, bodyString) {
313
- var header = b.webhook.sign({
314
- alg: "hmac-sha256-stripe",
315
- secret: secretPlaintext,
316
- body: bodyString,
317
- timestamp: timestampSeconds,
318
- });
319
- // Header shape is `t=<ts>,v1=<hex>` — the marker is the only place
320
- // ",v1=" appears so a literal indexOf is the canonical parse.
321
- var marker = ",v1=";
322
- var i = header.indexOf(marker);
323
- if (i < 0) {
324
- // Defensive: a future blamejs version that changes the shape
325
- // would slip past silently if we returned an empty string. Throw
326
- // loudly so the operator notices at deploy time, not at the
327
- // first inbound webhook.
328
- throw new Error(
329
- "webhookReceiver: b.webhook.sign returned an unexpected header shape " +
330
- "(no ',v1=' marker) — vendored blamejs may need a refresh"
331
- );
332
- }
333
- return header.slice(i + marker.length);
334
- }
335
-
336
310
  // ---- factory ------------------------------------------------------------
337
311
 
338
312
  function create(opts) {
@@ -610,40 +584,9 @@ function create(opts) {
610
584
  timestamp_out_of_window: false,
611
585
  };
612
586
  }
613
- var timestampSeconds = parseInt(tsStr, 10);
614
- if (!Number.isFinite(timestampSeconds) || timestampSeconds < 0) {
615
- return {
616
- ok: false,
617
- event_id: null,
618
- replay: false,
619
- signature_invalid: true,
620
- timestamp_out_of_window: false,
621
- };
622
- }
623
-
624
- // Replay-window check. The third-party's timestamp must land
625
- // within ±replay_window_seconds of the receiver's current
626
- // wall-clock. We accept future-skewed timestamps inside the
627
- // window (a peer with a slightly fast clock shouldn't reject)
628
- // but not arbitrarily-future timestamps that would let an
629
- // attacker bypass replay protection by stamping a fresh ts.
630
- var windowSec = Number(sourceRow.replay_window_seconds);
631
- var nowSec = Math.floor(nowMs / 1000);
632
- var ageSec = Math.abs(nowSec - timestampSeconds);
633
- if (ageSec > windowSec) {
634
- return {
635
- ok: false,
636
- event_id: null,
637
- replay: false,
638
- signature_invalid: false,
639
- timestamp_out_of_window: true,
640
- };
641
- }
642
-
643
- // Signature validation. The third-party signs the literal
644
- // `<timestamp>.<body>` string with HMAC-SHA-256 under the
645
- // secret. We recompute via b.webhook.sign and constant-time-
646
- // compare the supplied hex against the expected hex.
587
+ // The signature must be exactly the hex digest before it is composed
588
+ // into the header below — a value carrying a comma or an `=` would
589
+ // otherwise add fields the parser reads as its own.
647
590
  var supplied = input.signature_header.trim().toLowerCase();
648
591
  if (!SIGNATURE_HEX_RE.test(supplied)) {
649
592
  return {
@@ -654,15 +597,42 @@ function create(opts) {
654
597
  timestamp_out_of_window: false,
655
598
  };
656
599
  }
657
- var expected = _hmacSha256Hex(matched.plaintext, timestampSeconds, bodyBytes.toString("utf8"));
658
- var sigOk = b.crypto.timingSafeEqual(supplied, expected);
659
- if (!sigOk) {
600
+
601
+ // Signature + replay window, both from b.webhookHmac. It owns this exact
602
+ // scheme — `t=<ts>,v1=<hex>` over `<timestamp>.<raw-body>` — including
603
+ // the strict-integer timestamp, the skew window, a constant-time compare
604
+ // and multi-signature support for a rotated secret. This receiver takes
605
+ // its clock from the operator (opts.now) and its window from the source
606
+ // row, so both are passed in; the primitive gained the clock argument in
607
+ // 0.18.18, which is what made composing it possible at all.
608
+ //
609
+ // The body goes in as BYTES. Signing a UTF-8 string rebuild of it
610
+ // replaces anything that is not valid UTF-8 with U+FFFD, taking the
611
+ // digest over different bytes than the sender signed and rejecting a
612
+ // correctly-signed delivery. The timestamp goes in as the raw header
613
+ // text rather than a reparsed integer for the same reason.
614
+ var windowSec = Number(sourceRow.replay_window_seconds);
615
+ try {
616
+ b.webhookHmac.verify({
617
+ header: "t=" + tsStr + ",v1=" + supplied,
618
+ rawBody: bodyBytes,
619
+ secret: matched.plaintext,
620
+ profile: "stripe",
621
+ toleranceSec: windowSec,
622
+ now: nowMs,
623
+ });
624
+ } catch (e) {
625
+ // Out-of-window and bad-signature are distinct outcomes to an operator
626
+ // reading the delivery log: one is a clock or a replay, the other is a
627
+ // wrong secret. Every other refusal is a malformed header, which is
628
+ // not a signature the receiver can accept.
629
+ var skew = e && e.code === "webhook-hmac/timestamp-skew";
660
630
  return {
661
631
  ok: false,
662
632
  event_id: null,
663
633
  replay: false,
664
- signature_invalid: true,
665
- timestamp_out_of_window: false,
634
+ signature_invalid: !skew,
635
+ timestamp_out_of_window: skew,
666
636
  };
667
637
  }
668
638
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.5.19",
3
+ "version": "0.5.20",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {