amqplib 2.3.0 → 2.3.1
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.
- package/CHANGELOG.md +3 -0
- package/lib/codec.js +24 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Change log for amqplib
|
|
2
2
|
|
|
3
|
+
## v2.3.1
|
|
4
|
+
- Throw a clear `RangeError` from `publish` when an encoded headers or `arguments` table exceeds the 64 KiB encoding buffer. Previously the outcome depended on where the boundary fell: a string or Buffer value crossing the end of the buffer was silently truncated, producing a frame whose table length prefix did not match its contents, which RabbitMQ rejected by closing the connection with a 541 `INTERNAL_ERROR` after `publish` had returned; at other positions a bare `offset is out of range` error escaped from `publish` instead (fixes #867)
|
|
5
|
+
|
|
3
6
|
## v2.3.0
|
|
4
7
|
- Reject a `connection.tune` whose `frameMax` is below the protocol minimum of 4096 (the spec allows 0 or at least frame-min-size). Previously such a value was accepted, and the first publish whose encoded frames total 2048 bytes or more entered a loop in `sendContent` that never advanced, exhausting memory. `sendContent` now also throws if `frameMax` leaves no room for a body frame (GHSA-37f9-7m79-6866, reported by Rizwan Bedekar ([@r-bedekar](https://github.com/r-bedekar)); fix based in part on their patch and tests)
|
|
5
8
|
- Add a `maxFrameSize` connection option, also settable as a URL query parameter, bounding the payload size of frames accepted from the server. The default is 64 MiB, or the negotiated `frameMax` if that is larger, so raising `frameMax` needs no second option; `0` disables the limit. Incoming frame sizes were previously unchecked, so a frame header declaring up to 4 GiB made the client buffer, and repeatedly re-copy, that much data before looking at it. An oversized frame is now refused from its header alone and closes the connection with `Frame size N exceeds maxFrameSize M`. The limit is deliberately separate from the negotiated `frameMax`, since some brokers send larger frames without splitting them (see #784). **Upgrade note:** a broker that delivers an unsplit frame larger than the limit will now have that connection closed; pass `maxFrameSize: 0`, or a higher value, to restore the previous behaviour (GHSA-37f9-7m79-6866)
|
package/lib/codec.js
CHANGED
|
@@ -62,12 +62,20 @@ function isFloatingPoint(n) {
|
|
|
62
62
|
return n >= 0x8000000000000000 || (Math.abs(n) < 0x4000000000000 && Math.floor(n) !== n);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function checkSpace(buffer, offset, size) {
|
|
66
|
+
if (offset + size > buffer.length) {
|
|
67
|
+
throw new RangeError(`Encoded field table (headers or arguments) exceeds the ${buffer.length} byte encoding buffer`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
function encodeTable(buffer, val, offset) {
|
|
66
72
|
const start = offset;
|
|
73
|
+
checkSpace(buffer, offset, 4);
|
|
67
74
|
offset += 4; // leave room for the table length
|
|
68
75
|
for (const key in val) {
|
|
69
76
|
if (val[key] !== undefined) {
|
|
70
77
|
const len = Buffer.byteLength(key);
|
|
78
|
+
checkSpace(buffer, offset, 1 + len);
|
|
71
79
|
buffer.writeUInt8(len, offset);
|
|
72
80
|
offset++;
|
|
73
81
|
buffer.write(key, offset, 'utf8');
|
|
@@ -82,6 +90,7 @@ function encodeTable(buffer, val, offset) {
|
|
|
82
90
|
|
|
83
91
|
function encodeArray(buffer, val, offset) {
|
|
84
92
|
const start = offset;
|
|
93
|
+
checkSpace(buffer, offset, 4);
|
|
85
94
|
offset += 4;
|
|
86
95
|
for (let i = 0, num = val.length; i < num; i++) {
|
|
87
96
|
offset += encodeFieldValue(buffer, val[i], offset);
|
|
@@ -133,6 +142,7 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
function tag(t) {
|
|
145
|
+
checkSpace(buffer, offset, 1);
|
|
136
146
|
buffer.write(t, offset);
|
|
137
147
|
offset++;
|
|
138
148
|
}
|
|
@@ -142,6 +152,7 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
142
152
|
// no shortstr in field tables
|
|
143
153
|
const len = Buffer.byteLength(val, 'utf8');
|
|
144
154
|
tag('S');
|
|
155
|
+
checkSpace(buffer, offset, 4 + len);
|
|
145
156
|
buffer.writeUInt32BE(len, offset);
|
|
146
157
|
offset += 4;
|
|
147
158
|
buffer.write(val, offset, 'utf8');
|
|
@@ -156,6 +167,7 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
156
167
|
offset += encodeArray(buffer, val, offset);
|
|
157
168
|
} else if (Buffer.isBuffer(val)) {
|
|
158
169
|
tag('x');
|
|
170
|
+
checkSpace(buffer, offset, 4 + val.length);
|
|
159
171
|
buffer.writeUInt32BE(val.length, offset);
|
|
160
172
|
offset += 4;
|
|
161
173
|
val.copy(buffer, offset);
|
|
@@ -167,6 +179,7 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
167
179
|
break;
|
|
168
180
|
case 'boolean':
|
|
169
181
|
tag('t');
|
|
182
|
+
checkSpace(buffer, offset, 1);
|
|
170
183
|
buffer.writeUInt8(val ? 1 : 0, offset);
|
|
171
184
|
offset++;
|
|
172
185
|
break;
|
|
@@ -175,48 +188,56 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
175
188
|
case 'double':
|
|
176
189
|
case 'float64':
|
|
177
190
|
tag('d');
|
|
191
|
+
checkSpace(buffer, offset, 8);
|
|
178
192
|
buffer.writeDoubleBE(val, offset);
|
|
179
193
|
offset += 8;
|
|
180
194
|
break;
|
|
181
195
|
case 'byte':
|
|
182
196
|
case 'int8':
|
|
183
197
|
tag('b');
|
|
198
|
+
checkSpace(buffer, offset, 1);
|
|
184
199
|
buffer.writeInt8(val, offset);
|
|
185
200
|
offset++;
|
|
186
201
|
break;
|
|
187
202
|
case 'unsignedbyte':
|
|
188
203
|
case 'uint8':
|
|
189
204
|
tag('B');
|
|
205
|
+
checkSpace(buffer, offset, 1);
|
|
190
206
|
buffer.writeUInt8(val, offset);
|
|
191
207
|
offset++;
|
|
192
208
|
break;
|
|
193
209
|
case 'short':
|
|
194
210
|
case 'int16':
|
|
195
211
|
tag('s');
|
|
212
|
+
checkSpace(buffer, offset, 2);
|
|
196
213
|
buffer.writeInt16BE(val, offset);
|
|
197
214
|
offset += 2;
|
|
198
215
|
break;
|
|
199
216
|
case 'unsignedshort':
|
|
200
217
|
case 'uint16':
|
|
201
218
|
tag('u');
|
|
219
|
+
checkSpace(buffer, offset, 2);
|
|
202
220
|
buffer.writeUInt16BE(val, offset);
|
|
203
221
|
offset += 2;
|
|
204
222
|
break;
|
|
205
223
|
case 'int':
|
|
206
224
|
case 'int32':
|
|
207
225
|
tag('I');
|
|
226
|
+
checkSpace(buffer, offset, 4);
|
|
208
227
|
buffer.writeInt32BE(val, offset);
|
|
209
228
|
offset += 4;
|
|
210
229
|
break;
|
|
211
230
|
case 'unsignedint':
|
|
212
231
|
case 'uint32':
|
|
213
232
|
tag('i');
|
|
233
|
+
checkSpace(buffer, offset, 4);
|
|
214
234
|
buffer.writeUInt32BE(val, offset);
|
|
215
235
|
offset += 4;
|
|
216
236
|
break;
|
|
217
237
|
case 'long':
|
|
218
238
|
case 'int64':
|
|
219
239
|
tag('l');
|
|
240
|
+
checkSpace(buffer, offset, 8);
|
|
220
241
|
buffer.writeBigInt64BE(BigInt(val), offset);
|
|
221
242
|
offset += 8;
|
|
222
243
|
break;
|
|
@@ -225,17 +246,20 @@ function encodeFieldValue(buffer, value, offset) {
|
|
|
225
246
|
// `{'!': type, value: val}
|
|
226
247
|
case 'timestamp':
|
|
227
248
|
tag('T');
|
|
249
|
+
checkSpace(buffer, offset, 8);
|
|
228
250
|
buffer.writeBigUInt64BE(BigInt(val), offset);
|
|
229
251
|
offset += 8;
|
|
230
252
|
break;
|
|
231
253
|
case 'float':
|
|
232
254
|
tag('f');
|
|
255
|
+
checkSpace(buffer, offset, 4);
|
|
233
256
|
buffer.writeFloatBE(val, offset);
|
|
234
257
|
offset += 4;
|
|
235
258
|
break;
|
|
236
259
|
case 'decimal':
|
|
237
260
|
tag('D');
|
|
238
261
|
if (Object.hasOwn(val, 'places') && Object.hasOwn(val, 'digits') && val.places >= 0 && val.places < 256) {
|
|
262
|
+
checkSpace(buffer, offset, 5);
|
|
239
263
|
buffer[offset] = val.places;
|
|
240
264
|
offset++;
|
|
241
265
|
buffer.writeUInt32BE(val.digits, offset);
|