bencode2 0.3.28__py3-none-any.whl → 0.3.30__py3-none-any.whl

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.
bencode2/__decoder.py CHANGED
@@ -13,6 +13,8 @@ char_9: Final = ord("9")
13
13
  char_dash: Final = ord("-")
14
14
  char_colon: Final = ord(":")
15
15
 
16
+ _MAX_DECODE_DEPTH: Final = 4096
17
+
16
18
 
17
19
  class BencodeDecodeError(ValueError):
18
20
  """Bencode decode error."""
@@ -28,7 +30,7 @@ class Decoder:
28
30
  index: int
29
31
  size: int
30
32
 
31
- __slots__ = ("value", "index", "size")
33
+ __slots__ = ("value", "index", "size", "_depth")
32
34
 
33
35
  def __init__(self, value: memoryview) -> None:
34
36
  self.size = len(value)
@@ -37,6 +39,7 @@ class Decoder:
37
39
 
38
40
  self.value = value
39
41
  self.index = 0
42
+ self._depth = 0
40
43
 
41
44
  def decode(self) -> object:
42
45
  data = self.__decode()
@@ -47,6 +50,10 @@ class Decoder:
47
50
  return data
48
51
 
49
52
  def __decode(self) -> object:
53
+ self._depth += 1
54
+ if self._depth > _MAX_DECODE_DEPTH:
55
+ raise BencodeDecodeError("exceeded maximum decode depth")
56
+
50
57
  if char_0 <= self.value[self.index] <= char_9:
51
58
  return self.__decode_bytes()
52
59
  if self.value[self.index] == char_i:
@@ -81,6 +88,10 @@ class Decoder:
81
88
  if self.value[self.index] == char_dash:
82
89
  n = -1
83
90
  offset = 1
91
+ if self.index + offset >= index_end:
92
+ raise BencodeDecodeError(
93
+ f"invalid int, '-' with no digits at {self.index}"
94
+ )
84
95
 
85
96
  total: int = 0
86
97
  for c in self.value[self.index + offset : index_end]:
bencode2/decode.cpp CHANGED
@@ -13,10 +13,18 @@
13
13
 
14
14
  namespace nb = nanobind;
15
15
 
16
- nb::object decodeAny(const char *buf, Py_ssize_t &index, Py_ssize_t size);
16
+ #define BENCODE_MAX_DECODE_DEPTH 4096
17
+
18
+ nb::object decodeAny(const char *buf, Py_ssize_t &index, Py_ssize_t size, uint_fast32_t depth);
17
19
 
18
20
  #define decoderError(f, ...) throw DecodeError(fmt::format(f, ##__VA_ARGS__))
19
21
 
