ox 2.14.26 → 2.14.28

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '028d3be16e23f546d40a021dc76fe2a9a506f9f140f673e9e2aef60ab481fb5b'
4
- data.tar.gz: 98efdca44712e1095cfaea485f66b46918649261fc0855794a6bf943d1a86e37
3
+ metadata.gz: 55a1d3d6cf3b51d434dc27e11ac62c93b2eab2b08426daf03c13f295b43cdd5e
4
+ data.tar.gz: 17b62fcfb67d670bf3693b6863d3906cd52a5f61b0344d5aa5fd1ed8dda26584
5
5
  SHA512:
6
- metadata.gz: c1158647c428da2193d3de8c8a564077dc3e7854890dd9b31d79747e65dc14595a381ab079aaa3b9471d2b0de30d96c4b8698d1b82e2e53d34bfa6ff116a35cd
7
- data.tar.gz: 43fcda76ed7d4d5febf5dc67e228278af4b16fcff4ff221ef4a3b39dbf0451d1da3f2ff25524079026ed482d14f0ae3d74af2019cb88f5296e6ebc340e4091b7
6
+ metadata.gz: c063a565a7dc1f1edd042377fbf195d53533a8a3d1585f3a29455beefb9cc7d4c6e9e4911c4632ee48c45f67b4a16b34c8cde7fbcb5aa5c63db74240e7d72230
7
+ data.tar.gz: 4338c45e7ea8c81948d0dc44af7244593fd0594b9c6c38ffddd1bd247baaa1369e856b8604e09419f8c035cc36478a633d50c005c171ecc4d054c2def302d53a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All changes to the Ox gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.14.28] - 2026-06-28
6
+
7
+ ### Fixed
8
+
9
+ - Parsing an excessively long DOCTYPE now raises and error instead of
10
+ waiting for a stack too deep system error.
11
+
12
+ ## [2.14.27] - 2026-06-18
13
+
14
+ ### Fixed
15
+
16
+ - Fixed stack overflow issue in the parser that occurred when reading
17
+ special character sequences.
18
+
19
+ - Set a limit of 1000 as the maximum nesting depth of elements to
20
+ avoid stack exhaustion.
21
+
22
+ - Added a note on the symbolize_keys option indicating it should not
23
+ be used on unregulated input where the symbol table could grow
24
+ indefinitely.
25
+
5
26
  ## [2.14.26] - 2026-05-09
6
27
 
7
28
  ### Fixed
data/ext/ox/base64.c CHANGED
@@ -11,7 +11,7 @@
11
11
  static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12
12
 
13
13
  /* invalid or terminating characters are set to 'X' or \x58 */
14
- static uchar s_digits[256] = "\
14
+ static uchar s_digits[257] = "\
15
15
  \x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\
16
16
  \x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\
17
17
  \x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x3E\x58\x58\x58\x3F\
data/ext/ox/ox.c CHANGED
@@ -274,7 +274,9 @@ static VALUE hints_to_overlay(Hints hints) {
274
274
  * - _:xsd_date_ [true|false|nil] use XSD date format instead of decimal format
275
275
  * - _:mode_ [:object|:generic|:limited|:hash|:hash_no_attrs|nil] load method to use for XML
276
276
  * - _:effort_ [:strict|:tolerant|:auto_define] set the tolerance level for loading
277
- * - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings
277
+ * - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings.
278
+ * Note that symbolized keys are more efficient but for uncontrolled input it can lead to unlimited
279
+ * growth of the symbol (intern) table.
278
280
  * - _:element_key_mod_ [Proc|nil] converts element keys on parse if not nil
279
281
  * - _:attr_key_mod_ [Proc|nil] converts attribute keys on parse if not nil
280
282
  * - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
data/ext/ox/parse.c CHANGED
@@ -18,11 +18,16 @@
18
18
  #include "ruby.h"
19
19
  #include "special.h"
20
20
 
21
+ #define MAX_ELEMENT_DEPTH 1000
22
+ // Anything even close to the max prolog length is most likely someone trying
23
+ // to break something.
24
+ #define MAX_PROLOG 32767
25
+
21
26
  static void mark_pi_cb(void *ptr);
22
27
  static void read_instruction(PInfo pi);
23
28
  static void read_doctype(PInfo pi);
24
29
  static void read_comment(PInfo pi);
25
- static char *read_element(PInfo pi);
30
+ static char *read_element(PInfo pi, int depth);
26
31
  static void read_text(PInfo pi);
27
32
  /*static void read_reduced_text(PInfo pi); */
28
33
  static void read_cdata(PInfo pi);
@@ -216,7 +221,7 @@ ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options
216
221
  helper_stack_cleanup(&pi.helpers);
217
222
  return Qnil;
218
223
  default:
219
- read_element(&pi);
224
+ read_element(&pi, 0);
220
225
  body_read = 1;
221
226
  break;
222
227
  }
