ox 2.14.26 → 2.14.27

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: b981c5e93cbbe907c8beb655007ebd2d3bfd740da911372ce82ea329e85773f0
4
+ data.tar.gz: 98935b178f268fad229368d02b2efd14529b4144aebae15b557aed80d20a5aac
5
5
  SHA512:
6
- metadata.gz: c1158647c428da2193d3de8c8a564077dc3e7854890dd9b31d79747e65dc14595a381ab079aaa3b9471d2b0de30d96c4b8698d1b82e2e53d34bfa6ff116a35cd
7
- data.tar.gz: 43fcda76ed7d4d5febf5dc67e228278af4b16fcff4ff221ef4a3b39dbf0451d1da3f2ff25524079026ed482d14f0ae3d74af2019cb88f5296e6ebc340e4091b7
6
+ metadata.gz: aaa1d3f2183519ef26fd82b80f1bae7bef61216e8404ae03fcc0ad7c9b8e450b2acb423cb7f113be08fa3cab8ba448cf25425e7cc12e138a63e04c93642e2523
7
+ data.tar.gz: b0b0e20339a346d17a3a29b57e8c1d722b5999d346d775c6f587400aa8affa30e63186a5a6109dd59091fd13ecc172ef93d69e8168bc65a2aa70bd66e76f58ec
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All changes to the Ox gem are documented here. Releases follow semantic versioning.
4
4
 
5
+ ## [2.14.27] - 2026-06-18
6
+
7
+ ### Fixed
8
+
9
+ - Fixed stack overflow issue in the parser that occurred when reading
10
+ special character sequences.
11
+
12
+ - Set a limit of 1000 as the maximum nesting depth of elements to
13
+ avoid stack exhaustion.
14
+
15
+ - Added a note on the symbolize_keys option indicating it should not
16
+ be used on unregulated input where the symbol table could grow
17
+ indefinitely.
18
+
5
19
  ## [2.14.26] - 2026-05-09
6
20
 
7
21
  ### 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,13 @@
18
18
  #include "ruby.h"
19
19
  #include "special.h"
20
20
 
21
+ #define MAX_ELEMENT_DEPTH 1000
22
+
21
23
  static void mark_pi_cb(void *ptr);
22
24
  static void read_instruction(PInfo pi);
23
25
  static void read_doctype(PInfo pi);
24
26
  static void read_comment(PInfo pi);
25
- static char *read_element(PInfo pi);
27
+ static char *read_element(PInfo pi, int depth);
26
28
  static void read_text(PInfo pi);
27
29
  /*static void read_reduced_text(PInfo pi); */
28
30
  static void read_cdata(PInfo pi);
@@ -216,7 +218,7 @@ ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options
216
218
  helper_stack_cleanup(&pi.helpers);
217
219
  return Qnil;
218
220
  default:
219
- read_element(&pi);
221
+ read_element(&pi, 0);
220
222
  body_read = 1;
221
223
  break;
222
224
  }
@@ -452,7 +454,7 @@ static void read_comment(PInfo pi) {
452
454
 
453
455
  // Entered after the '<' and the first character after that. Returns stat
454
456
  // code.
455
- static char *read_element(PInfo pi) {
457
+ static char *read_element(PInfo pi, int depth) {
456
458
  struct _attrStack attrs;
457
459
  const char *attr_name;
458
460
  const char *attr_value;
@@ -464,6 +466,10 @@ static char *read_element(PInfo pi) {
464
466
  int hasChildren = 0;
465
467
  int done = 0;
466
468
 
469
+ if (MAX_ELEMENT_DEPTH < depth) {
470
+ set_error(&pi->err, "element nested too deeply, limit is 1000", pi->str, pi->s);
471
+ return 0;
472
+ }
467
473
  attr_stack_init(&attrs);
468
474
  if (0 == (ename = read_name_token(pi))) {
469
475
  return 0;
@@ -684,7 +690,7 @@ static char *read_element(PInfo pi) {
684
690
  first = 0;
685
691
  /* a child element */
686
692
  // Child closed with mismatched name.
687
- if (0 != (name = read_element(pi))) {
693
+ if (0 != (name = read_element(pi, depth + 1))) {
688
694
  attr_stack_cleanup(&attrs);
689
695
 
690
696
  if (0 ==
@@ -1056,7 +1062,8 @@ static char *read_coded_chars(PInfo pi, char *text) {
1056
1062
  break;
1057
1063
  }
1058
1064
  }
1059
- if (b > end) {
1065
+ if (b >= end) {
1066
+ // No terminating ; found in the first 31 bytes after an &.
1060
1067
  *text++ = '&';
1061
1068
  } else if ('#' == *buf) {
1062
1069
  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.27'
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.27
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 */