22
+ static inline void checkDepth(uint_fast32_t depth) {
23
+ if (depth > BENCODE_MAX_DECODE_DEPTH) {
24
+ throw DecodeError("exceeded maximum decode depth");
25
+ }
26
+ }
27
+
20
28
  nb::object decodeInt(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
21
29
  Py_ssize_t index_e = 0;
22
30
  for (Py_ssize_t i = index + 1; i < size; i++) {
@@ -27,7 +35,7 @@ nb::object decodeInt(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
27
35
  }
28
36
 
29
37
  if (index_e == 0) {
30
- throw DecodeError(fmt::format("invalid int, missing 'e': %zd", index));
38
+ decoderError("invalid int, missing 'e': {}", index);
31
39
  }
32
40
 
33
41
  // malformed 'ie'
@@ -44,16 +52,17 @@ nb::object decodeInt(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
44
52
  Py_ssize_t num_start = index;
45
53
 
46
54
  if (buf[index] == '-') {
47
- if (buf[index + 1] == '0') {
48
- decoderError("invalid int, '-0' found at %zd", index);
49
- }
50
-
51
55
  num_start = 1 + index;
56
+ if (num_start >= index_e) {
57
+ decoderError("invalid int, '-' with no digits at {}", index);
58
+ }
59
+ if (buf[num_start] == '0') {
60
+ decoderError("invalid int, '-0' found at {}", index);
61
+ }
52
62
  sign = -1;
53
63
  } else if (buf[index] == '0') {
54
64
  if (index + 1 != index_e) {
55
- decoderError("invalid int, non-zero int should not start with '0'. found at %zd",
56
- index);
65
+ decoderError("invalid int, non-zero int should not start with '0'. found at {}", index);
57
66
  }
58
67
  }
59
68
 
@@ -121,7 +130,6 @@ __OverFlow:;
121
130
  }
122
131
 
123
132
  // there is no bytes/Str in bencode, they only have 1 type for both of them.
124
- // TODO: check byte length overflow ssize_t
125
133
  static std::string_view decodeAsView(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
126
134
  Py_ssize_t index_sep = 0;
127
135
  for (Py_ssize_t i = index; i < size; i++) {
@@ -132,7 +140,7 @@ static std::string_view decodeAsView(const char *buf, Py_ssize_t &index, Py_ssiz
132
140
  }
133
141
 
134
142
  if (index_sep == 0) {
135
- decoderError("invalid string, missing length: index %zd", index);
143
+ decoderError("invalid string, missing length: index {}", index);
136
144
  }
137
145
 
138
146
  if (buf[index] == '0' && index + 1 != index_sep) {
@@ -142,7 +150,10 @@ static std::string_view decodeAsView(const char *buf, Py_ssize_t &index, Py_ssiz
142
150
  Py_ssize_t len = 0;
143
151
  for (Py_ssize_t i = index; i < index_sep; i++) {
144
152
  if (buf[i] < '0' || buf[i] > '9') {
145
- decoderError("invalid bytes length, found '%c' at %zd", buf[i], i);
153
+ decoderError("invalid bytes length, found '{:c}' at {}", buf[i], i);
154
+ }
155
+ if (len > (PY_SSIZE_T_MAX - 9) / 10) {
156
+ decoderError("bytes length overflow, index {}", index);
146
157
  }
147
158
  len = len * 10 + (buf[i] - '0');
148
159
  }
@@ -162,7 +173,7 @@ static nb::bytes decodeBytes(const char *buf, Py_ssize_t &index, Py_ssize_t size
162
173
  return nb::bytes(s.data(), s.length());
163
174
  }
164
175
 
165
- nb::object decodeList(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
176
+ nb::object decodeList(const char *buf, Py_ssize_t &index, Py_ssize_t size, uint_fast32_t depth) {
166
177
  index = index + 1;
167
178
 
168
179
  nb::list l = nb::list();
@@ -176,7 +187,7 @@ nb::object decodeList(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
176
187
  break;
177
188
  }
178
189
 
179
- nb::object obj = decodeAny(buf, index, size);
190
+ nb::object obj = decodeAny(buf, index, size, depth);
180
191
 
181
192
  l.append(obj);
182
193
  }
@@ -186,7 +197,7 @@ nb::object decodeList(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
186
197
  return l;
187
198
  }
188
199
 
189
- nb::object decodeDict(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
200
+ nb::object decodeDict(const char *buf, Py_ssize_t &index, Py_ssize_t size, uint_fast32_t depth) {
190
201
  index = index + 1;
191
202
  std::optional<std::string_view> lastKey = std::nullopt;
192
203
 
@@ -207,7 +218,7 @@ nb::object decodeDict(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
207
218
  }
208
219
 
209
220
  auto key = decodeAsView(buf, index, size);
210
- auto obj = decodeAny(buf, index, size);
221
+ auto obj = decodeAny(buf, index, size, depth);
211
222
 
212
223
  // skip first key
213
224
  if (lastKey.has_value()) {
@@ -228,7 +239,10 @@ nb::object decodeDict(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
228
239
  return d;
229
240
  }
230
241
 
231
- nb::object decodeAny(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
242
+ nb::object decodeAny(const char *buf, Py_ssize_t &index, Py_ssize_t size, uint_fast32_t depth) {
243
+ depth++;
244
+ checkDepth(depth);
245
+
232
246
  // int
233
247
  if (buf[index] == 'i') {
234
248
  return decodeInt(buf, index, size);
@@ -241,12 +255,12 @@ nb::object decodeAny(const char *buf, Py_ssize_t &index, Py_ssize_t size) {
241
255
 
242
256
  // list
243
257
  if (buf[index] == 'l') {
244
- return decodeList(buf, index, size);
258
+ return decodeList(buf, index, size, depth);
245
259
  }
246
260
 
247
261
  // dict
248
262
  if (buf[index] == 'd') {
249
- return decodeDict(buf, index, size);
263
+ return decodeDict(buf, index, size, depth);
250
264
  }
251
265
 
252
266
  decoderError("invalid bencode prefix '{:c}', index {}", buf[index], index);
@@ -275,7 +289,7 @@ nb::object bdecode(nb::object b) {
275
289
 
276
290
  nb::object o;
277
291
  try {
278
- o = decodeAny(buf, index, size);
292
+ o = decodeAny(buf, index, size, 0);
279
293
  } catch (std::exception &e) {
280
294
  PyBuffer_Release(&view);
281
295
  throw e;
bencode2/encode.cpp CHANGED
@@ -22,15 +22,20 @@ static bool cmp(std::pair<std::string_view, nb::handle> &a,
22
22
  }
23
23
 
24
24
  static std::string_view py_string_view(nb::handle obj) {
25
+ #ifndef Py_LIMITED_API
25
26
  // fast path for pure ascii string
26
27
  if (PyUnicode_IS_COMPACT_ASCII(obj.ptr())) {
27
28
  const char *s = (char *)PyUnicode_DATA(obj.ptr());
28
29
  Py_ssize_t size = ((PyASCIIObject *)(obj.ptr()))->length;
29
30
  return std::string_view(s, size);
30
31
  }
32
+ #endif
31
33
 
32
34
  Py_ssize_t size = 0;
33
35
  const char *s = PyUnicode_AsUTF8AndSize(obj.ptr(), &size);
36
+ if (s == nullptr) {
37
+ throw nb::python_error();
38
+ }
34
39
  return std::string_view(s, size);
35
40
  }
36
41
 
@@ -242,6 +247,7 @@ void encodeComposeObject(EncodeContext *ctx, nb::handle obj, Encode encode) {
242
247
  void encodeStr(EncodeContext *ctx, const nb::handle obj) {
243
248
  debug_print("encode str");
244
249
 
250
+ #ifndef Py_LIMITED_API
245
251
  // fast path for pure ascii string
246
252
  if (PyUnicode_IS_COMPACT_ASCII(obj.ptr())) {
247
253
  const char *s = (char *)PyUnicode_DATA(obj.ptr());
@@ -254,9 +260,13 @@ void encodeStr(EncodeContext *ctx, const nb::handle obj) {
254
260
  ctx->write(s, size);
255
261
  return;
256
262
  }
263
+ #endif
257
264
 
258
265
  Py_ssize_t size = 0;
259
266
  const char *s = PyUnicode_AsUTF8AndSize(obj.ptr(), &size);
267
+ if (s == nullptr) {
268
+ throw nb::python_error();
269
+ }
260
270
 
261
271
  debug_print("write length");
262
272
  ctx->writeSize_t(size);
@@ -331,6 +341,7 @@ void encodeAny(EncodeContext *ctx, const nb::handle obj) {
331
341
  return;
332
342
  }
333
343
 
344
+ #ifndef Py_LIMITED_API
334
345
  // fast path for memoryview
335
346
  if (PyMemoryView_Check(obj.ptr())) {
336
347
  Py_buffer *buf = PyMemoryView_GET_BUFFER(obj.ptr());
@@ -340,6 +351,7 @@ void encodeAny(EncodeContext *ctx, const nb::handle obj) {
340
351
 
341
352
  return;
342
353
  }
354
+ #endif
343
355
 
344
356
  if (PyObject_CheckBuffer(obj.ptr())) {
345
357
  Py_buffer buf;
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bencode2
3
- Version: 0.3.28
3
+ Version: 0.3.30
4
4
  Summary: A fast and correct bencode serialize/deserialize library
5
5
  Keywords: bencode,bittorrent,bit-torrent,serialize,deserialize,p2p
6
6
  Author-email: trim21 <trim21me@gmail.com>
7
- Requires-Python: >=3.10,<4.0
7
+ Requires-Python: >=3.10
8
8
  Description-Content-Type: text/markdown
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: License :: OSI Approved :: MIT License
@@ -21,7 +21,6 @@ Project-URL: Repository, https://github.com/trim21/bencode-py
21
21
 
22
22
  [![PyPI](https://img.shields.io/pypi/v/bencode2)](https://pypi.org/project/bencode2/)
23
23
  [![tests](https://github.com/trim21/bencode-py/actions/workflows/tests.yaml/badge.svg)](https://github.com/trim21/bencode-py/actions/workflows/tests.yaml)
24
- [![CircleCI](https://dl.circleci.com/status-badge/img/gh/trim21/bencode-py/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/trim21/bencode-py/tree/master)
25
24
  [![PyPI - Python Version](https://img.shields.io/badge/python-%3E%3D3.8%2C%3C4.0-blue)](https://pypi.org/project/bencode2/)
26
25
  [![Codecov branch](https://img.shields.io/codecov/c/github/Trim21/bencode-py/master)](https://codecov.io/gh/Trim21/bencode-py/branch/master)
27
26
 
@@ -111,7 +110,7 @@ bencode2 will decode bencode string to python `bytes`.
111
110
  | `bytes`, `bytearray`,`memoryview` | string |
112
111
  | `list`, `tuple`, `NamedTuple` | array |
113
112
  | `dict`, `OrderedDict` | dictionary |
114
- | `types.MaapingProxy` | dictionary |
113
+ | `types.MappingProxy` | dictionary |
115
114
  | dataclasses | dictionary |
116
115
 
117
116
  ## free threading
@@ -1,16 +1,16 @@
1
1
  bencode2/__bencode.pyi,sha256=R11h4PI4pSxBQgxslVunDxzx5sjrTcHq-_RpclDTlpY,137
2
- bencode2/__decoder.py,sha256=UVW_01zUSKATUHVddB-bX-5x8tulIeheweWE7_XbS3o,6192
2
+ bencode2/__decoder.py,sha256=WWHd_HCdxxoJwYs7m22KOj5qrEBuCzEZEBWiYVK-tdY,6579
3
3
  bencode2/__encoder.py,sha256=ky4hMgkpZNe7PtoqYaTroyTsoH9ZDKLqxYICQpL-kbk,4196
4
4
  bencode2/__init__.py,sha256=H7VTW80hlELobN5j9dhyUi2l0pUuOon8ibTW2kVG78Y,383
5
5
  bencode2/__init__.pyi,sha256=rFe9L4Kof8XKp1CVLJj6zZeNRifRVLwVN7Ag4kRQndI,237
6
6
  bencode2/bencode.cpp,sha256=sn12w9hvaBdAdEMAJZjQdPM3pjPyfHVlhFIc-wlNNJ4,710
7
7
  bencode2/common.hpp,sha256=liOfMvppYm1Es8ki7G2UjVNkOP5pVuvZvNCyQ_cRAqA,1791
8
- bencode2/decode.cpp,sha256=MvhUdAJ1A0NYWW7O8_L9C0_ntvJ6gSIjm2fGiC1nKgQ,7266
9
- bencode2/encode.cpp,sha256=oyzl4VZPH7LX5eI5tC9FEUoeb1MCfu_HanKlQAP_F0c,11090
8
+ bencode2/decode.cpp,sha256=smagKRJzWth8_L56OxPDyI8lBnlRDq6_XG2eCN6u8jk,7777
9
+ bencode2/encode.cpp,sha256=IGNlXzr-gs4Mk0UMKetbjn8UISzdzOpwy_paM1u1Tc0,11308
10
10
  bencode2/encode_ctx.hpp,sha256=_CO3bHSsx6DYJBSnUeoCDqtZFFR3jOyIQJdv5YG0J44,1254
11
11
  bencode2/overflow.hpp,sha256=n3W-45ErhBf5ZGKKn03ll9j6bB5jY0R4eybbzTPKMnc,701
12
12
  bencode2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- bencode2-0.3.28.dist-info/licenses/LICENSE,sha256=WraLL2GWym7_6EnkH1Zi8kU_-2iPhXGv7yegTIEX3E8,1085
14
- bencode2-0.3.28.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
15
- bencode2-0.3.28.dist-info/METADATA,sha256=pnooWGThJ_Fl_P2Yp4MQhiNnlOZKE4hqvZiAxFd8iJw,5294
16
- bencode2-0.3.28.dist-info/RECORD,,
13
+ bencode2-0.3.30.dist-info/licenses/LICENSE,sha256=WraLL2GWym7_6EnkH1Zi8kU_-2iPhXGv7yegTIEX3E8,1085
14
+ bencode2-0.3.30.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
15
+ bencode2-0.3.30.dist-info/METADATA,sha256=Q27Lrz4VwtE2-crVnOjalvnay2NVeKfYLJs18ZaCJEE,5106
16
+ bencode2-0.3.30.dist-info/RECORD,,