@blamejs/blamejs-shop 0.5.19 → 0.5.21

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 (194) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -0
  3. package/SECURITY.md +17 -0
  4. package/lib/addresses.js +2 -2
  5. package/lib/admin.js +49 -45
  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 +4 -20
  16. package/lib/cms-blocks.js +4 -7
  17. package/lib/compliance-export.js +4 -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 +25 -14
  31. package/lib/email-warmup.js +2 -7
  32. package/lib/experiments.js +6 -10
  33. package/lib/externaldb-d1.js +78 -44
  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 +3 -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/payment.js +103 -58
  54. package/lib/pixel-events.js +8 -23
  55. package/lib/plan-changes.js +2 -7
  56. package/lib/product-qa.js +4 -8
  57. package/lib/promo-banners.js +7 -15
  58. package/lib/purchase-orders.js +3 -6
  59. package/lib/push-notifications.js +7 -32
  60. package/lib/pwa-manifest.js +6 -11
  61. package/lib/quotes.js +5 -9
  62. package/lib/referrals.js +10 -6
  63. package/lib/reviews.js +4 -10
  64. package/lib/robots-config.js +6 -14
  65. package/lib/sales-tax-filings.js +2 -2
  66. package/lib/search-facets.js +5 -5
  67. package/lib/search-ranking.js +4 -7
  68. package/lib/search-synonyms.js +7 -21
  69. package/lib/security-middleware.js +125 -70
  70. package/lib/seller-signup.js +10 -26
  71. package/lib/sidebar-widgets.js +6 -15
  72. package/lib/site-redirects.js +49 -137
  73. package/lib/sitemap-generator.js +3 -7
  74. package/lib/sms-dispatcher.js +7 -23
  75. package/lib/stock-receipts.js +4 -10
  76. package/lib/storefront-dashboards.js +2 -10
  77. package/lib/storefront-forms.js +9 -22
  78. package/lib/storefront-pages.js +6 -20
  79. package/lib/storefront.js +21 -23
  80. package/lib/subscription-billing.js +3 -8
  81. package/lib/subscription-controls.js +2 -7
  82. package/lib/suggestion-box.js +9 -26
  83. package/lib/support-tickets.js +6 -24
  84. package/lib/tax-remittance.js +2 -2
  85. package/lib/tenants.js +2 -7
  86. package/lib/text-guard.js +137 -35
  87. package/lib/theme-assets.js +4 -16
  88. package/lib/translations.js +3 -6
  89. package/lib/trust-badges.js +4 -7
  90. package/lib/vendor/MANIFEST.json +107 -97
  91. package/lib/vendor/blamejs/CHANGELOG.md +180 -0
  92. package/lib/vendor/blamejs/NOTICE +14 -10
  93. package/lib/vendor/blamejs/README.md +17 -5
  94. package/lib/vendor/blamejs/index.js +2 -0
  95. package/lib/vendor/blamejs/lib/acme.js +9 -9
  96. package/lib/vendor/blamejs/lib/api-snapshot.js +35 -16
  97. package/lib/vendor/blamejs/lib/app-shutdown.js +1 -1
  98. package/lib/vendor/blamejs/lib/archive-adapters.js +1 -1
  99. package/lib/vendor/blamejs/lib/archive.js +13 -24
  100. package/lib/vendor/blamejs/lib/auth/access-lock.js +2 -1
  101. package/lib/vendor/blamejs/lib/auth/bot-challenge.js +3 -6
  102. package/lib/vendor/blamejs/lib/auth/ciba.js +32 -3
  103. package/lib/vendor/blamejs/lib/auth/oauth.js +65 -8
  104. package/lib/vendor/blamejs/lib/auth/password.js +1 -1
  105. package/lib/vendor/blamejs/lib/auth/saml.js +21 -3
  106. package/lib/vendor/blamejs/lib/auth-bot-challenge.js +3 -3
  107. package/lib/vendor/blamejs/lib/backup/index.js +5 -5
  108. package/lib/vendor/blamejs/lib/case-fold-classes.js +491 -0
  109. package/lib/vendor/blamejs/lib/codepoint-class.js +131 -0
  110. package/lib/vendor/blamejs/lib/constants.js +79 -0
  111. package/lib/vendor/blamejs/lib/db-declare-view.js +8 -2
  112. package/lib/vendor/blamejs/lib/error-page.js +20 -1
  113. package/lib/vendor/blamejs/lib/external-db.js +5 -1
  114. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +24 -6
  115. package/lib/vendor/blamejs/lib/flag.js +28 -2
  116. package/lib/vendor/blamejs/lib/framework-schema.js +14 -3
  117. package/lib/vendor/blamejs/lib/guard-regex.js +2345 -89
  118. package/lib/vendor/blamejs/lib/http-client.js +217 -40
  119. package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
  120. package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +3 -2
  121. package/lib/vendor/blamejs/lib/mail-dav.js +26 -26
  122. package/lib/vendor/blamejs/lib/mail-deploy.js +10 -10
  123. package/lib/vendor/blamejs/lib/mail-scan.js +3 -0
  124. package/lib/vendor/blamejs/lib/mail-store.js +7 -1
  125. package/lib/vendor/blamejs/lib/mail.js +5 -1
  126. package/lib/vendor/blamejs/lib/middleware/age-gate.js +3 -2
  127. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +1 -1
  128. package/lib/vendor/blamejs/lib/middleware/assetlinks.js +2 -1
  129. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +3 -2
  130. package/lib/vendor/blamejs/lib/middleware/bearer-auth.js +2 -1
  131. package/lib/vendor/blamejs/lib/middleware/compression.js +1 -1
  132. package/lib/vendor/blamejs/lib/middleware/cors.js +1 -1
  133. package/lib/vendor/blamejs/lib/middleware/csp-report.js +5 -5
  134. package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +2 -1
  135. package/lib/vendor/blamejs/lib/middleware/daily-byte-quota.js +2 -2
  136. package/lib/vendor/blamejs/lib/middleware/deny-response.js +2 -1
  137. package/lib/vendor/blamejs/lib/middleware/dpop.js +2 -2
  138. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +2 -1
  139. package/lib/vendor/blamejs/lib/middleware/flag-context.js +17 -0
  140. package/lib/vendor/blamejs/lib/middleware/idempotency-key.js +10 -8
  141. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +3 -2
  142. package/lib/vendor/blamejs/lib/middleware/require-aal.js +3 -2
  143. package/lib/vendor/blamejs/lib/middleware/require-auth.js +4 -3
  144. package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +4 -3
  145. package/lib/vendor/blamejs/lib/middleware/require-content-type.js +3 -2
  146. package/lib/vendor/blamejs/lib/middleware/require-methods.js +3 -2
  147. package/lib/vendor/blamejs/lib/middleware/require-mtls.js +3 -2
  148. package/lib/vendor/blamejs/lib/middleware/security-txt.js +2 -1
  149. package/lib/vendor/blamejs/lib/middleware/web-app-manifest.js +2 -1
  150. package/lib/vendor/blamejs/lib/network-dns-resolver.js +1 -1
  151. package/lib/vendor/blamejs/lib/network-dns.js +3 -3
  152. package/lib/vendor/blamejs/lib/network-nts.js +35 -14
  153. package/lib/vendor/blamejs/lib/network-proxy.js +42 -4
  154. package/lib/vendor/blamejs/lib/network-smtp-policy.js +1 -1
  155. package/lib/vendor/blamejs/lib/network-tls.js +341 -11
  156. package/lib/vendor/blamejs/lib/nist-crosswalk.js +2 -2
  157. package/lib/vendor/blamejs/lib/ntp-check.js +30 -19
  158. package/lib/vendor/blamejs/lib/object-store/azure-blob.js +1 -1
  159. package/lib/vendor/blamejs/lib/object-store/gcs.js +1 -1
  160. package/lib/vendor/blamejs/lib/object-store/http-put.js +2 -1
  161. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +5 -5
  162. package/lib/vendor/blamejs/lib/object-store/sigv4.js +2 -2
  163. package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +4 -2
  164. package/lib/vendor/blamejs/lib/openapi.js +1 -1
  165. package/lib/vendor/blamejs/lib/pqc-agent.js +4 -7
  166. package/lib/vendor/blamejs/lib/problem-details.js +2 -1
  167. package/lib/vendor/blamejs/lib/redis-client.js +12 -1
  168. package/lib/vendor/blamejs/lib/regex-linear.js +1349 -0
  169. package/lib/vendor/blamejs/lib/render.js +531 -7
  170. package/lib/vendor/blamejs/lib/request-helpers.js +181 -5
  171. package/lib/vendor/blamejs/lib/router.js +37 -13
  172. package/lib/vendor/blamejs/lib/safe-async.js +142 -0
  173. package/lib/vendor/blamejs/lib/safe-icap.js +3 -0
  174. package/lib/vendor/blamejs/lib/safe-sql.js +15 -3
  175. package/lib/vendor/blamejs/lib/self-update.js +2 -2
  176. package/lib/vendor/blamejs/lib/testing.js +54 -7
  177. package/lib/vendor/blamejs/lib/validate-opts.js +56 -0
  178. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +62 -25
  179. package/lib/vendor/blamejs/lib/vendor/blamejs-pki.cjs +2004 -142
  180. package/lib/vendor/blamejs/lib/vendor/browser/noble-ciphers.mjs +727 -0
  181. package/lib/vendor/blamejs/lib/vendor/browser/noble-hashes.mjs +1070 -0
  182. package/lib/vendor/blamejs/lib/vendor/browser/noble-post-quantum.mjs +1090 -0
  183. package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +510 -227
  184. package/lib/vendor/blamejs/lib/webhook-hmac.js +19 -2
  185. package/lib/vendor/blamejs/lib/webhook.js +1 -1
  186. package/lib/vendor/blamejs/lib/ws-client.js +24 -2
  187. package/lib/vendor/blamejs/package.json +1 -1
  188. package/lib/vendor/blamejs/sbom.cdx.json +6 -6
  189. package/lib/vendor-invoices.js +3 -6
  190. package/lib/vendors.js +5 -19
  191. package/lib/webhook-receiver.js +46 -83
  192. package/lib/webhook-subscriptions.js +3 -13
  193. package/lib/wishlist-sharing.js +5 -10
  194. package/package.json +1 -1
