@blamejs/blamejs-shop 0.5.20 → 0.5.22

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 (102) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/SECURITY.md +23 -0
  4. package/lib/addresses.js +17 -3
  5. package/lib/admin.js +14 -5
  6. package/lib/affiliates.js +7 -23
  7. package/lib/announcement-bar.js +5 -22
  8. package/lib/api-keys.js +4 -13
  9. package/lib/asset-manifest.json +1 -1
  10. package/lib/banner-ab-tests.js +5 -9
  11. package/lib/blog-articles.js +8 -26
  12. package/lib/captcha-gate.js +7 -17
  13. package/lib/carrier-accounts.js +3 -10
  14. package/lib/catalog-drafts.js +5 -12
  15. package/lib/category-navigation.js +7 -20
  16. package/lib/cms-blocks.js +4 -7
  17. package/lib/compliance-export.js +11 -11
  18. package/lib/consent-ledger.js +2 -4
  19. package/lib/cost-layers.js +56 -21
  20. package/lib/customer-import.js +2 -2
  21. package/lib/customer-notes.js +7 -23
  22. package/lib/customer-roles.js +3 -5
  23. package/lib/customer-segments.js +17 -14
  24. package/lib/customer-surveys.js +7 -14
  25. package/lib/customers.js +2 -4
  26. package/lib/dispute-resolution.js +5 -13
  27. package/lib/dropship-forwarding.js +3 -5
  28. package/lib/dunning.js +2 -7
  29. package/lib/email-ab-tests.js +4 -8
  30. package/lib/email-campaigns.js +39 -15
  31. package/lib/email-warmup.js +2 -7
  32. package/lib/experiments.js +6 -10
  33. package/lib/externaldb-d1.js +47 -31
  34. package/lib/geolocation.js +18 -28
  35. package/lib/gift-options.js +5 -23
  36. package/lib/knowledge-base.js +7 -22
  37. package/lib/line-gift-wrap.js +7 -17
  38. package/lib/live-chat.js +5 -18
  39. package/lib/metered-usage.js +3 -8
  40. package/lib/notifications.js +3 -7
  41. package/lib/operator-accounts.js +3 -5
  42. package/lib/operator-approvals.js +6 -8
  43. package/lib/operator-audit-log.js +2 -7
  44. package/lib/operator-help-center.js +5 -21
  45. package/lib/operator-inbox.js +4 -11
  46. package/lib/operator-roles.js +5 -7
  47. package/lib/order-escalation.js +2 -2
  48. package/lib/order-export.js +24 -20
  49. package/lib/order-notes.js +4 -14
  50. package/lib/order-ratings.js +4 -10
  51. package/lib/payment-methods.js +5 -13
  52. package/lib/payment-retries.js +2 -7
  53. package/lib/pixel-events.js +21 -28
  54. package/lib/plan-changes.js +2 -7
  55. package/lib/product-qa.js +4 -8
  56. package/lib/promo-banners.js +7 -15
  57. package/lib/purchase-orders.js +3 -6
  58. package/lib/push-notifications.js +7 -32
  59. package/lib/pwa-manifest.js +6 -11
  60. package/lib/quotes.js +5 -9
  61. package/lib/reviews.js +4 -10
  62. package/lib/robots-config.js +6 -14
  63. package/lib/sales-tax-filings.js +2 -2
  64. package/lib/search-facets.js +5 -5
  65. package/lib/search-ranking.js +4 -7
  66. package/lib/search-synonyms.js +7 -21
  67. package/lib/security-middleware.js +86 -58
  68. package/lib/seller-signup.js +10 -26
  69. package/lib/sidebar-widgets.js +6 -15
  70. package/lib/site-redirects.js +5 -16
  71. package/lib/sitemap-generator.js +3 -7
  72. package/lib/sms-dispatcher.js +7 -23
  73. package/lib/stock-receipts.js +4 -10
  74. package/lib/storefront-dashboards.js +2 -10
  75. package/lib/storefront-forms.js +9 -22
  76. package/lib/storefront-pages.js +6 -20
  77. package/lib/storefront.js +2 -2
  78. package/lib/subscription-billing.js +3 -8
  79. package/lib/subscription-controls.js +2 -7
  80. package/lib/suggestion-box.js +9 -26
  81. package/lib/support-tickets.js +6 -24
  82. package/lib/tax-remittance.js +2 -2
  83. package/lib/tenants.js +2 -7
  84. package/lib/text-guard.js +179 -35
  85. package/lib/theme-assets.js +4 -16
  86. package/lib/translations.js +3 -6
  87. package/lib/trust-badges.js +4 -7
  88. package/lib/vendor/MANIFEST.json +12 -12
  89. package/lib/vendor/blamejs/CHANGELOG.md +32 -0
  90. package/lib/vendor/blamejs/NOTICE +1 -1
  91. package/lib/vendor/blamejs/README.md +1 -1
  92. package/lib/vendor/blamejs/lib/network-tls.js +98 -13
  93. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +12 -12
  94. package/lib/vendor/blamejs/lib/vendor/blamejs-pki.cjs +568 -304
  95. package/lib/vendor/blamejs/package.json +1 -1
  96. package/lib/vendor/blamejs/sbom.cdx.json +6 -6
  97. package/lib/vendor-invoices.js +3 -6
  98. package/lib/vendors.js +5 -19
  99. package/lib/webhook-receiver.js +3 -10
  100. package/lib/webhook-subscriptions.js +3 -13
  101. package/lib/wishlist-sharing.js +5 -10
  102. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.18.21",
