@suveren/gateway 0.7.6 → 0.8.0

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 (161) hide show
  1. package/content/integrations/mollie.json +103 -28
  2. package/dist/mcp-server/http.mjs +228 -118
  3. package/dist/ui/assets/{index-CsrM6RQr.js → index-BFnSVa9N.js} +22 -22
  4. package/dist/ui/index.html +1 -1
  5. package/node_modules/@hap/core/dist/index.d.mts +29 -6
  6. package/node_modules/@hap/core/dist/index.d.ts +29 -6
  7. package/node_modules/@hap/core/dist/index.js +54 -8
  8. package/node_modules/@hap/core/dist/index.mjs +53 -8
  9. package/node_modules/@hap/core/package.json +2 -1
  10. package/node_modules/@hap/core/src/frame.ts +115 -14
  11. package/node_modules/@hap/core/src/gatekeeper.ts +26 -0
  12. package/node_modules/@hap/core/src/types.ts +7 -1
  13. package/node_modules/@types/node/README.md +1 -1
  14. package/node_modules/@types/node/buffer.buffer.d.ts +5 -0
  15. package/node_modules/@types/node/crypto.d.ts +0 -2
  16. package/node_modules/@types/node/dgram.d.ts +119 -4
  17. package/node_modules/@types/node/ffi.d.ts +1 -1
  18. package/node_modules/@types/node/fs/promises.d.ts +61 -65
  19. package/node_modules/@types/node/fs.d.ts +86 -76
  20. package/node_modules/@types/node/http.d.ts +1 -1
  21. package/node_modules/@types/node/http2.d.ts +2 -2
  22. package/node_modules/@types/node/index.d.ts +1 -0
  23. package/node_modules/@types/node/inspector.d.ts +2 -1
  24. package/node_modules/@types/node/net.d.ts +108 -16
  25. package/node_modules/@types/node/package.json +2 -2
  26. package/node_modules/@types/node/quic.d.ts +37 -11
  27. package/node_modules/@types/node/stream/iter.d.ts +4 -4
  28. package/node_modules/@types/node/tls.d.ts +24 -0
  29. package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +5 -0
  30. package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
  31. package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
  32. package/node_modules/@types/node/tty.d.ts +4 -3
  33. package/node_modules/@types/node/vfs.d.ts +210 -0
  34. package/node_modules/@types/node/vm.d.ts +1 -1
  35. package/node_modules/express-rate-limit/dist/index.cjs +6 -3
  36. package/node_modules/express-rate-limit/dist/index.d.cts +11 -0
  37. package/node_modules/express-rate-limit/dist/index.d.mts +11 -0
  38. package/node_modules/express-rate-limit/dist/index.d.ts +11 -0
  39. package/node_modules/express-rate-limit/dist/index.mjs +6 -3
  40. package/node_modules/express-rate-limit/{license.md → license} +7 -6
  41. package/node_modules/express-rate-limit/package.json +5 -5
  42. package/node_modules/express-rate-limit/readme.md +1 -0
  43. package/node_modules/fast-uri/index.js +20 -5
  44. package/node_modules/fast-uri/lib/utils.js +8 -1
  45. package/node_modules/fast-uri/package.json +1 -1
  46. package/node_modules/fast-uri/test/component-safe-serialization.test.js +58 -0
  47. package/node_modules/fast-uri/test/ipv6-validation.test.js +34 -0
  48. package/node_modules/ip-address/README.md +92 -82
  49. package/node_modules/ip-address/dist/common.d.ts +20 -0
  50. package/node_modules/ip-address/dist/common.js +36 -0
  51. package/node_modules/ip-address/dist/common.js.map +1 -1
  52. package/node_modules/ip-address/dist/ipv4.d.ts +50 -0
  53. package/node_modules/ip-address/dist/ipv4.js +75 -0
  54. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  55. package/node_modules/ip-address/dist/ipv6.d.ts +79 -10
  56. package/node_modules/ip-address/dist/ipv6.js +120 -30
  57. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  58. package/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
  59. package/node_modules/ip-address/dist/v4/constants.js +40 -1
  60. package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
  61. package/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
  62. package/node_modules/ip-address/dist/v6/constants.js +47 -1
  63. package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
  64. package/node_modules/ip-address/package.json +1 -1
  65. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +7 -16
  66. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +6 -17
  67. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +7 -16
  68. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +11 -34
  69. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +7 -16
  70. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +15 -35
  71. package/node_modules/jose/dist/types/jwk/embedded.d.ts +3 -8
  72. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +5 -5
  73. package/node_modules/jose/dist/types/jwks/local.d.ts +6 -10
  74. package/node_modules/jose/dist/types/jwks/remote.d.ts +13 -24
  75. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +5 -10
  76. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +9 -18
  77. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +7 -16
  78. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +9 -18
  79. package/node_modules/jose/dist/types/jws/general/sign.d.ts +11 -21
  80. package/node_modules/jose/dist/types/jws/general/verify.d.ts +9 -18
  81. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +10 -20
  82. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +6 -13
  83. package/node_modules/jose/dist/types/jwt/sign.d.ts +5 -6
  84. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -14
  85. package/node_modules/jose/dist/types/jwt/verify.d.ts +9 -19
  86. package/node_modules/jose/dist/types/key/export.d.ts +4 -4
  87. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +6 -8
  88. package/node_modules/jose/dist/types/key/generate_secret.d.ts +4 -11
  89. package/node_modules/jose/dist/types/key/import.d.ts +10 -17
  90. package/node_modules/jose/dist/types/types.d.ts +44 -84
  91. package/node_modules/jose/dist/types/util/base64url.d.ts +2 -2
  92. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +3 -10
  93. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +4 -4
  94. package/node_modules/jose/dist/types/util/errors.d.ts +26 -53
  95. package/node_modules/jose/dist/webapi/index.js +70 -32
  96. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +7 -8
  97. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +42 -48
  98. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +14 -14
  99. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +53 -64
  100. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +38 -49
  101. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +121 -169
  102. package/node_modules/jose/dist/webapi/jwk/embedded.js +30 -32
  103. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +54 -70
  104. package/node_modules/jose/dist/webapi/jwks/local.js +47 -64
  105. package/node_modules/jose/dist/webapi/jwks/remote.js +88 -136
  106. package/node_modules/jose/dist/webapi/jws/compact/sign.js +21 -21
  107. package/node_modules/jose/dist/webapi/jws/compact/verify.js +7 -8
  108. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +29 -31
  109. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +21 -24
  110. package/node_modules/jose/dist/webapi/jws/general/sign.js +61 -72
  111. package/node_modules/jose/dist/webapi/jws/general/verify.js +45 -69
  112. package/node_modules/jose/dist/webapi/jwt/decrypt.js +13 -20
  113. package/node_modules/jose/dist/webapi/jwt/encrypt.js +57 -70
  114. package/node_modules/jose/dist/webapi/jwt/sign.js +17 -16
  115. package/node_modules/jose/dist/webapi/jwt/unsecured.js +32 -40
  116. package/node_modules/jose/dist/webapi/jwt/verify.js +12 -14
  117. package/node_modules/jose/dist/webapi/key/export.js +32 -37
  118. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +40 -48
  119. package/node_modules/jose/dist/webapi/key/generate_secret.js +35 -40
  120. package/node_modules/jose/dist/webapi/key/import.js +52 -56
  121. package/node_modules/jose/dist/webapi/lib/asn1.js +130 -169
  122. package/node_modules/jose/dist/webapi/lib/base64.js +18 -20
  123. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +34 -38
  124. package/node_modules/jose/dist/webapi/lib/content_encryption.js +79 -115
  125. package/node_modules/jose/dist/webapi/lib/crypto_key.js +24 -25
  126. package/node_modules/jose/dist/webapi/lib/deflate.js +45 -47
  127. package/node_modules/jose/dist/webapi/lib/helpers.js +40 -37
  128. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +10 -24
  129. package/node_modules/jose/dist/webapi/lib/is_key_like.js +17 -15
  130. package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +106 -93
  131. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +207 -230
  132. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +104 -138
  133. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +17 -18
  134. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +9 -16
  135. package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +62 -61
  136. package/node_modules/jose/dist/webapi/lib/jws_sign.js +50 -75
  137. package/node_modules/jose/dist/webapi/lib/jws_verify.js +80 -125
  138. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +123 -209
  139. package/node_modules/jose/dist/webapi/lib/key.js +84 -136
  140. package/node_modules/jose/dist/webapi/lib/key_algorithm.js +13 -8
  141. package/node_modules/jose/dist/webapi/lib/key_descriptor.js +8 -6
  142. package/node_modules/jose/dist/webapi/lib/key_management.js +200 -231
  143. package/node_modules/jose/dist/webapi/lib/key_options.js +7 -5
  144. package/node_modules/jose/dist/webapi/lib/options.js +55 -68
  145. package/node_modules/jose/dist/webapi/lib/signing.js +18 -22
  146. package/node_modules/jose/dist/webapi/lib/type_checks.js +26 -29
  147. package/node_modules/jose/dist/webapi/util/base64url.js +26 -35
  148. package/node_modules/jose/dist/webapi/util/decode_jwt.js +32 -31
  149. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +18 -23
  150. package/node_modules/jose/dist/webapi/util/errors.js +94 -84
  151. package/node_modules/jose/package.json +17 -81
  152. package/package.json +2 -2
  153. package/profiles/calendar/0.5.profile.json +190 -0
  154. package/profiles/charge/0.5.profile.json +210 -0
  155. package/profiles/customers/0.7.profile.json +157 -0
  156. package/profiles/deploy/0.9.profile.json +176 -0
  157. package/profiles/email/0.6.profile.json +208 -0
  158. package/profiles/index.json +9 -1
  159. package/profiles/publish/0.5.profile.json +186 -0
  160. package/profiles/purchase/0.5.profile.json +231 -0
  161. package/profiles/records/0.5.profile.json +117 -0
