@suveren/gateway 0.38.0 → 0.39.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.
@@ -23,7 +23,7 @@ const { normalizeOptions } = require('../utils')
23
23
  module.exports = json
24
24
 
25
25
  /**
26
- * RegExp to match the first non-space in a string.
26
+ * RegExp to match the first non-whitespace character in a string.
27
27
  *
28
28
  * Allowed whitespace is defined in RFC 7159:
29
29
  *
@@ -33,7 +33,7 @@ module.exports = json
33
33
  * %x0A / ; Line feed or New line
34
34
  * %x0D ) ; Carriage return
35
35
  */
36
- const FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
36
+ const FIRST_NON_WHITESPACE_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d]|$)/ // eslint-disable-line no-control-regex
37
37
 
38
38
  const JSON_SYNTAX_CHAR = '#'
39
39
  const JSON_SYNTAX_REGEXP = /#+/g
@@ -80,10 +80,12 @@ function createJsonParser (options) {
80
80
  return {}
81
81
  }
82
82
 
83
- const first = firstchar(body)
84
- if (first !== '{' && first !== '[') {
85
- debug('strict violation')
86
- throw createStrictSyntaxError(body, first)
83
+ if (body[0] !== '{' && body[0] !== '[') {
84
+ const first = firstNonWhitespaceChar(body)
85
+ if (first !== '{' && first !== '[') {
86
+ debug('strict violation')
87
+ throw createStrictSyntaxError(body, first)
88
+ }
87
89
  }
88
90
 
89
91
  try {
@@ -152,12 +154,10 @@ function createStrictSyntaxError (str, char) {
152
154
  * @returns {string|undefined}
153
155
  * @private
154
156
  */
155
- function firstchar (str) {
156
- const match = FIRST_CHAR_REGEXP.exec(str)
157
+ function firstNonWhitespaceChar (str) {
158
+ const match = FIRST_NON_WHITESPACE_REGEXP.exec(str)
157
159
 
158
- return match
159
- ? match[1]
160
- : undefined
160
+ return match && match[1] ? match[1] : undefined
161
161
  }
162
162
 
163
163
  /**
@@ -98,12 +98,12 @@ function createQueryParser (options) {
98
98
  try {
99
99
  return qs.parse(body, {
100
100
  allowPrototypes: true,
101
- arrayLimit: arrayLimit,
102
- depth: depth,
103
- charsetSentinel: charsetSentinel,
104
- interpretNumericEntities: interpretNumericEntities,
101
+ arrayLimit,
102
+ depth,
103
+ charsetSentinel,
104
+ interpretNumericEntities,
105
105
  charset: encoding,
106
- parameterLimit: parameterLimit,
106
+ parameterLimit,
107
107
  strictDepth: true
108
108
  })
109
109
  } catch (err) {
@@ -5,31 +5,16 @@
5
5
  */
6
6
 
7
7
  const bytes = require('bytes')
8
- const contentType = require('content-type')
9
8
  const typeis = require('type-is')
10
9
 
11
10
  /**
12
11
  * Module exports.
13
12
  */
14
13
  module.exports = {
15
- getCharset,
16
14
  normalizeOptions,
17
15
  passthrough
18
16
  }
19
17
 
20
- /**
21
- * Get the charset of a request.
22
- *
23
- * @param {Object} req
24
- * @returns {string | undefined}
25
- * @private
26
- */
27
- function getCharset (req) {
28
- const header = req.headers['content-type']
29
- if (!header) return undefined
30
- return contentType.parse(header).parameters.charset?.toLowerCase()
31
- }
32
-
33
18
  /**
34
19
  * Get the simple type checker.
35
20
  *
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "body-parser",
3
3
  "description": "Node.js body parsing middleware",
4
- "version": "2.3.0",
4
+ "version": "2.4.0",
5
5
  "contributors": [
6
6
  "Douglas Christopher Wilson <doug@somethingdoug.com>",
7
7
  "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@@ -27,26 +27,21 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "bytes": "^3.1.2",
30
- "content-type": "^2.0.0",
30
+ "content-type": "^2.1.0",
31
31
  "debug": "^4.4.3",
32
32
  "http-errors": "^2.0.1",
33
- "iconv-lite": "^0.7.2",
33
+ "iconv-lite": "^0.7.3",
34
34
  "on-finished": "^2.4.1",
35
- "qs": "^6.15.2",
35
+ "qs": "^6.16.1",
36
36
  "raw-body": "^3.0.2",
37
37
  "type-is": "^2.1.0"
38
38
  },
39
39
  "devDependencies": {
40
- "eslint": "^8.57.1",
41
- "eslint-config-standard": "^14.1.1",
42
- "eslint-plugin-import": "^2.32.0",
43
- "eslint-plugin-markdown": "^3.0.1",
44
- "eslint-plugin-node": "^11.1.0",
45
- "eslint-plugin-promise": "^6.6.0",
46
- "eslint-plugin-standard": "^4.1.0",
40
+ "eslint": "^9.39.5",
47
41
  "mocha": "^11.7.6",
42
+ "neostandard": "^0.13.0",
48
43
  "nyc": "^17.1.0",
49
- "supertest": "^7.2.2"
44
+ "supertest": "7.3.0"
50
45
  },
51
46
  "files": [
52
47
  "lib/",
@@ -1,10 +1,15 @@
1
+ 1.20.9
2
+ ======
3
+
4
+ * Security fix for [GHSA-27r3-jxmh-p828](https://github.com/expressjs/body-parser/security/advisories/GHSA-27r3-jxmh-p828) (CVE-2026-111181)
5
+
1
6
  1.20.8
2
- ===================
7
+ ======
3
8
 
4
9
  * Same code base as 1.20.7. This was created to test the new release process.
5
10
 
6
11
  1.20.7
7
- ===================
12
+ ======
8
13
 
9
14
  * deps: qs@~6.16.0
10
15
 
@@ -98,7 +98,7 @@ argument. You can find more information on this argument
98
98
 
99
99
  ##### strict
100
100
 
101
- When set to `true`, will only accept arrays and objects; when `false` will
101
+ When set to `true`, it will only accept arrays and objects; when `false`, it will
102
102
  accept anything `JSON.parse` accepts. Defaults to `true`.
103
103
 
104
104
  ##### type
@@ -313,7 +313,7 @@ the entity value that failed parsing.
313
313
  ### entity verify failed
314
314
 
315
315
  This error will occur when the request contained an entity that could not be
316
- failed verification by the defined `verify` option. The `status` property is
316
+ verified by the defined `verify` option. The `status` property is
317
317
  set to `403`, the `type` property is set to `'entity.verify.failed'`, and the
318
318
  `body` property is set to the entity value that failed verification.
319
319
 
@@ -380,7 +380,7 @@ property is set to the encoding that is unsupported.
380
380
 
381
381
  ### The input exceeded the depth
382
382
 
383
- This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. When the `depth` option is set to `32` (default value), the error will not be thrown.
383
+ This error occurs when using `bodyParser.urlencoded` with the `extended` property set to `true` and the input exceeds the configured `depth` option. The `status` property is set to `400`. It is recommended to review the `depth` option and evaluate if it requires a higher value. With the default `depth` of `32`, an input with 33 or more levels of nesting gets this error.
384
384
 
385
385
  ## Examples
386
386
 
@@ -51,6 +51,15 @@ function read (req, res, next, parse, debug, options) {
51
51
  : null
52
52
  var verify = opts.verify
53
53
 
54
+ // assert charset is supported before creating the decompression stream, so a
55
+ // 415 cannot leave a decoder piped with no error listener
56
+ if (verify && encoding !== null && !iconv.encodingExists(encoding)) {
57
+ return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
58
+ charset: encoding.toLowerCase(),
59
+ type: 'charset.unsupported'
60
+ }))
61
+ }
62
+
54
63
  try {
55
64
  // get the content stream
56
65
  stream = contentstream(req, debug, opts.inflate)
@@ -66,14 +75,6 @@ function read (req, res, next, parse, debug, options) {
66
75
  ? null
67
76
  : encoding
68
77
 
69
- // assert charset is supported
70
- if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
71
- return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
72
- charset: encoding.toLowerCase(),
73
- type: 'charset.unsupported'
74
- }))
75
- }
76
-
77
78
  // read body
78
79
  debug('read body')
79
80
  getBody(stream, opts, function (error, body) {
@@ -104,6 +105,23 @@ function read (req, res, next, parse, debug, options) {
104
105
  return
105
106
  }
106
107
 
108
+ // detach and destroy the decompression stream to avoid
109
+ // decompression errors on the finished stream
110
+ if (stream !== req) {
111
+ // only detach the decompression stream, keep other consumers piped
112
+ if (typeof req.unpipe === 'function') {
113
+ req.unpipe(stream)
114
+ } else {
115
+ // Node.js 0.8 streams have no unpipe(dest)
116
+ unpipe(req)
117
+ }
118
+ stream.on('error', function () {})
119
+ destroy(stream, true)
120
+
121
+ // consume any remaining bytes so the HTTP request can finish
122
+ req.resume()
123
+ }
124
+
107
125
  // verify
108
126
  if (verify) {
109
127
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "body-parser",
3
3
  "description": "Node.js body parsing middleware",
4
- "version": "1.20.8",
4
+ "version": "1.20.9",
5
5
  "contributors": [
6
6
  "Douglas Christopher Wilson <doug@somethingdoug.com>",
7
7
  "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
@@ -65,10 +65,11 @@ function onFinished (msg, listener) {
65
65
 
66
66
  function isFinished (msg) {
67
67
  var socket = msg.socket
68
+ var finished = writableEnded(msg)
68
69
 
69
- if (typeof msg.finished === 'boolean') {
70
+ if (typeof finished === 'boolean') {
70
71
  // OutgoingMessage
71
- return Boolean(msg.finished || (socket && !socket.writable))
72
+ return Boolean(finished || (socket && !socket.writable))
72
73
  }
73
74
 
74
75
  if (typeof msg.complete === 'boolean') {
@@ -232,3 +233,14 @@ function wrap (fn) {
232
233
  // return bound function
233
234
  return res.runInAsyncScope.bind(res, fn, null)
234
235
  }
236
+
237
+ /**
238
+ * Determines if a writable stream has finished.
239
+ *
240
+ * @param {Object} res
241
+ * @returns {boolean}
242
+ * @private
243
+ */
244
+ function writableEnded (res) {
245
+ return typeof res.writableEnded === 'boolean' ? res.writableEnded : res.finished
246
+ }
@@ -1,38 +1,42 @@
1
1
  {
2
2
  "name": "on-finished",
3
3
  "description": "Execute a callback when a request closes, finishes, or errors",
4
- "version": "2.4.1",
4
+ "version": "2.4.2",
5
5
  "contributors": [
6
6
  "Douglas Christopher Wilson <doug@somethingdoug.com>",
7
7
  "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
8
8
  ],
9
9
  "license": "MIT",
10
10
  "repository": "jshttp/on-finished",
11
+ "funding": {
12
+ "type": "opencollective",
13
+ "url": "https://opencollective.com/express"
14
+ },
15
+ "type": "commonjs",
11
16
  "dependencies": {
12
17
  "ee-first": "1.1.1"
13
18
  },
14
19
  "devDependencies": {
15
- "eslint": "7.32.0",
20
+ "eslint": "8.17.0",
16
21
  "eslint-config-standard": "14.1.1",
17
- "eslint-plugin-import": "2.25.4",
22
+ "eslint-plugin-import": "2.26.0",
18
23
  "eslint-plugin-markdown": "2.2.1",
19
24
  "eslint-plugin-node": "11.1.0",
20
- "eslint-plugin-promise": "5.2.0",
25
+ "eslint-plugin-promise": "6.0.0",
21
26
  "eslint-plugin-standard": "4.1.0",
22
- "mocha": "9.2.1",
27
+ "mocha": "12.0.3",
23
28
  "nyc": "15.1.0"
24
29
  },
25
30
  "engines": {
26
31
  "node": ">= 0.8"
27
32
  },
28
33
  "files": [
29
- "HISTORY.md",
30
34
  "LICENSE",
31
35
  "index.js"
32
36
  ],
33
37
  "scripts": {
34
38
  "lint": "eslint .",
35
- "test": "mocha --reporter spec --bail --check-leaks test/",
39
+ "test": "mocha --reporter spec --check-leaks test/",
36
40
  "test-ci": "nyc --reporter=lcovonly --reporter=text npm test",
37
41
  "test-cov": "nyc --reporter=html --reporter=text npm test"
38
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suveren/gateway",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
5
5
  "type": "module",
6
6
  "main": "server.js",