@@ -380,6 +385,11 @@ static void read_delimited(PInfo pi, char end) {
380
385
  if (end == c) {
381
386
  return;
382
387
  }
388
+ if (MAX_PROLOG < (pi->s - pi->str)) {
389
+ pi->s--;
390
+ set_error(&pi->err, "prolog (doctype) too long", pi->str, pi->s);
391
+ return;
392
+ }
383
393
  switch (c) {
384
394
  case '\0':
385
395
  pi->s--;
@@ -452,7 +462,7 @@ static void read_comment(PInfo pi) {
452
462
 
453
463
  // Entered after the '<' and the first character after that. Returns stat
454
464
  // code.
455
- static char *read_element(PInfo pi) {
465
+ static char *read_element(PInfo pi, int depth) {
456
466
  struct _attrStack attrs;
457
467
  const char *attr_name;
458
468
  const char *attr_value;
@@ -464,6 +474,10 @@ static char *read_element(PInfo pi) {
464
474
  int hasChildren = 0;
465
475
  int done = 0;
466
476
 
477
+ if (MAX_ELEMENT_DEPTH < depth) {
478
+ set_error(&pi->err, "element nested too deeply, limit is 1000", pi->str, pi->s);
479
+ return 0;
480
+ }
467
481
  attr_stack_init(&attrs);
468
482
  if (0 == (ename = read_name_token(pi))) {
469
483
  return 0;
@@ -684,7 +698,7 @@ static char *read_element(PInfo pi) {
684
698
  first = 0;
685
699
  /* a child element */
686
700
  // Child closed with mismatched name.
687
- if (0 != (name = read_element(pi))) {
701
+ if (0 != (name = read_element(pi, depth + 1))) {
688
702
  attr_stack_cleanup(&attrs);
689
703
 
690
704
  if (0 ==
@@ -1056,7 +1070,8 @@ static char *read_coded_chars(PInfo pi, char *text) {
1056
1070
  break;
1057
1071
  }
1058
1072
  }
1059
- if (b > end) {
1073
+ if (b >= end) {
1074
+ // No terminating ; found in the first 31 bytes after an &.
1060
1075
  *text++ = '&';
1061
1076
  } else if ('#' == *buf) {
1062
1077
  uint64_t u = 0;
data/lib/ox/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Ox
2
2
  # Current version of the module.
3
- VERSION = '2.14.26'
3
+ VERSION = '2.14.28'
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ox
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.14.26
4
+ version: 2.14.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
@@ -51,7 +51,6 @@ files:
51
51
  - ext/ox/err.c
52
52
  - ext/ox/err.h
53
53
  - ext/ox/extconf.rb
54
- - ext/ox/foo.h
55
54
  - ext/ox/gen_load.c
56
55
  - ext/ox/hash_load.c
57
56
  - ext/ox/helper.h
@@ -123,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  requirements: []
126
- rubygems_version: 4.0.3
125
+ rubygems_version: 4.0.6
127
126
  specification_version: 4
128
127
  summary: A fast XML parser and object serializer.
129
128
  test_files: []
data/ext/ox/foo.h DELETED
@@ -1,204 +0,0 @@
1
- /* sax_buf.h
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- */
5
-
6
- #ifndef OX_SAX_BUF_H
7
- #define OX_SAX_BUF_H
8
-
9
- #include <stdio.h>
10
-
11
- typedef struct _buf {
12
- char base[0x00001000];
13
- char *head;
14
- char *end;
15
- char *tail;
16
- char *read_end; /* one past last character read */
17
- char *pro; /* protection start, buffer can not slide past this point */
18
- char *str; /* start of current string being read */
19
- off_t pos;
20
- off_t line;
21
- off_t col;
22
- off_t pro_pos;
23
- off_t pro_line;
24
- off_t pro_col;
25
- int (*read_func)(struct _buf *buf);
26
- union {
27
- int fd;
28
- VALUE io;
29
- const char *str;
30
- } in;
31
- struct _saxDrive *dr;
32
- } *Buf;
33
-
34
- typedef struct _checkPt {
35
- off_t pro_dif;
36
- off_t pos;
37
- off_t line;
38
- off_t col;
39
- char c;
40
- } *CheckPt;
41
-
42
- #define CHECK_PT_INIT {-1, 0, 0, 0, '\0'}
43
-
44
- extern void ox_sax_buf_init(Buf buf, VALUE io);
45
- extern int ox_sax_buf_read(Buf buf);
46
-
47
- static inline char buf_get(Buf buf) {
48
- // printf("*** drive get from '%s' from start: %ld buf: %p from read_end: %ld\n", buf->tail, buf->tail -
49
- // buf->head, buf->head, buf->read_end - buf->tail);
50
- if (buf->read_end <= buf->tail) {
51
- if (0 != ox_sax_buf_read(buf)) {
52
- return '\0';
53
- }
54
- }
55
- if ('\n' == *buf->tail) {
56
- buf->line++;
57
- buf->col = 0;
58
- } else {
59
- buf->col++;
60
- }
61
- buf->pos++;
62
-
63
- return *buf->tail++;
64
- }
65
-
66
- static inline void buf_backup(Buf buf) {
67
- buf->tail--;
68
- buf->col--;
69
- buf->pos--;
70
- if (0 >= buf->col) {
71
- buf->line--;
72
- // allow col to be negative since we never backup twice in a row
73
- }
74
- }
75
-
76
- static inline void buf_protect(Buf buf) {
77
- buf->pro = buf->tail;
78
- buf->str = buf->tail; // can't have str before pro
79
- buf->pro_pos = buf->pos;
80
- buf->pro_line = buf->line;
81
- buf->pro_col = buf->col;
82
- }
83
-
84
- static inline void buf_reset(Buf buf) {
85
- buf->tail = buf->pro;
86
- buf->pos = buf->pro_pos;
87
- buf->line = buf->pro_line;
88
- buf->col = buf->pro_col;
89
- }
90
-
91
- /* Starts by reading a character so it is safe to use with an empty or
92
- * compacted buffer.
93
- */
94
- static inline char buf_next_non_white(Buf buf) {
95
- char c;
96
-
97
- while ('\0' != (c = buf_get(buf))) {
98
- switch (c) {
99
- case ' ':
100
- case '\t':
101
- case '\f':
102
- case '\n':
103
- case '\r': break;
104
- default: return c;
105
- }
106
- }
107
- return '\0';
108
- }
109
-
110
- /* Starts by reading a character so it is safe to use with an empty or
111
- * compacted buffer.
112
- */
113
- static inline char buf_next_white(Buf buf) {
114
- char c;
115
-
116
- while ('\0' != (c = buf_get(buf))) {
117
- switch (c) {
118
- case ' ':
119
- case '\t':
120
- case '\f':
121
- case '\n':
122
- case '\r':
123
- case '\0': return c;
124
- default: break;
125
- }
126
- }
127
- return '\0';
128
- }
129
-
130
- static inline void buf_cleanup(Buf buf) {
131
- if (buf->base != buf->head && 0 != buf->head) {
132
- xfree(buf->head);
133
- buf->head = 0;
134
- }
135
- }
136
-
137
- static inline int is_white(char c) {
138
- switch (c) {
139
- case ' ':
140
- case '\t':
141
- case '\f':
142
- case '\n':
143
- case '\r': return 1;
144
- default: break;
145
- }
146
- return 0;
147
- }
148
-
149
- static inline void buf_checkpoint(Buf buf, CheckPt cp) {
150
- cp->pro_dif = (int)(buf->tail - buf->pro);
151
- cp->pos = buf->pos;
152
- cp->line = buf->line;
153
- cp->col = buf->col;
154
- cp->c = *(buf->tail - 1);
155
- }
156
-
157
- static inline int buf_checkset(CheckPt cp) {
158
- return (0 <= cp->pro_dif);
159
- }
160
-
161
- static inline char buf_checkback(Buf buf, CheckPt cp) {
162
- buf->tail = buf->pro + cp->pro_dif;
163
- buf->pos = cp->pos;
164
- buf->line = cp->line;
165
- buf->col = cp->col;
166
- return cp->c;
167
- }
168
-
169
- static inline void buf_collapse_return(char *str) {
170
- char *s = str;
171
- char *back = str;
172
-
173
- for (; '\0' != *s; s++) {
174
- if (back != str && '\n' == *s && '\r' == *(back - 1)) {
175
- *(back - 1) = '\n';
176
- } else {
177
- *back++ = *s;
178
- }
179
- }
180
- *back = '\0';
181
- }
182
-
183
- static inline void buf_collapse_white(char *str) {
184
- char *s = str;
185
- char *back = str;
186
-
187
- for (; '\0' != *s; s++) {
188
- switch (*s) {
189
- case ' ':
190
- case '\t':
191
- case '\f':
192
- case '\n':
193
- case '\r':
194
- if (back == str || ' ' != *(back - 1)) {
195
- *back++ = ' ';
196
- }
197
- break;
198
- default: *back++ = *s; break;
199
- }
200
- }
201
- *back = '\0';
202
- }
203
-
204
- #endif /* OX_SAX_BUF_H */