@@ -408,6 +408,17 @@ export type Options = {
408
408
  * Defaults to `{limit}-in-{window}`.
409
409
  */
410
410
  identifier: string | ValueDeterminingMiddleware<string>;
411
+ /**
412
+ * Method to generate a custom `Retry-After` header value (in seconds) when
413
+ * a client is rate limited.
414
+ *
415
+ * This is advisory only, it customizes what the header says, it does not
416
+ * change how long the limiter actually keeps blocking the client for.
417
+ * That is still controlled by `windowMs`.
418
+ *
419
+ * By default, the number of seconds remaining until the window resets.
420
+ */
421
+ retryAfter?: number | ValueDeterminingMiddleware<number>;
411
422
  /**
412
423
  * The name of the property on the request object to store the rate limit info.
413
424
  *
@@ -408,6 +408,17 @@ export type Options = {
408
408
  * Defaults to `{limit}-in-{window}`.
409
409
  */
410
410
  identifier: string | ValueDeterminingMiddleware<string>;
411
+ /**
412
+ * Method to generate a custom `Retry-After` header value (in seconds) when
413
+ * a client is rate limited.
414
+ *
415
+ * This is advisory only, it customizes what the header says, it does not
416
+ * change how long the limiter actually keeps blocking the client for.
417
+ * That is still controlled by `windowMs`.
418
+ *
419
+ * By default, the number of seconds remaining until the window resets.
420
+ */
421
+ retryAfter?: number | ValueDeterminingMiddleware<number>;
411
422
  /**
412
423
  * The name of the property on the request object to store the rate limit info.
413
424
  *
@@ -254,9 +254,9 @@ var setDraft8Headers = (response, info, windowMs, name, key) => {
254
254
  response.append("RateLimit", `"${name}"; ${header}`);
255
255
  response.append("RateLimit-Policy", `"${name}"; ${policy}`);
256
256
  };
257
- var setRetryAfterHeader = (response, info, windowMs) => {
257
+ var setRetryAfterHeader = (response, info, windowMs, retryAfter) => {
258
258
  if (response.headersSent) return;
259
- const resetSeconds = getResetSeconds(windowMs, info.resetTime);
259
+ const resetSeconds = retryAfter ?? getResetSeconds(windowMs, info.resetTime);
260
260
  response.setHeader("Retry-After", resetSeconds.toString());
261
261
  };
262
262
 
@@ -523,6 +523,7 @@ var validations = {
523
523
  legacyHeaders: true,
524
524
  standardHeaders: true,
525
525
  identifier: true,
526
+ retryAfter: true,
526
527
  requestPropertyName: true,
527
528
  skipFailedRequests: true,
528
529
  skipSuccessfulRequests: true,
@@ -1007,7 +1008,9 @@ var rateLimit = (passedOptions) => {
1007
1008
  debug("limit exceeded");
1008
1009
  if (config.legacyHeaders || config.standardHeaders) {
1009
1010
  debug("set retry-after header");
1010
- setRetryAfterHeader(response, info, config.windowMs);
1011
+ const retrieveRetryAfter = typeof config.retryAfter === "function" ? config.retryAfter(request, response) : config.retryAfter;
1012
+ const retryAfter = await retrieveRetryAfter;
1013
+ setRetryAfterHeader(response, info, config.windowMs, retryAfter);
1011
1014
  }
1012
1015
  config.handler(request, response, next, options);
1013
1016
  return;
@@ -1,4 +1,4 @@
1
- # MIT License
1
+ MIT License
2
2
 
3
3
  Copyright 2023 Nathan Friedly, Vedant K
4
4
 
@@ -13,8 +13,9 @@ The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-rate-limit",
3
- "version": "8.6.2",
3
+ "version": "8.7.0",
4
4
  "description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
5
5
  "author": {
6
6
  "name": "Nathan Friedly",
@@ -90,16 +90,16 @@
90
90
  "@types/debug": "4.1.13",
91
91
  "@types/express": "5.0.6",
92
92
  "@types/jest": "30.0.0",
93
- "@types/node": "26.1.2",
93
+ "@types/node": "26.2.0",
94
94
  "@types/supertest": "7.2.1",
95
95
  "del-cli": "7.0.0",
96
96
  "dts-bundle-generator": "8.1.2",
97
- "esbuild": "0.28.1",
97
+ "esbuild": "0.28.2",
98
98
  "express": "5.2.1",
99
99
  "husky": "9.1.7",
100
100
  "jest": "30.4.2",
101
- "lint-staged": "17.2.0",
102
- "mintlify": "4.2.743",
101
+ "lint-staged": "17.3.0",
102
+ "mintlify": "4.2.817",
103
103
  "npm-run-all": "4.1.5",
104
104
  "prettier": "3.9.6",
105
105
  "ratelimit-header-parser": "0.1.0",
@@ -5,6 +5,7 @@
5
5
  [![tests](https://img.shields.io/github/actions/workflow/status/express-rate-limit/express-rate-limit/ci.yaml)](https://github.com/express-rate-limit/express-rate-limit/actions/workflows/ci.yaml)
6
6
  [![npm version](https://img.shields.io/npm/v/express-rate-limit.svg)](https://npmjs.org/package/express-rate-limit 'View this project on NPM')
7
7
  [![npm downloads](https://img.shields.io/npm/dm/express-rate-limit)](https://www.npmjs.com/package/express-rate-limit)
8
+ [![inspect.software](https://raw.githubusercontent.com/inspect-software/badges/main/v1/e/express-rate-limit/express-rate-limit.svg)](https://inspect.software/software/express-rate-limit/express-rate-limit)
8
9
  [![license](https://img.shields.io/npm/l/express-rate-limit)](license.md)
9
10
 
10
11
  </div>
@@ -324,6 +324,18 @@ function hasMalformedPercentEncoding (component) {
324
324
  return false
325
325
  }
326
326
 
327
+ /**
328
+ * Whether the host is a bracketed IP literal (RFC 3986 `IP-literal`).
329
+ * An unterminated `[` is not a literal, so it must still be validated as a
330
+ * reg-name instead of being waved through as an IP.
331
+ *
332
+ * @param {string} host
333
+ * @returns {boolean}
334
+ */
335
+ function isIPLiteral (host) {
336
+ return host[0] === '[' && host[host.length - 1] === ']'
337
+ }
338
+
327
339
  /**
328
340
  * @param {RegExpMatchArray} matches
329
341
  * @returns {boolean}
@@ -333,7 +345,7 @@ function hasMalformedComponentPercentEncoding (matches) {
333
345
  // compatibility. Their parsing is intentionally left to normalizeIPv6.
334
346
  const host = matches[4]
335
347
  return hasMalformedPercentEncoding(matches[3]) ||
336
- (host !== undefined && !(host[0] === '[' && host[host.length - 1] === ']') && hasMalformedPercentEncoding(host)) ||
348
+ (host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host)) ||
337
349
  hasMalformedPercentEncoding(matches[6]) ||
338
350
  hasMalformedPercentEncoding(matches[7]) ||
339
351
  hasMalformedPercentEncoding(matches[8])
@@ -351,7 +363,7 @@ function canonicalizeHost (parsed, options, schemeHandler, isIP) {
351
363
  !options.unicodeSupport &&
352
364
  (!schemeHandler || !schemeHandler.unicodeSupport) &&
353
365
  parsed.host &&
354
- parsed.host[0] !== '[' &&
366
+ !isIPLiteral(parsed.host) &&
355
367
  (options.domainHost || (schemeHandler && schemeHandler.domainHost)) &&
356
368
  isIP === false &&
357
369
  nonSimpleDomain(parsed.host)
@@ -476,10 +488,11 @@ function parseWithStatus (uri, opts) {
476
488
  if (parsed.host) {
477
489
  const ipv4result = isIPv4(parsed.host)
478
490
  if (ipv4result === false) {
479
- const bracketedIPLiteral = parsed.host[0] === '[' && parsed.host[parsed.host.length - 1] === ']'
491
+ const bracketedIPLiteral = isIPLiteral(parsed.host)
492
+ const hasIPLiteralBracket = parsed.host.indexOf('[') !== -1 || parsed.host.indexOf(']') !== -1
480
493
  const ipv6result = normalizeIPv6(parsed.host)
481
494
  isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true
482
- malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true
495
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true)
483
496
  parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase()
484
497
 
485
498
  if (malformedIPLiteral) {
@@ -509,7 +522,9 @@ function parseWithStatus (uri, opts) {
509
522
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme)
510
523
 
511
524
  // convert Unicode IDN -> ASCII IDN when the effective scheme uses domain hosts
512
- malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP)
525
+ if (!malformedIPLiteral) {
526
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP)
527
+ }
513
528
 
514
529
  if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
515
530
  if (uri.indexOf('%') !== -1) {
@@ -6,6 +6,9 @@ const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\d
6
6
  /** @type {(value: string) => boolean} */
7
7
  const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u)