3
+ "version": "0.18.24",
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:885de67e-71f9-4512-83e7-3538ecfd9d88",
5
+ "serialNumber": "urn:uuid:6fba0fbc-d16f-4c2c-864f-99f1c662d2af",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-08-10T21:28:58.997Z",
8
+ "timestamp": "2026-08-11T01:57:40.816Z",
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.21",
22
+ "bom-ref": "@blamejs/core@0.18.24",
23
23
  "type": "application",
24
24
  "name": "blamejs",
25
- "version": "0.18.21",
25
+ "version": "0.18.24",
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.21",
29
+ "purl": "pkg:npm/%40blamejs/core@0.18.24",
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.21",
57
+ "ref": "@blamejs/core@0.18.24",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]
@@ -95,10 +95,6 @@ var CURRENCY_RE = /^[A-Z]{3}$/;
95
95
  // as the sibling accounts-payable primitives — operator-rendered text
96
96
  // fields refuse these to keep the dashboard + printout safe from
97
97
  // header-injection + visual-spoofing attacks.
98
- var CONTROL_BYTE_RE = /[\x00-\x1f\x7f]/;
99
- var ZERO_WIDTH_RE = new RegExp(
100
- "[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF\\u061C]"
101
- );
102
98
 
103
99
  var INVOICE_STATUSES = Object.freeze([
104
100
  "received", "approved", "paid", "disputed", "voided",
@@ -107,6 +103,7 @@ var INVOICE_STATUSES = Object.freeze([
107
103
  var MAX_DUE_WITHIN_DAYS = 3650; // ~10 years — bound the predicate
108
104
 
109
105
  var b = require("./vendor/blamejs");
106
+ var textGuard = require("./text-guard");
110
107
 
111
108
  var MS_PER_DAY = b.constants.TIME.days(1);
112
109
 
@@ -219,10 +216,10 @@ function _shortText(s, label, max) {
219
216
  if (s.length > max) {
220
217
  throw new TypeError("vendor-invoices: " + label + " must be <= " + max + " characters");
221
218
  }
222
- if (CONTROL_BYTE_RE.test(s)) {
219
+ if (textGuard.hasCodepointThreat(s, { singleLine: "reject" })) {
223
220
  throw new TypeError("vendor-invoices: " + label + " contains control bytes");
224
221
  }
225
- if (ZERO_WIDTH_RE.test(s)) {
222
+ if (textGuard.hasCodepointThreat(s, { zeroWidth: "reject" })) {
226
223
  throw new TypeError("vendor-invoices: " + label + " contains zero-width / direction-override bytes");
227
224
  }
228
225
  return s;
package/lib/vendors.js CHANGED
@@ -97,10 +97,6 @@ var PHONE_RE = /^\+?[1-9]\d{1,14}$/;
97
97
  // direction-override bytes are a slipping-class for header injection
98
98
  // + visual-spoofing attacks downstream. Spelled with \u-escapes so
99
99
  // ESLint's no-irregular-whitespace stays happy.
100
- var CONTROL_BYTE_STRICT_RE = /[\x00-\x1f\x7f]/;
101
- var ZERO_WIDTH_RE = new RegExp(
102
- "[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF\\u061C]"
103
- );
104
100
 
105
101
  // Mutable columns for `updateVendor(slug, patch)`. Slug is immutable
106
102
  // — it's the public handle + the join-table key; mutating it would
@@ -119,6 +115,7 @@ var ALLOWED_UPDATE_COLUMNS = Object.freeze([
119
115
 
120
116
  // Framework handle (the vendored blamejs); index.js re-exports this as .framework.
121
117
  var b = require("./vendor/blamejs");
118
+ var textGuard = require("./text-guard");
122
119
 
123
120
  // ---- validators ---------------------------------------------------------
124
121
 
@@ -159,12 +156,7 @@ function _name(s) {
159
156
  if (s.length > MAX_NAME_LEN) {
160
157
  throw new TypeError("vendors: name must be <= " + MAX_NAME_LEN + " characters");
161
158
  }
162
- if (CONTROL_BYTE_STRICT_RE.test(s)) {
163
- throw new TypeError("vendors: name contains control bytes");
164
- }
165
- if (ZERO_WIDTH_RE.test(s)) {
166
- throw new TypeError("vendors: name contains zero-width / direction-override bytes");
167
- }
159
+ textGuard.freeText(s, "vendors: name", { singleLine: "reject", zeroWidth: "reject", bidiMarks: "allow" });
168
160
  return s;
169
161
  }
170
162
 
@@ -186,9 +178,7 @@ function _payoutAddress(s) {
186
178
  if (s.length > MAX_PAYOUT_ADDRESS_LEN) {
187
179
  throw new TypeError("vendors: payout_address must be <= " + MAX_PAYOUT_ADDRESS_LEN + " characters");
188
180
  }
189
- if (CONTROL_BYTE_STRICT_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
190
- throw new TypeError("vendors: payout_address contains control / zero-width bytes");
191
- }
181
+ textGuard.freeText(s, "vendors: payout_address", { singleLine: "reject", zeroWidth: "reject", bidiMarks: "allow" });
192
182
  return s;
193
183
  }
194
184
 
@@ -221,9 +211,7 @@ function _addressJson(value) {
221
211
  if (encoded.length > MAX_ADDRESS_JSON_LEN) {
222
212
  throw new TypeError("vendors: address JSON must be <= " + MAX_ADDRESS_JSON_LEN + " characters serialised");
223
213
  }
224
- if (CONTROL_BYTE_STRICT_RE.test(encoded) || ZERO_WIDTH_RE.test(encoded)) {
225
- throw new TypeError("vendors: address contains control / zero-width bytes");
226
- }
214
+ textGuard.freeText(encoded, "vendors: address", { singleLine: "reject", zeroWidth: "reject", bidiMarks: "allow" });
227
215
  return encoded;
228
216
  }
229
217
 
@@ -265,9 +253,7 @@ function _orderId(s) {
265
253
  if (s.length > 256) {
266
254
  throw new TypeError("vendors: order_id must be <= 256 characters");
267
255
  }
268
- if (CONTROL_BYTE_STRICT_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
269
- throw new TypeError("vendors: order_id contains control / zero-width bytes");
270
- }
256
+ textGuard.freeText(s, "vendors: order_id", { singleLine: "reject", zeroWidth: "reject" });
271
257
  return s;
272
258
  }
273
259
 
@@ -79,6 +79,7 @@
79
79
  */
80
80
 
81
81
  var b = require("./vendor/blamejs");
82
+ var textGuard = require("./text-guard");
82
83
  var C = b.constants;
83
84
 
84
85
  var SECRET_NAMESPACE = "webhook-receiver-secret";
@@ -118,10 +119,6 @@ var MAX_REASON_LEN = 1024;
118
119
 
119
120
  var SIGNATURE_HEX_RE = /^[0-9a-f]{64}$/i; // 32 bytes of HMAC-SHA-256 = 64 hex chars
120
121
 
121
- var CONTROL_BYTE_STRICT_RE = /[\x00-\x1f\x7f]/;
122
- var ZERO_WIDTH_RE = new RegExp(
123
- "[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF\\u061C]"
124
- );
125
122
 
126
123
  var STATUSES = ["received", "processed", "failed"];
127
124
 
@@ -198,9 +195,7 @@ function _outcome(s) {
198
195
  if (s.length > MAX_OUTCOME_LEN) {
199
196
  throw new TypeError("webhookReceiver: outcome must be <= " + MAX_OUTCOME_LEN + " characters");
200
197
  }
201
- if (CONTROL_BYTE_STRICT_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
202
- throw new TypeError("webhookReceiver: outcome contains control / zero-width bytes");
203
- }
198
+ textGuard.freeText(s, "webhookReceiver: outcome", { singleLine: "reject", zeroWidth: "reject" });
204
199
  return s;
205
200
  }
206
201
 
@@ -215,9 +210,7 @@ function _reason(s) {
215
210
  if (s.length > MAX_REASON_LEN) {
216
211
  throw new TypeError("webhookReceiver: reason must be <= " + MAX_REASON_LEN + " characters");
217
212
  }
218
- if (CONTROL_BYTE_STRICT_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
219
- throw new TypeError("webhookReceiver: reason contains control / zero-width bytes");
220
- }
213
+ textGuard.freeText(s, "webhookReceiver: reason", { singleLine: "reject", zeroWidth: "reject", bidiMarks: "allow" });
221
214
  return s;
222
215
  }
223
216
 
@@ -68,10 +68,6 @@ var WILDCARD_EVENT = "*";
68
68
  // control bytes are a slipping-class for header injection + visual
69
69
  // spoofing downstream. Spelled with \u-escapes so ESLint's
70
70
  // no-irregular-whitespace stays happy.
71
- var CONTROL_BYTE_RE = /[\x00-\x1f\x7f]/;
72
- var ZERO_WIDTH_RE = new RegExp(
73
- "[\\u200B-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF\\u061C]"
74
- );
75
71
 
76
72
  // Event-type identifier shape — `domain.action[.qualifier]` segments
77
73
  // of ASCII letters / digits / underscore, joined by `.`. The wildcard
@@ -111,9 +107,7 @@ function _ownerId(s) {
111
107
  if (s.length > MAX_OWNER_ID_LEN) {
112
108
  throw new TypeError("webhookSubscriptions: owner_id must be <= " + MAX_OWNER_ID_LEN + " characters");
113
109
  }
114
- if (CONTROL_BYTE_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
115
- throw new TypeError("webhookSubscriptions: owner_id contains control / zero-width bytes");
116
- }
110
+ textGuard.freeText(s, "webhookSubscriptions: owner_id", { singleLine: "reject", zeroWidth: "reject" });
117
111
  return s;
118
112
  }
119
113
 
@@ -191,9 +185,7 @@ function _name(s) {
191
185
  if (s.length > MAX_NAME_LEN) {
192
186
  throw new TypeError("webhookSubscriptions: name must be <= " + MAX_NAME_LEN + " characters");
193
187
  }
194
- if (CONTROL_BYTE_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
195
- throw new TypeError("webhookSubscriptions: name contains control / zero-width bytes");
196
- }
188
+ textGuard.freeText(s, "webhookSubscriptions: name", { singleLine: "reject", zeroWidth: "reject", bidiMarks: "allow" });
197
189
  return s;
198
190
  }
199
191
 
@@ -219,9 +211,7 @@ function _signingSecret(s) {
219
211
  if (s.length > 512) {
220
212
  throw new TypeError("webhookSubscriptions: signing_secret must be <= 512 characters");
221
213
  }
222
- if (CONTROL_BYTE_RE.test(s) || ZERO_WIDTH_RE.test(s)) {
223
- throw new TypeError("webhookSubscriptions: signing_secret contains control / zero-width bytes");
224
- }
214
+ textGuard.freeText(s, "webhookSubscriptions: signing_secret", { singleLine: "reject", zeroWidth: "reject" });
225
215
  return s;
226
216
  }
227
217
 
@@ -63,6 +63,7 @@
63
63
  */
64
64
 
65
65
  var b = require("./vendor/blamejs");
66
+ var textGuard = require("./text-guard");
66
67
  var wishlistModule = require("./wishlist");
67
68
 
68
69
  // ---- constants ----------------------------------------------------------
@@ -82,8 +83,6 @@ var MAX_REASON_LEN = 500;
82
83
  var MAX_EMAIL_LEN = 254;
83
84
  var MAX_MEMBER_EMAILS = 50;
84
85
 
85
- var CONTROL_BYTE_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;
86
- var CONTROL_BYTE_STRICT_RE = /[\x00-\x1f\x7f]/;
87
86
 
88
87
  // Conservative single-line email shape — defence in depth against
89
88
  // header-injection / control-byte smuggling when the operator
@@ -127,7 +126,7 @@ function _title(s, label) {
127
126
  if (typeof s !== "string" || s.length > MAX_TITLE_LEN) {
128
127
  throw new TypeError("wishlistSharing: " + label + " must be a string <= " + MAX_TITLE_LEN + " chars");
129
128
  }
130
- if (CONTROL_BYTE_RE.test(s)) {
129
+ if (textGuard.hasCodepointThreat(s)) {
131
130
  throw new TypeError("wishlistSharing: " + label + " must not contain control bytes");
132
131
  }
133
132
  return s;
@@ -137,7 +136,7 @@ function _requiredTitle(s, label) {
137
136
  if (typeof s !== "string" || !s.length || s.length > MAX_TITLE_LEN) {
138
137
  throw new TypeError("wishlistSharing: " + label + " must be a non-empty string <= " + MAX_TITLE_LEN + " chars");
139
138
  }
140
- if (CONTROL_BYTE_RE.test(s)) {
139
+ if (textGuard.hasCodepointThreat(s)) {
141
140
  throw new TypeError("wishlistSharing: " + label + " must not contain control bytes");
142
141
  }
143
142
  return s;
@@ -162,9 +161,7 @@ function _reason(s) {
162
161
  if (typeof s !== "string" || !s.length || s.length > MAX_REASON_LEN) {
163
162
  throw new TypeError("wishlistSharing: reason must be a non-empty string <= " + MAX_REASON_LEN + " chars");
164
163
  }
165
- if (CONTROL_BYTE_RE.test(s)) {
166
- throw new TypeError("wishlistSharing: reason must not contain control bytes");
167
- }
164
+ textGuard.freeText(s, "wishlistSharing: reason", { bidiMarks: "allow" });
168
165
  return s;
169
166
  }
170
167
 
@@ -205,9 +202,7 @@ function _viewerSessionId(s) {
205
202
  // Session ids are opaque to this primitive; refuse control bytes
206
203
  // so the hashed breadcrumb has no log-injection vector even if a
207
204
  // downstream tool ever rendered the raw value.
208
- if (CONTROL_BYTE_STRICT_RE.test(s)) {
209
- throw new TypeError("wishlistSharing: viewer_session_id must not contain control bytes");
210
- }
205
+ textGuard.freeText(s, "wishlistSharing: viewer_session_id", { singleLine: "reject" });
211
206
  return s;
212
207
  }
213
208
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.5.20",
3
+ "version": "0.5.22",
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": {