@@ -296,6 +296,9 @@ function create(opts) {
296
296
  var threats = [];
297
297
  if (parsed.threatName) threats.push(parsed.threatName);
298
298
  resolve({
299
+ // ICAP's own codes (RFC 3507): 200 with the message returned, 204
300
+ // when it needed no change. They are not HTTP statuses, so they are
301
+ // not named as though they were.
299
302
  verdict: parsed.threatFound ? "infected" : (parsed.statusCode === 200 || parsed.statusCode === 204 ? "clean" : "error"),
300
303
  icapResponse: parsed,
301
304
  threats: threats,
@@ -158,7 +158,13 @@ function create(opts) {
158
158
  // expressions (the decrement update + the accumulate-on-conflict upsert),
159
159
  // which a structured set()/value cell cannot express. Every other table
160
160
  // identifier emits through the b.sql builders above with quoteName.
161
- var qQuota = safeSql.quoteIdentifier(quotaTable, "sqlite");
161
+ // The name is built from an operator `tablePrefix`, so it is quoted the way
162
+ // every other operator-supplied identifier is. It changes nothing today and
163
+ // is not meant to: `create()` refuses a reserved-word prefix outright, and
164
+ // the `_quota` suffix means this name could not be a bare keyword anyway.
165
+ // It is here so the rule reads the same at every call site rather than
166
+ // depending on a reader re-deriving those two facts.
167
+ var qQuota = safeSql.quoteIdentifier(quotaTable, "sqlite", { allowReserved: true });
162
168
 
163
169
  var maxMessageBytes = opts.maxMessageBytes !== undefined ? opts.maxMessageBytes : DEFAULT_MAX_MESSAGE_BYTES;
164
170
  var maxBodyBytes = opts.maxBodyBytes !== undefined ? opts.maxBodyBytes : DEFAULT_MAX_BODY_BYTES;
@@ -819,7 +819,11 @@ function _dotStuffForData(msg) {
819
819
  function _smtpTlsOpts(cfg, extra) {
820
820
  var out = Object.assign(networkTls().outboundPosture(), cfg.tlsOpts);
821
821
  if (extra) Object.assign(out, extra);
822
- return out;
822
+ // The posture advertises RFC 8879 certificate compression, which is a TLS
823
+ // 1.3 extension. An SMTP peer that only speaks TLS 1.2 is common enough
824
+ // that operators cap this dial; Node refuses the whole options object
825
+ // rather than ignoring an extension it cannot use.
826
+ return networkTls()._stripUnreachableCertCompression(out, cfg.tlsOpts);
823
827
  }
824
828
 
825
829
  function smtpTransport(opts) {
@@ -37,6 +37,7 @@
37
37
  * router.use(gate);
38
38
  */
39
39
 
40
+ var C = require("../constants");
40
41
  var defineClass = require("../framework-error").defineClass;
41
42
  var lazyRequire = require("../lazy-require");
42
43
  var validateOpts = require("../validate-opts");
@@ -165,8 +166,8 @@ function create(opts) {
165
166
  denyResponse(req, res, {
166
167
  onDeny: onDeny,
167
168
  problem: problemMode,
168
- status: 451, // HTTP 451 Unavailable For Legal Reasons
169
- info: { status: 451, reason: "parental-consent-required",
169
+ status: C.HTTP.STATUS.UNAVAILABLE_FOR_LEGAL_REASONS, // HTTP 451 Unavailable For Legal Reasons
170
+ info: { status: C.HTTP.STATUS.UNAVAILABLE_FOR_LEGAL_REASONS, reason: "parental-consent-required",
170
171
  age: age, classification: classification, requireAge: requireAge },
171
172
  problemCode: "parental-consent-required",
172
173
  problemTitle: "Unavailable For Legal Reasons",
@@ -376,7 +376,7 @@ function create(opts) {
376
376
  }
377
377
  numericBounds.requirePositiveFiniteInt(sessionMaxResponses,
378
378
  "apiEncrypt: sessionMaxResponses", ApiEncryptError, "BAD_OPT", null,
379
- { permanent: true, statusCode: 500 });
379
+ { permanent: true, statusCode: C.HTTP.STATUS.INTERNAL_SERVER_ERROR });
380
380
  // sessionStore — duck-typed handle exposing { get, set, delete }. The
381
381
  // helper optionalObjectWithMethod only checks one method; here we need
382
382
  // three. Inline shape kept; not a generic enough pattern to warrant a
@@ -27,6 +27,7 @@
27
27
  * multiple statement entries.
28
28
  */
29
29
 
30
+ var C = require("../constants");
30
31
  var lazyRequire = require("../lazy-require");
31
32
  var safeJson = require("../safe-json");
32
33
  var validateOpts = require("../validate-opts");
@@ -114,7 +115,7 @@ function create(opts) {
114
115
  denyResponse.methodNotAllowed(res, "GET, HEAD");
115
116
  return;
116
117
  }
117
- res.writeHead(200, { // HTTP 200 status
118
+ res.writeHead(C.HTTP.STATUS.OK, { // HTTP 200 status
118
119
  "Content-Type": "application/json; charset=utf-8",
119
120
  "Content-Length": bodyBuf.length,
120
121
  "Cache-Control": "public, max-age=86400",
@@ -21,6 +21,7 @@
21
21
  * paths / methods.
22
22
  */
23
23
 
24
+ var C = require("../constants");
24
25
  var nodeCrypto = require("node:crypto");
25
26
  var validateOpts = require("../validate-opts");
26
27
  var lazyRequire = require("../lazy-require");
@@ -157,7 +158,7 @@ function create(opts) {
157
158
  function _writeBody(req, res, body, etag, contentType) {
158
159
  var requestEtag = (req.headers && req.headers["if-none-match"]) || null;
159
160
  if (requestEtag && requestEtag === etag) {
160
- res.writeHead(304, { "ETag": etag, "Cache-Control": cacheControl }); // HTTP 304
161
+ res.writeHead(C.HTTP.STATUS.NOT_MODIFIED, { "ETag": etag, "Cache-Control": cacheControl }); // HTTP 304
161
162
  res.end();
162
163
  return;
163
164
  }
@@ -174,7 +175,7 @@ function create(opts) {
174
175
  // origin to another's request (Fetch Standard §3.2.1).
175
176
  if (allowOriginHeader !== "*") headers["Vary"] = "Origin";
176
177
  }
177
- res.writeHead(200, headers); // HTTP 200
178
+ res.writeHead(C.HTTP.STATUS.OK, headers); // HTTP 200
178
179
  // HEAD carries the GET headers (incl. Content-Length) with no body
179
180
  // (RFC 9110 §9.3.2).
180
181
  if ((req.method || "GET").toUpperCase() === "HEAD") { res.end(); return; }
@@ -36,6 +36,7 @@
36
36
  * route).
37
37
  */
38
38
 
39
+ var C = require("../constants");
39
40
  var lazyRequire = require("../lazy-require");
40
41
  var requestHelpers = require("../request-helpers");
41
42
  var validateOpts = require("../validate-opts");
@@ -56,7 +57,7 @@ function _refuse(req, res, status, challenge, bodyObj, reason, problemExt, onDen
56
57
  status: status,
57
58
  info: Object.assign({ status: status, reason: reason }, problemExt || {}),
58
59
  problemCode: "bearer-" + reason,
59
- problemTitle: status === 403 ? "Forbidden" : "Unauthorized",
60
+ problemTitle: status === C.HTTP.STATUS.FORBIDDEN ? "Forbidden" : "Unauthorized",
60
61
  problemDetail: typeof bodyObj.error === "string" ? bodyObj.error : ("bearer authentication failed: " + reason),
61
62
  problemExt: problemExt || null,
62
63
  headers: { "WWW-Authenticate": challenge },
@@ -334,7 +334,7 @@ function create(opts) {
334
334
  // UNtransformed (RFC 7233 §4.1) — compressing it drops Content-Length but
335
335
  // leaves Content-Range advertising an uncompressed byte interval over a
336
336
  // now-compressed body, corrupting range-assembling clients.
337
- if (statusCode === 206) { compress = false; return; }
337
+ if (statusCode === C.HTTP.STATUS.PARTIAL_CONTENT) { compress = false; return; }
338
338
  var crRange = (headersObj && headersObj["content-range"]) ||
339
339
  (originalGetHeader && originalGetHeader("Content-Range"));
340
340
  if (crRange) { compress = false; return; }
@@ -296,7 +296,7 @@ function create(opts) {
296
296
  onDeny: onDeny,
297
297
  problem: problemMode,
298
298
  status: requestHelpers.HTTP_STATUS.FORBIDDEN,
299
- info: Object.assign({ status: 403, reason: reason }, ext || {}),
299
+ info: Object.assign({ status: C.HTTP.STATUS.FORBIDDEN, reason: reason }, ext || {}),
300
300
  problemCode: "cors-refused",
301
301
  problemTitle: "Forbidden",
302
302
  problemDetail: body,
@@ -175,7 +175,7 @@ function create(opts) {
175
175
 
176
176
  return async function cspReport(req, res, _next) {
177
177
  if (req.method !== "POST") {
178
- res.writeHead(405, { "Allow": "POST" }); // HTTP 405 status
178
+ res.writeHead(C.HTTP.STATUS.METHOD_NOT_ALLOWED, { "Allow": "POST" }); // HTTP 405 status
179
179
  res.end();
180
180
  _emitReject(req, res, 405, "method-not-allowed");
181
181
  return;
@@ -184,7 +184,7 @@ function create(opts) {
184
184
  try {
185
185
  body = await safeBuffer.collectStream(req, { maxBytes: maxBytes });
186
186
  } catch (_e) {
187
- res.writeHead(413); // HTTP 413 status
187
+ res.writeHead(C.HTTP.STATUS.CONTENT_TOO_LARGE); // HTTP 413 status
188
188
  res.end();
189
189
  _emitReject(req, res, 413, "payload-too-large");
190
190
  return;
@@ -192,7 +192,7 @@ function create(opts) {
192
192
  var parsed;
193
193
  try { parsed = safeJson.parse(body.toString("utf8")); }
194
194
  catch (_e) {
195
- res.writeHead(400); // HTTP 400 status
195
+ res.writeHead(C.HTTP.STATUS.BAD_REQUEST); // HTTP 400 status
196
196
  res.end();
197
197
  _emitReject(req, res, 400, "invalid-json");
198
198
  return;
@@ -204,7 +204,7 @@ function create(opts) {
204
204
  // unauthenticated caller can force. A throwing/absent audit or hook is
205
205
  // unaffected — nothing is processed on this path.
206
206
  if (reports.length > maxReports) {
207
- res.writeHead(413); // HTTP 413 status
207
+ res.writeHead(C.HTTP.STATUS.CONTENT_TOO_LARGE); // HTTP 413 status
208
208
  res.end();
209
209
  _emitReject(req, res, 413, "too-many-reports");
210
210
  return;
@@ -225,7 +225,7 @@ function create(opts) {
225
225
  try { onReport(normalized); } catch (_e) { /* hook best-effort */ }
226
226
  }
227
227
  }
228
- res.writeHead(204); // HTTP 204 status
228
+ res.writeHead(C.HTTP.STATUS.NO_CONTENT); // HTTP 204 status
229
229
  res.end();
230
230
  };
231
231
  }
@@ -68,6 +68,7 @@
68
68
  * audit: true
69
69
  * }
70
70
  */
71
+ var C = require("../constants");
71
72
  var lazyRequire = require("../lazy-require");
72
73
  var pick = require("../pick");
73
74
  var forms = require("../forms");
@@ -235,7 +236,7 @@ function _writeReject(req, res, message, reason, onDeny, problemMode) {
235
236
  onDeny: onDeny,
236
237
  problem: problemMode,
237
238
  status: requestHelpers.HTTP_STATUS.FORBIDDEN,
238
- info: { status: 403, reason: reason },
239
+ info: { status: C.HTTP.STATUS.FORBIDDEN, reason: reason },
239
240
  problemCode: "csrf-refused",
240
241
  problemTitle: "Forbidden",
241
242
  problemDetail: message,
@@ -158,7 +158,7 @@ function create(opts) {
158
158
  _emitMetric("refused", 1, { reason: "quota-exceeded" });
159
159
  _emitAudit("refused", "denied", { key: key, total: total, quota: bytesPerDay });
160
160
  var info = {
161
- status: 429,
161
+ status: C.HTTP.STATUS.TOO_MANY_REQUESTS,
162
162
  reason: "quota-exceeded",
163
163
  quota: bytesPerDay,
164
164
  total: total,
@@ -167,7 +167,7 @@ function create(opts) {
167
167
  denyResponse(req, res, {
168
168
  onDeny: onDeny,
169
169
  problem: problemMode,
170
- status: 429,
170
+ status: C.HTTP.STATUS.TOO_MANY_REQUESTS,
171
171
  info: info,
172
172
  problemCode: "daily-byte-quota-exceeded",
173
173
  problemTitle: "Too Many Requests",
@@ -35,6 +35,7 @@
35
35
  * middleware that composes it.
36
36
  */
37
37
 
38
+ var C = require("../constants");
38
39
  var problemDetails = require("../problem-details");
39
40
  var validateOpts = require("../validate-opts");
40
41
 
@@ -158,7 +159,7 @@ function denyResponse(req, res, ctx) {
158
159
  // "GET, HEAD").
159
160
  function methodNotAllowed(res, allow) {
160
161
  var bodyMsg = "Method Not Allowed";
161
- res.writeHead(405, { // HTTP 405 status
162
+ res.writeHead(C.HTTP.STATUS.METHOD_NOT_ALLOWED, { // HTTP 405 status
162
163
  "Allow": allow,
163
164
  "Content-Type": "text/plain; charset=utf-8",
164
165
  "Content-Length": Buffer.byteLength(bodyMsg),
@@ -64,8 +64,8 @@ function _writeUnauthorized(req, res, errorCode, description, freshNonce, onDeny
64
64
  denyResponse(req, res, {
65
65
  onDeny: onDeny,
66
66
  problem: problemMode,
67
- status: 401, // HTTP 401 status
68
- info: { status: 401, reason: errorCode, error_description: description },
67
+ status: C.HTTP.STATUS.UNAUTHORIZED, // HTTP 401 status
68
+ info: { status: C.HTTP.STATUS.UNAUTHORIZED, reason: errorCode, error_description: description },
69
69
  problemCode: "dpop-" + errorCode.replace(/_/g, "-"),
70
70
  problemTitle: "Unauthorized",
71
71
  problemDetail: description,
@@ -35,6 +35,7 @@
35
35
  * own auth threat model.
36
36
  */
37
37
 
38
+ var C = require("../constants");
38
39
  var requestHelpers = require("../request-helpers");
39
40
  var validateOpts = require("../validate-opts");
40
41
  var lazyRequire = require("../lazy-require");
@@ -100,7 +101,7 @@ function _writeReject(req, res, message, reason, onDeny, problemMode) {
100
101
  onDeny: onDeny,
101
102
  problem: problemMode,
102
103
  status: requestHelpers.HTTP_STATUS.FORBIDDEN,
103
- info: { status: 403, reason: reason },
104
+ info: { status: C.HTTP.STATUS.FORBIDDEN, reason: reason },
104
105
  problemCode: "fetch-metadata-refused",
105
106
  problemTitle: "Forbidden",
106
107
  problemDetail: message,
@@ -31,6 +31,7 @@ var { defineClass } = require("../framework-error");
31
31
  var FlagError = defineClass("FlagError", { alwaysPermanent: true });
32
32
 
33
33
  var contextMod = lazyRequire(function () { return require("../flag-evaluation-context"); });
34
+ var requestHelpers = lazyRequire(function () { return require("../request-helpers"); });
34
35
 
35
36
  /**
36
37
  * @primitive b.middleware.flagContext
@@ -70,6 +71,7 @@ function create(opts) {
70
71
  opts = opts || {};
71
72
  validateOpts(opts, [
72
73
  "userKey", "userKeyHeader", "extractAttributes", "tenantKeyHeader",
74
+ "trustedProxies", "forwardedHeaders", "clientIpResolver",
73
75
  ], "middleware.flagContext");
74
76
  if (opts.extractAttributes != null && typeof opts.extractAttributes !== "function") {
75
77
  throw new FlagError("flag/bad-opt",
@@ -84,6 +86,14 @@ function create(opts) {
84
86
  var explicitUserKey = (typeof opts.userKey === "string" && opts.userKey.length > 0)
85
87
  ? opts.userKey
86
88
  : null;
89
+ var declaredTrust = opts.trustedProxies !== undefined ||
90
+ opts.forwardedHeaders !== undefined ||
91
+ opts.clientIpResolver !== undefined;
92
+ var trustedIp = declaredTrust ? requestHelpers().trustedClientIp({
93
+ trustedProxies: opts.trustedProxies,
94
+ forwardedHeaders: opts.forwardedHeaders,
95
+ clientIpResolver: opts.clientIpResolver,
96
+ }) : null;
87
97
 
88
98
  return function flagContextMiddleware(req, res, next) {
89
99
  var headers = req.headers || {};
@@ -104,6 +114,13 @@ function create(opts) {
104
114
  augment.tenantId = headers[tenantKeyHeader];
105
115
  }
106
116
  fromReqOpts.extra = augment;
117
+ // A request with no identified user is keyed by its client address, and
118
+ // that key picks the rollout bucket. Behind a reverse proxy the operator
119
+ // has to say which proxies and which header, or every anonymous caller
120
+ // arrives as the proxy and shares one bucket. Same options as
121
+ // flag.middleware; the resolver is built once, so a malformed CIDR fails
122
+ // at boot rather than on the first request.
123
+ if (trustedIp) fromReqOpts.clientIpResolver = trustedIp.resolve;
107
124
  req.flagCtx = contextMod().fromRequest(req, fromReqOpts);
108
125
  return next();
109
126
  };
@@ -734,7 +734,7 @@ function create(opts) {
734
734
  var missing = problemDetails().create({
735
735
  type: problemDetails().getBase() + "/idempotency/missing-key",
736
736
  title: "Idempotency-Key header required",
737
- status: 400, // HTTP status 400 Bad Request
737
+ status: C.HTTP.STATUS.BAD_REQUEST, // HTTP status 400 Bad Request
738
738
  detail: "This endpoint requires an Idempotency-Key header (draft-ietf-httpapi-idempotency-key).",
739
739
  });
740
740
  _emitAudit("idempotency.missing_key", { method: method, path: req.url }, "denied");
@@ -745,7 +745,7 @@ function create(opts) {
745
745
  var bad = problemDetails().create({
746
746
  type: problemDetails().getBase() + "/idempotency/bad-key",
747
747
  title: "Idempotency-Key malformed",
748
- status: 400, // HTTP status 400
748
+ status: C.HTTP.STATUS.BAD_REQUEST, // HTTP status 400
749
749
  detail: "Idempotency-Key must be ASCII printable, length 1.." + KEY_MAX_LEN + " (draft §2).",
750
750
  });
751
751
  _emitAudit("idempotency.bad_key", { method: method, keyLen: key.length }, "denied");
@@ -816,7 +816,7 @@ function create(opts) {
816
816
  var missingBody = problemDetails().create({
817
817
  type: problemDetails().getBase() + "/idempotency/missing-body-fingerprint",
818
818
  title: "Idempotency body fingerprint unavailable",
819
- status: 400, // HTTP status 400 Bad Request
819
+ status: C.HTTP.STATUS.BAD_REQUEST, // HTTP status 400 Bad Request
820
820
  detail: "The idempotency middleware could not derive a body fingerprint for this " +
821
821
  "request. Mount body-parser BEFORE the idempotency middleware, OR provide an " +
822
822
  "opts.bodyFingerprint(req) hook. To restore the pre-0.9.58 method+path-only " +
@@ -844,7 +844,7 @@ function create(opts) {
844
844
  var mismatch = problemDetails().create({
845
845
  type: problemDetails().getBase() + "/idempotency/key-reuse-mismatch",
846
846
  title: "Idempotency-Key reused with different request",
847
- status: 422, // HTTP status 422 Unprocessable Content (RFC 9110)
847
+ status: C.HTTP.STATUS.UNPROCESSABLE_CONTENT, // HTTP status 422 Unprocessable Content (RFC 9110)
848
848
  detail: "The Idempotency-Key matches a prior request but the request body/method/path differs (draft §4.3).",
849
849
  });
850
850
  _emitAudit("idempotency.key_reuse_mismatch",
@@ -900,10 +900,12 @@ function create(opts) {
900
900
  if (!captured) {
901
901
  captured = true;
902
902
  _pushChunk(chunk, encoding);
903
- var status = res.statusCode || 200; // default HTTP status 200
904
- // Only persist 2xx-4xx responses; 5xx is transient infra
905
- // failure that should be retried fresh, not replayed.
906
- if (!oversized && status >= 200 && status < 500) { // HTTP status class boundaries
903
+ var status = res.statusCode || C.HTTP.STATUS.OK;
904
+ // Only persist what the SERVER settled: a success, a redirect, or the
905
+ // caller's own mistake. A 5xx is transient infrastructure failure that
906
+ // should be retried fresh rather than replayed from a cache.
907
+ if (!oversized &&
908
+ (C.HTTP.success(status) || C.HTTP.redirect(status) || C.HTTP.clientError(status))) {
907
909
  var headerMap = {};
908
910
  try {
909
911
  var allHeaders = typeof res.getHeaders === "function" ? res.getHeaders() : {};
@@ -29,6 +29,7 @@
29
29
  * origin is validated and echoed verbatim with `Vary: Origin`.
30
30
  */
31
31
 
32
+ var C = require("../constants");
32
33
  var nodeCrypto = require("node:crypto");
33
34
  var validateOpts = require("../validate-opts");
34
35
  var lazyRequire = require("../lazy-require");
@@ -163,7 +164,7 @@ function create(opts) {
163
164
  function _writeBody(req, res, body, etag, contentType) {
164
165
  var requestEtag = (req.headers && req.headers["if-none-match"]) || null;
165
166
  if (requestEtag && requestEtag === etag) {
166
- res.writeHead(304, { "ETag": etag, "Cache-Control": cacheControl }); // HTTP 304
167
+ res.writeHead(C.HTTP.STATUS.NOT_MODIFIED, { "ETag": etag, "Cache-Control": cacheControl }); // HTTP 304
167
168
  res.end();
168
169
  return;
169
170
  }
@@ -180,7 +181,7 @@ function create(opts) {
180
181
  // origin to another's request (Fetch Standard §3.2.1).
181
182
  if (allowOriginHeader !== "*") headers["Vary"] = "Origin";
182
183
  }
183
- res.writeHead(200, headers); // HTTP 200
184
+ res.writeHead(C.HTTP.STATUS.OK, headers); // HTTP 200
184
185
  // HEAD carries the GET headers (incl. Content-Length) with no body
185
186
  // (RFC 9110 §9.3.2).
186
187
  if ((req.method || "GET").toUpperCase() === "HEAD") { res.end(); return; }
@@ -20,6 +20,7 @@
20
20
  * auth.aal.denied — request below the required minimum
21
21
  */
22
22
 
23
+ var C = require("../constants");
23
24
  var lazyRequire = require("../lazy-require");
24
25
  var requestHelpers = require("../request-helpers");
25
26
  var validateOpts = require("../validate-opts");
@@ -42,8 +43,8 @@ function _writeUnauthorized(req, res, requiredBand, actualBand, realm, onDeny, p
42
43
  denyResponse(req, res, {
43
44
  onDeny: onDeny,
44
45
  problem: problemMode,
45
- status: 401, // HTTP 401 status
46
- info: { status: 401, reason: "step_up_required",
46
+ status: C.HTTP.STATUS.UNAUTHORIZED, // HTTP 401 status
47
+ info: { status: C.HTTP.STATUS.UNAUTHORIZED, reason: "step_up_required",
47
48
  required_aal: requiredBand, actual_aal: actualBand || null },
48
49
  problemCode: "step-up-required",
49
50
  problemTitle: "Step-Up Authentication Required",
@@ -38,6 +38,7 @@
38
38
  * audit: true (emit auth.required.denied on reject)
39
39
  * }
40
40
  */
41
+ var C = require("../constants");
41
42
  var lazyRequire = require("../lazy-require");
42
43
  var requestHelpers = require("../request-helpers");
43
44
  var validateOpts = require("../validate-opts");
@@ -118,7 +119,7 @@ function create(opts) {
118
119
  // before the default content-negotiation runs.
119
120
  if (onDeny) {
120
121
  try {
121
- var returned = onDeny(req, res, { status: 401, reason: "no-authenticated-user", redirectTo: redirectTo });
122
+ var returned = onDeny(req, res, { status: C.HTTP.STATUS.UNAUTHORIZED, reason: "no-authenticated-user", redirectTo: redirectTo });
122
123
  if (res.writableEnded) return returned;
123
124
  } catch (_e) {
124
125
  if (res.writableEnded) return;
@@ -138,7 +139,7 @@ function create(opts) {
138
139
  if (!wantsJson && redirectTo) {
139
140
  if (!res.writableEnded && typeof res.writeHead === "function") {
140
141
  // 302 Found — RFC 7231 §6.4.3. Not in HTTP_STATUS table.
141
- res.writeHead(302, { "Location": redirectTo, "Cache-Control": "no-store" });
142
+ res.writeHead(C.HTTP.STATUS.FOUND, { "Location": redirectTo, "Cache-Control": "no-store" });
142
143
  res.end();
143
144
  }
144
145
  return;
@@ -146,7 +147,7 @@ function create(opts) {
146
147
  denyResponse(req, res, {
147
148
  problem: problemMode,
148
149
  status: requestHelpers.HTTP_STATUS.UNAUTHORIZED,
149
- info: { status: 401, reason: "no-authenticated-user" },
150
+ info: { status: C.HTTP.STATUS.UNAUTHORIZED, reason: "no-authenticated-user" },
150
151
  problemCode: "authentication-required",
151
152
  problemTitle: "Unauthorized",
152
153
  problemDetail: msg,
@@ -50,6 +50,7 @@
50
50
  * undefined → refused with reason "resolver-unavailable".
51
51
  */
52
52
 
53
+ var C = require("../constants");
53
54
  var defineClass = require("../framework-error").defineClass;
54
55
  var lazyRequire = require("../lazy-require");
55
56
  var validateOpts = require("../validate-opts");
@@ -177,12 +178,12 @@ function create(opts) {
177
178
  // are not authentication challenges, so they advertise the scheme
178
179
  // without an (incorrect) auth-error code.
179
180
  function _bearerChallenge(status, reason) {
180
- if (status === 401) {
181
+ if (status === C.HTTP.STATUS.UNAUTHORIZED) {
181
182
  if (reason === "no-bearer-token") return 'Bearer realm="api"';
182
183
  return 'Bearer realm="api", error="invalid_token"';
183
184
  }
184
- if (status === 403) return 'Bearer realm="api", error="insufficient_scope"';
185
- if (status === 400) return 'Bearer realm="api", error="invalid_request"'; // HTTP 400
185
+ if (status === C.HTTP.STATUS.FORBIDDEN) return 'Bearer realm="api", error="insufficient_scope"';
186
+ if (status === C.HTTP.STATUS.BAD_REQUEST) return 'Bearer realm="api", error="invalid_request"'; // HTTP 400
186
187
  return 'Bearer realm="api"';
187
188
  }
188
189
 
@@ -19,6 +19,7 @@
19
19
  * `methods` to override.
20
20
  */
21
21
 
22
+ var C = require("../constants");
22
23
  var lazyRequire = require("../lazy-require");
23
24
  var denyResponse = require("./deny-response").denyResponse;
24
25
  var { defineClass } = require("../framework-error");
@@ -100,8 +101,8 @@ function create(allowed, opts) {
100
101
  denyResponse(req, res, {
101
102
  onDeny: onDeny,
102
103
  problem: problemMode,
103
- status: 415,
104
- info: { status: 415, reason: "unsupported-media-type",
104
+ status: C.HTTP.STATUS.UNSUPPORTED_MEDIA_TYPE,
105
+ info: { status: C.HTTP.STATUS.UNSUPPORTED_MEDIA_TYPE, reason: "unsupported-media-type",
105
106
  contentType: bare || null, accepted: normalized },
106
107
  problemCode: "unsupported-media-type",
107
108
  problemTitle: "Unsupported Media Type",
@@ -17,6 +17,7 @@
17
17
  * RFC 9110 §15.5.6.
18
18
  */
19
19
 
20
+ var C = require("../constants");
20
21
  var lazyRequire = require("../lazy-require");
21
22
  var denyResponse = require("./deny-response").denyResponse;
22
23
  var { defineClass } = require("../framework-error");
@@ -84,8 +85,8 @@ function create(allowed, opts) {
84
85
  denyResponse(req, res, {
85
86
  onDeny: onDeny,
86
87
  problem: problemMode,
87
- status: 405,
88
- info: { status: 405, reason: "method-not-allowed", method: m, allowed: normalized },
88
+ status: C.HTTP.STATUS.METHOD_NOT_ALLOWED,
89
+ info: { status: C.HTTP.STATUS.METHOD_NOT_ALLOWED, reason: "method-not-allowed", method: m, allowed: normalized },
89
90
  problemCode: "method-not-allowed",
90
91
  problemTitle: "Method Not Allowed",
91
92
  problemDetail: "The " + m + " method is not allowed on this resource.",
@@ -47,6 +47,7 @@
47
47
  * allow-list ALSO requires the fingerprint to match.
48
48
  */
49
49
 
50
+ var C = require("../constants");
50
51
  var nodeCrypto = require("node:crypto");
51
52
  var defineClass = require("../framework-error").defineClass;
52
53
  var lazyRequire = require("../lazy-require");
@@ -164,8 +165,8 @@ function create(opts) {
164
165
  denyResponse(req, res, {
165
166
  onDeny: onDeny,
166
167
  problem: problemMode,
167
- status: 401,
168
- info: Object.assign({ status: 401, reason: reason }, metadata || {}),
168
+ status: C.HTTP.STATUS.UNAUTHORIZED,
169
+ info: Object.assign({ status: C.HTTP.STATUS.UNAUTHORIZED, reason: reason }, metadata || {}),
169
170
  problemCode: "client-certificate-required",
170
171
  problemTitle: "Unauthorized",
171
172
  problemDetail: errorMessage,
@@ -27,6 +27,7 @@
27
27
  * also throws when the operator-supplied `expires` is in the past.
28
28
  */
29
29
 
30
+ var C = require("../constants");
30
31
  var lazyRequire = require("../lazy-require");
31
32
  var validateOpts = require("../validate-opts");
32
33
  var denyResponse = require("./deny-response");
@@ -149,7 +150,7 @@ function create(opts) {
149
150
  denyResponse.methodNotAllowed(res, "GET, HEAD");
150
151
  return;
151
152
  }
152
- res.writeHead(200, { // HTTP 200 status
153
+ res.writeHead(C.HTTP.STATUS.OK, { // HTTP 200 status
153
154
  "Content-Type": "text/plain; charset=utf-8",
154
155
  "Content-Length": bodyBuf.length,
155
156
  "Cache-Control": "public, max-age=86400",
@@ -28,6 +28,7 @@
28
28
  * of those are missing.
29
29
  */
30
30
 
31
+ var C = require("../constants");
31
32
  var lazyRequire = require("../lazy-require");
32
33
  var safeJson = require("../safe-json");
33
34
  var validateOpts = require("../validate-opts");
@@ -141,7 +142,7 @@ function create(opts) {
141
142
  denyResponse.methodNotAllowed(res, "GET, HEAD");
142
143
  return;
143
144
  }
144
- res.writeHead(200, { // HTTP 200 status
145
+ res.writeHead(C.HTTP.STATUS.OK, { // HTTP 200 status
145
146
  "Content-Type": "application/manifest+json",
146
147
  "Content-Length": bodyBuf.length,
147
148
  "Cache-Control": "public, max-age=86400",
@@ -557,7 +557,7 @@ async function _wireLookup(name, qtype, timeoutMs) {
557
557
  res.on("end", function () {
558
558
  try {
559
559
  if (pushFailed) { _fail(pushFailed); return; }
560
- if (res.statusCode !== 200) { // HTTP 200 OK
560
+ if (res.statusCode !== C.HTTP.STATUS.OK) { // HTTP 200 OK
561
561
  _fail(new ResolverError("resolver/upstream-http",
562
562
  "DoH HTTP " + res.statusCode + " for " + name));
563
563
  return;
@@ -581,7 +581,7 @@ async function _dohLookup(host, family) {
581
581
  try {
582
582
  if (pushFailed) { reject(pushFailed); return; }
583
583
  var body = collector.result();
584
- if (res.statusCode !== 200) {
584
+ if (res.statusCode !== C.HTTP.STATUS.OK) {
585
585
  reject(new DnsError("dns/doh-http", "DoH HTTP " + res.statusCode + " for " + host));
586
586
  return;
587
587
  }
@@ -641,7 +641,7 @@ async function _dohLookupSecure(host, family) {
641
641
  try {
642
642
  if (pushFailed) { reject(pushFailed); return; }
643
643
  var body = collector.result();
644
- if (res.statusCode !== 200) { // HTTP 200 OK
644
+ if (res.statusCode !== C.HTTP.STATUS.OK) { // HTTP 200 OK
645
645
  reject(new DnsError("dns/doh-http", "DoH HTTP " + res.statusCode + " for " + host));
646
646
  return;
647
647
  }
@@ -1005,7 +1005,7 @@ async function _dohRawQuery(host, qtype) {
1005
1005
  res.on("end", function () {
1006
1006
  try {
1007
1007
  if (pushFailed) { reject(pushFailed); return; }
1008
- if (res.statusCode !== 200) { // HTTP 200 OK
1008
+ if (res.statusCode !== C.HTTP.STATUS.OK) { // HTTP 200 OK
1009
1009
  reject(new DnsError("dns/doh-http", "DoH HTTP " + res.statusCode + " for " + host));
1010
1010
  return;
1011
1011
  }