8
8
 
9
+ /** @type {(value: string) => boolean} */
10
+ const isPort = RegExp.prototype.test.bind(/^\d*$/u)
11
+
9
12
  /** @type {(value: string) => boolean} */
10
13
  const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu)
11
14
 
@@ -713,8 +716,12 @@ function recomposeAuthority (component) {
713
716
  }
714
717
 
715
718
  if (typeof component.port === 'number' || typeof component.port === 'string') {
719
+ const port = String(component.port)
720
+ if (!isPort(port)) {
721
+ throw new TypeError('URI port is malformed.')
722
+ }
716
723
  uriTokens.push(':')
717
- uriTokens.push(String(component.port))
724
+ uriTokens.push(port)
718
725
  }
719
726
 
720
727
  return uriTokens.length ? uriTokens.join('') : undefined
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fast-uri",
3
3
  "description": "Dependency-free RFC 3986 URI toolbox",
4
- "version": "3.1.6",
4
+ "version": "3.1.7",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
7
7
  "types": "types/index.d.ts",
@@ -24,6 +24,64 @@ test('userinfo serialization cannot terminate the authority', (t) => {
24
24
  t.end()
25
25
  })
26
26
 
27
+ test('port serialization rejects non-digit values', (t) => {
28
+ const malformedPorts = [
29
+ '@127.0.0.1:8124',
30
+ '8080@evil.example',
31
+ '8080/path',
32
+ '8080?query',
33
+ '8080#fragment',
34
+ '8080:9000',
35
+ '-1',
36
+ '1.5',
37
+ 1.5,
38
+ NaN,
39
+ Infinity,
40
+ '\u0661'
41
+ ]
42
+
43
+ for (const port of malformedPorts) {
44
+ t.throws(
45
+ () => fastURI.serialize({ scheme: 'http', host: 'trusted.example', port, path: '/app' }),
46
+ /URI port is malformed\./,
47
+ String(port)
48
+ )
49
+ }
50
+
51
+ t.throws(
52
+ () => fastURI.normalize({ scheme: 'http', host: 'trusted.example', port: '@evil.example' }),
53
+ /URI port is malformed\./,
54
+ 'object normalization rejects a malformed port'
55
+ )
56
+ t.equal(
57
+ fastURI.equal(
58
+ { scheme: 'http', host: 'trusted.example', port: '@evil.example' },
59
+ { scheme: 'http', host: 'trusted.example', port: '@evil.example' }
60
+ ),
61
+ false,
62
+ 'object equality fails closed for a malformed port'
63
+ )
64
+ t.end()
65
+ })
66
+
67
+ test('port serialization preserves RFC 3986 digit values', (t) => {
68
+ const validPorts = [
69
+ [8080, '8080'],
70
+ ['8080', '8080'],
71
+ ['00080', '00080'],
72
+ ['', '']
73
+ ]
74
+
75
+ for (const [port, expected] of validPorts) {
76
+ t.equal(
77
+ fastURI.serialize({ scheme: 'uri', host: 'example.test', port }),
78
+ `uri://example.test:${expected}`,
79
+ JSON.stringify(port)
80
+ )
81
+ }
82
+ t.end()
83
+ })
84
+
27
85
  test('query and fragment serialization cannot inject component delimiters', (t) => {
28
86
  const uri = fastURI.serialize({
29
87
  scheme: 'x',
@@ -88,3 +88,37 @@ test('valid IPv6, IPvFuture, embedded IPv4, and zone forms normalize safely', (t
88
88
  }
89
89
  t.end()
90
90
  })
91
+
92
+ test('hosts with unbalanced or misplaced IP-literal brackets are rejected', (t) => {
93
+ const malformed = [
94
+ 'http://[fe80',
95
+ 'http://[',
96
+ 'http://[not-an-ip',
97
+ 'http://[\u65e5\u672c',
98
+ 'http://user@[@127.0.0.1:8123/admin',
99
+ 'http://user@]127.0.0.1:8123/admin',
100
+ 'http://user@prefix[@127.0.0.1:8123/admin',
101
+ 'http://user@prefix]@127.0.0.1:8123/admin'
102
+ ]
103
+ const modes = [
104
+ ['default', undefined],
105
+ ['Unicode', { unicodeSupport: true }]
106
+ ]
107
+
108
+ for (const [mode, options] of modes) {
109
+ for (const uri of malformed) {
110
+ const parsed = fastURI.parse(uri, options)
111
+ const message = `${mode} mode rejects ${uri}`
112
+
113
+ t.equal(parsed.error, HOST_ERROR, `parse ${message}`)
114
+ t.equal(fastURI.normalize(uri, options), uri, `normalize preserves ${uri} in ${mode} mode`)
115
+ t.equal(fastURI.equal(uri, uri, options), false, `equal ${message}`)
116
+ t.throws(
117
+ () => fastURI.resolve('http://example.com/', uri, options),
118
+ /URI host is malformed\./,
119
+ `resolve ${message}`
120
+ )
121
+ }
122
+ }
123
+ t.end()
124
+ })