commonmarker 0.23.8 → 0.23.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5720fe7353e4f737164cc06dd97f54d8e203342bcaf2267b77e26231122ef65
4
- data.tar.gz: 1b929f138c087ce5da68ad0cdf61701ef0521db9e5d13c973b0ae81ea3c25bb1
3
+ metadata.gz: 8197daf1b6113b658c30459c0c4436ee688bfdd2381d3f541ff31bf3c3177bae
4
+ data.tar.gz: 25761b81607e28e9f569a61f502913c55f88319023b7d12e362d87dd37a935f7
5
5
  SHA512:
6
- metadata.gz: 2867a5effac7f5a52f1f850e8c9f9f4257105e14fafd34ea5bb688bf98357aa15aece3e55cc8df27df219624493d41e34a715dba4f631b2618e294dee30e61a7
7
- data.tar.gz: 938dfa5d2335d7ec2f9ee330b2d1c502c6c95809f8835ee1b6e1ba9c9bce13e5e73315f7add0feab39e229085266e416c8865f77b66731f915aeee78e87eb5ac
6
+ metadata.gz: 822690178eaaedc1e173e81e1d7c302d1670b95d362ca056ac9552df81a46861f582e4624698325e7d9bac347079fb4af6f99d88fbd832d40b84c4cbaff16cfc
7
+ data.tar.gz: 19150ccc6ec605f469e09fcf56a4dcb40f5b6e1125ca890bad8a2e6dc8931abcf82c533535222340c6377b16f243dd95717b7158bf2836118b8e1542802a6afe
@@ -296,7 +296,7 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
296
296
  // inline was finished in inlines.c.
297
297
  }
298
298
 
299
- static bool validate_protocol(char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
299
+ static bool validate_protocol(const char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
300
300
  size_t len = strlen(protocol);
301
301
 
302
302
  if (len > (max_rewind - rewind)) {
@@ -27,6 +27,14 @@
27
27
  #define CODE_INDENT 4
28
28
  #define TAB_STOP 4
29
29
 
30
+ /**
31
+ * Very deeply nested lists can cause quadratic performance issues.
32
+ * This constant is used in open_new_blocks() to limit the nesting
33
+ * depth. It is unlikely that a non-contrived markdown document will
34
+ * be nested this deeply.
35
+ */
36
+ #define MAX_LIST_DEPTH 100
37
+
30
38
  #ifndef MIN
31
39
  #define MIN(x, y) ((x < y) ? x : y)
32
40
  #endif
@@ -1119,10 +1127,11 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
1119
1127
  bool has_content;
1120
1128
  int save_offset;
1121
1129
  int save_column;
1130
+ size_t depth = 0;
1122
1131
 
1123
1132
  while (cont_type != CMARK_NODE_CODE_BLOCK &&
1124
1133
  cont_type != CMARK_NODE_HTML_BLOCK) {
1125
-
1134
+ depth++;
1126
1135
  S_find_first_nonspace(parser, input);
1127
1136
  indented = parser->indent >= CODE_INDENT;
1128
1137
 
@@ -1208,15 +1217,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
1208
1217
  parser->first_nonspace + 1);
1209
1218
  S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
1210
1219
  } else if (!indented &&
1211
- parser->options & CMARK_OPT_FOOTNOTES &&
1220
+ (parser->options & CMARK_OPT_FOOTNOTES) &&
1221
+ depth < MAX_LIST_DEPTH &&
1212
1222
  (matched = scan_footnote_definition(input, parser->first_nonspace))) {
1213
1223
  cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
1214
- cmark_chunk_to_cstr(parser->mem, &c);
1215
1224
 
1216
1225
  while (c.data[c.len - 1] != ']')
1217
1226
  --c.len;
1218
1227
  --c.len;
1219
1228
 
1229
+ cmark_chunk_to_cstr(parser->mem, &c);
1230
+
1220
1231
  S_advance_offset(parser, input, parser->first_nonspace + matched - parser->offset, false);
1221
1232
  *container = add_child(parser, *container, CMARK_NODE_FOOTNOTE_DEFINITION, parser->first_nonspace + matched + 1);
1222
1233
  (*container)->as.literal = c;
@@ -1224,6 +1235,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
1224
1235
  (*container)->internal_offset = matched;
1225
1236
  } else if ((!indented || cont_type == CMARK_NODE_LIST) &&
1226
1237
  parser->indent < 4 &&
1238
+ depth < MAX_LIST_DEPTH &&
1227
1239
  (matched = parse_list_marker(
1228
1240
  parser->mem, input, parser->first_nonspace,
1229
1241
  (*container)->type == CMARK_NODE_PARAGRAPH, &data))) {
@@ -413,6 +413,17 @@ CMARK_GFM_EXPORT int cmark_node_get_list_tight(cmark_node *node);
413
413
  */
414
414
  CMARK_GFM_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight);
415
415
 
416
+ /**
417
+ * Returns item index of 'node'. This is only used when rendering output
418
+ * formats such as commonmark, which need to output the index. It is not
419
+ * required for formats such as html or latex.
420
+ */
421
+ CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node);
422
+
423
+ /** Sets item index of 'node'. Returns 1 on success, 0 on failure.
424
+ */
425
+ CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx);
426
+
416
427
  /** Returns the info string from a fenced code block.
417
428
  */
418
429
  CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);
@@ -1,7 +1,7 @@
1
1
  #ifndef CMARK_GFM_VERSION_H
2
2
  #define CMARK_GFM_VERSION_H
3
3
 
4
- #define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 6)
5
- #define CMARK_GFM_VERSION_STRING "0.29.0.gfm.6"
4
+ #define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 11)
5
+ #define CMARK_GFM_VERSION_STRING "0.29.0.gfm.11"
6
6
 
7
7
  #endif
@@ -153,23 +153,8 @@ static bool is_autolink(cmark_node *node) {
153
153
  link_text->as.literal.len) == 0);
154
154
  }
155
155
 
156
- // if node is a block node, returns node.
157
- // otherwise returns first block-level node that is an ancestor of node.
158
- // if there is no block-level ancestor, returns NULL.
159
- static cmark_node *get_containing_block(cmark_node *node) {
160
- while (node) {
161
- if (CMARK_NODE_BLOCK_P(node)) {
162
- return node;
163
- } else {
164
- node = node->parent;
165
- }
166
- }
167
- return NULL;
168
- }
169
-
170
156
  static int S_render_node(cmark_renderer *renderer, cmark_node *node,
171
157
  cmark_event_type ev_type, int options) {
172
- cmark_node *tmp;
173
158
  int list_number;
174
159
  cmark_delim_type list_delim;
175
160
  int numticks;
@@ -180,7 +165,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
180
165
  char fencechar[2] = {'\0', '\0'};
181
166
  size_t info_len, code_len;
182
167
  char listmarker[LISTMARKER_SIZE];
183
- char *emph_delim;
168
+ const char *emph_delim;
184
169
  bool first_in_list_item;
185
170
  bufsize_t marker_width;
186
171
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
@@ -189,14 +174,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
189
174
  // Don't adjust tight list status til we've started the list.
190
175
  // Otherwise we loose the blank line between a paragraph and
191
176
  // a following list.
192
- if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
193
- tmp = get_containing_block(node);
194
- renderer->in_tight_list_item =
195
- tmp && // tmp might be NULL if there is no containing block
196
- ((tmp->type == CMARK_NODE_ITEM &&
197
- cmark_node_get_list_tight(tmp->parent)) ||
198
- (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
199
- cmark_node_get_list_tight(tmp->parent->parent)));
177
+ if (entering) {
178
+ if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
179
+ renderer->in_tight_list_item = node->parent->parent->as.list.tight;
180
+ }
181
+ } else {
182
+ if (node->type == CMARK_NODE_LIST) {
183
+ renderer->in_tight_list_item =
184
+ node->parent &&
185
+ node->parent->type == CMARK_NODE_ITEM &&
186
+ node->parent->parent->as.list.tight;
187
+ }
200
188
  }
201
189
 
202
190
  if (node->extension && node->extension->commonmark_render_func) {
@@ -234,13 +222,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
234
222
  if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
235
223
  marker_width = 4;
236
224
  } else {
237
- list_number = cmark_node_get_list_start(node->parent);
225
+ list_number = cmark_node_get_item_index(node);
238
226
  list_delim = cmark_node_get_list_delim(node->parent);
239
- tmp = node;
240
- while (tmp->prev) {
241
- tmp = tmp->prev;
242
- list_number += 1;
243
- }
244
227
  // we ensure a width of at least 4 so
245
228
  // we get nice transition from single digits
246
229
  // to double
@@ -405,10 +388,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
405
388
  break;
406
389
 
407
390
  case CMARK_NODE_STRONG:
408
- if (entering) {
409
- LIT("**");
410
- } else {
411
- LIT("**");
391
+ if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
392
+ if (entering) {
393
+ LIT("**");
394
+ } else {
395
+ LIT("**");
396
+ }
412
397
  }
413
398
  break;
414
399
 
@@ -364,10 +364,12 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
364
364
  break;
365
365
 
366
366
  case CMARK_NODE_STRONG:
367
- if (entering) {
368
- cmark_strbuf_puts(html, "<strong>");
369
- } else {
370
- cmark_strbuf_puts(html, "</strong>");
367
+ if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
368
+ if (entering) {
369
+ cmark_strbuf_puts(html, "<strong>");
370
+ } else {
371
+ cmark_strbuf_puts(html, "</strong>");
372
+ }
371
373
  }
372
374
  break;
373
375
 
@@ -385,10 +385,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
385
385
  break;
386
386
 
387
387
  case CMARK_NODE_STRONG:
388
- if (entering) {
389
- LIT("\\textbf{");
390
- } else {
391
- LIT("}");
388
+ if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
389
+ if (entering) {
390
+ LIT("\\textbf{");
391
+ } else {
392
+ LIT("}");
393
+ }
392
394
  }
393
395
  break;
394
396
 
@@ -74,7 +74,6 @@ static void S_outc(cmark_renderer *renderer, cmark_node *node,
74
74
 
75
75
  static int S_render_node(cmark_renderer *renderer, cmark_node *node,
76
76
  cmark_event_type ev_type, int options) {
77
- cmark_node *tmp;
78
77
  int list_number;
79
78
  bool entering = (ev_type == CMARK_EVENT_ENTER);
80
79
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
@@ -123,12 +122,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
123
122
  if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
124
123
  LIT("\\[bu] 2");
125
124
  } else {
126
- list_number = cmark_node_get_list_start(node->parent);
127
- tmp = node;
128
- while (tmp->prev) {
129
- tmp = tmp->prev;
130
- list_number += 1;
131
- }
125
+ list_number = cmark_node_get_item_index(node);
132
126
  char list_number_s[LIST_NUMBER_SIZE];
133
127
  snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
134
128
  LIT(list_number_s);
@@ -225,10 +219,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
225
219
  break;
226
220
 
227
221
  case CMARK_NODE_STRONG:
228
- if (entering) {
229
- LIT("\\f[B]");
230
- } else {
231
- LIT("\\f[]");
222
+ if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
223
+ if (entering) {
224
+ LIT("\\f[B]");
225
+ } else {
226
+ LIT("\\f[]");
227
+ }
232
228
  }
233
229
  break;
234
230
 
@@ -5,6 +5,16 @@
5
5
  #include "node.h"
6
6
  #include "syntax_extension.h"
7
7
 
8
+ /**
9
+ * Expensive safety checks are off by default, but can be enabled
10
+ * by calling cmark_enable_safety_checks().
11
+ */
12
+ static bool enable_safety_checks = false;
13
+
14
+ void cmark_enable_safety_checks(bool enable) {
15
+ enable_safety_checks = enable;
16
+ }
17
+
8
18
  static void S_node_unlink(cmark_node *node);
9
19
 
10
20
  #define NODE_MEM(node) cmark_node_mem(node)
@@ -29,7 +39,7 @@ void cmark_register_node_flag(cmark_node_internal_flags *flags) {
29
39
  nextflag <<= 1;
30
40
  }
31
41
 
32
- void cmark_init_standard_node_flags() {}
42
+ void cmark_init_standard_node_flags(void) {}
33
43
 
34
44
  bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) {
35
45
  if (child_type == CMARK_NODE_DOCUMENT) {
@@ -70,8 +80,6 @@ bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) {
70
80
  }
71
81
 
72
82
  static bool S_can_contain(cmark_node *node, cmark_node *child) {
73
- cmark_node *cur;
74
-
75
83
  if (node == NULL || child == NULL) {
76
84
  return false;
77
85
  }
@@ -79,14 +87,16 @@ static bool S_can_contain(cmark_node *node, cmark_node *child) {
79
87
  return 0;
80
88
  }
81
89
 
82
- // Verify that child is not an ancestor of node or equal to node.
83
- cur = node;
84
- do {
85
- if (cur == child) {
86
- return false;
87
- }
88
- cur = cur->parent;
89
- } while (cur != NULL);
90
+ if (enable_safety_checks) {
91
+ // Verify that child is not an ancestor of node or equal to node.
92
+ cmark_node *cur = node;
93
+ do {
94
+ if (cur == child) {
95
+ return false;
96
+ }
97
+ cur = cur->parent;
98
+ } while (cur != NULL);
99
+ }
90
100
 
91
101
  return cmark_node_can_contain_type(node, (cmark_node_type) child->type);
92
102
  }
@@ -367,6 +377,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
367
377
  case CMARK_NODE_HTML_INLINE:
368
378
  case CMARK_NODE_CODE:
369
379
  case CMARK_NODE_FOOTNOTE_REFERENCE:
380
+ case CMARK_NODE_FOOTNOTE_DEFINITION:
370
381
  return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal);
371
382
 
372
383
  case CMARK_NODE_CODE_BLOCK:
@@ -554,6 +565,31 @@ int cmark_node_set_list_tight(cmark_node *node, int tight) {
554
565
  }
555
566
  }
556
567
 
568
+ int cmark_node_get_item_index(cmark_node *node) {
569
+ if (node == NULL) {
570
+ return 0;
571
+ }
572
+
573
+ if (node->type == CMARK_NODE_ITEM) {
574
+ return node->as.list.start;
575
+ } else {
576
+ return 0;
577
+ }
578
+ }
579
+
580
+ int cmark_node_set_item_index(cmark_node *node, int idx) {
581
+ if (node == NULL || idx < 0) {
582
+ return 0;
583
+ }
584
+
585
+ if (node->type == CMARK_NODE_ITEM) {
586
+ node->as.list.start = idx;
587
+ return 1;
588
+ } else {
589
+ return 0;
590
+ }
591
+ }
592
+
557
593
  const char *cmark_node_get_fence_info(cmark_node *node) {
558
594
  if (node == NULL) {
559
595
  return NULL;
@@ -82,6 +82,14 @@ struct cmark_node {
82
82
 
83
83
  cmark_syntax_extension *extension;
84
84
 
85
+ /**
86
+ * Used during cmark_render() to cache the most recent non-NULL
87
+ * extension, if you go up the parent chain like this:
88
+ *
89
+ * node->parent->...parent->extension
90
+ */
91
+ cmark_syntax_extension *ancestor_extension;
92
+
85
93
  union {
86
94
  int ref_ix;
87
95
  int def_count;
@@ -97,6 +105,7 @@ struct cmark_node {
97
105
  cmark_link link;
98
106
  cmark_custom custom;
99
107
  int html_block_type;
108
+ int cell_index; // For keeping track of TABLE_CELL table alignments
100
109
  void *opaque;
101
110
  } as;
102
111
  };
@@ -119,7 +128,7 @@ void cmark_register_node_flag(cmark_node_internal_flags *flags);
119
128
  * library. It is now a no-op.
120
129
  */
121
130
  CMARK_GFM_EXPORT
122
- void cmark_init_standard_node_flags();
131
+ void cmark_init_standard_node_flags(void);
123
132
 
124
133
  static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) {
125
134
  return node->content.mem;
@@ -144,6 +153,13 @@ static CMARK_INLINE bool CMARK_NODE_INLINE_P(cmark_node *node) {
144
153
 
145
154
  CMARK_GFM_EXPORT bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type);
146
155
 
156
+ /**
157
+ * Enable (or disable) extra safety checks. These extra checks cause
158
+ * extra performance overhead (in some cases quadratic), so they are only
159
+ * intended to be used during testing.
160
+ */
161
+ CMARK_GFM_EXPORT void cmark_enable_safety_checks(bool enable);
162
+
147
163
  #ifdef __cplusplus
148
164
  }
149
165
  #endif
@@ -16,23 +16,8 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
16
16
  cmark_render_code_point(renderer, c);
17
17
  }
18
18
 
19
- // if node is a block node, returns node.
20
- // otherwise returns first block-level node that is an ancestor of node.
21
- // if there is no block-level ancestor, returns NULL.
22
- static cmark_node *get_containing_block(cmark_node *node) {
23
- while (node) {
24
- if (CMARK_NODE_BLOCK_P(node)) {
25
- return node;
26
- } else {
27
- node = node->parent;
28
- }
29
- }
30
- return NULL;
31
- }
32
-
33
19
  static int S_render_node(cmark_renderer *renderer, cmark_node *node,
34
20
  cmark_event_type ev_type, int options) {
35
- cmark_node *tmp;
36
21
  int list_number;
37
22
  cmark_delim_type list_delim;
38
23
  int i;
@@ -46,14 +31,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
46
31
  // Don't adjust tight list status til we've started the list.
47
32
  // Otherwise we loose the blank line between a paragraph and
48
33
  // a following list.
49
- if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
50
- tmp = get_containing_block(node);
51
- renderer->in_tight_list_item =
52
- tmp && // tmp might be NULL if there is no containing block
53
- ((tmp->type == CMARK_NODE_ITEM &&
54
- cmark_node_get_list_tight(tmp->parent)) ||
55
- (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
56
- cmark_node_get_list_tight(tmp->parent->parent)));
34
+ if (entering) {
35
+ if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
36
+ renderer->in_tight_list_item = node->parent->parent->as.list.tight;
37
+ }
38
+ } else {
39
+ if (node->type == CMARK_NODE_LIST) {
40
+ renderer->in_tight_list_item =
41
+ node->parent &&
42
+ node->parent->type == CMARK_NODE_ITEM &&
43
+ node->parent->parent->as.list.tight;
44
+ }
57
45
  }
58
46
 
59
47
  if (node->extension && node->extension->plaintext_render_func) {
@@ -79,13 +67,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
79
67
  if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
80
68
  marker_width = 4;
81
69
  } else {
82
- list_number = cmark_node_get_list_start(node->parent);
70
+ list_number = cmark_node_get_item_index(node);
83
71
  list_delim = cmark_node_get_list_delim(node->parent);
84
- tmp = node;
85
- while (tmp->prev) {
86
- tmp = tmp->prev;
87
- list_number += 1;
88
- }
89
72
  // we ensure a width of at least 4 so
90
73
  // we get nice transition from single digits
91
74
  // to double
@@ -31,13 +31,7 @@ static void S_out(cmark_renderer *renderer, cmark_node *node,
31
31
  cmark_chunk remainder = cmark_chunk_literal("");
32
32
  int k = renderer->buffer->size - 1;
33
33
 
34
- cmark_syntax_extension *ext = NULL;
35
- cmark_node *n = node;
36
- while (n && !ext) {
37
- ext = n->extension;
38
- if (!ext)
39
- n = n->parent;
40
- }
34
+ cmark_syntax_extension *ext = node->ancestor_extension;
41
35
  if (ext && !ext->commonmark_escape_func)
42
36
  ext = NULL;
43
37
 
@@ -182,6 +176,20 @@ char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
182
176
 
183
177
  while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
184
178
  cur = cmark_iter_get_node(iter);
179
+ if (cur->extension) {
180
+ cur->ancestor_extension = cur->extension;
181
+ } else if (cur->parent) {
182
+ cur->ancestor_extension = cur->parent->ancestor_extension;
183
+ }
184
+ if (cur->type == CMARK_NODE_ITEM) {
185
+ // Calculate the list item's index, for the benefit of output formats
186
+ // like commonmark and plaintext.
187
+ if (cur->prev) {
188
+ cmark_node_set_item_index(cur, 1 + cmark_node_get_item_index(cur->prev));
189
+ } else {
190
+ cmark_node_set_item_index(cur, cmark_node_get_list_start(cur->parent));
191
+ }
192
+ }
185
193
  if (!render_node(&renderer, cur, ev_type, options)) {
186
194
  // a false value causes us to skip processing
187
195
  // the node's contents. this is used for
@@ -11,6 +11,9 @@
11
11
  #include "table.h"
12
12
  #include "cmark-gfm-core-extensions.h"
13
13
 
14
+ // Limit to prevent a malicious input from causing a denial of service.
15
+ #define MAX_AUTOCOMPLETED_CELLS 0x80000
16
+
14
17
  // Custom node flag, initialized in `create_table_extension`.
15
18
  static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
16
19
 
@@ -31,6 +34,8 @@ typedef struct {
31
34
  typedef struct {
32
35
  uint16_t n_columns;
33
36
  uint8_t *alignments;
37
+ int n_rows;
38
+ int n_nonempty_cells;
34
39
  } node_table;
35
40
 
36
41
  typedef struct {
@@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
83
88
  return 1;
84
89
  }
85
90
 
91
+ // Increment the number of rows in the table. Also update n_nonempty_cells,
92
+ // which keeps track of the number of cells which were parsed from the
93
+ // input file. (If one of the rows is too short, then the trailing cells
94
+ // are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
95
+ // The purpose of this is to prevent a malicious input from generating a very
96
+ // large number of autocompleted cells, which could cause a denial of service
97
+ // vulnerability.
98
+ static int incr_table_row_count(cmark_node *node, int i) {
99
+ if (!node || node->type != CMARK_NODE_TABLE) {
100
+ return 0;
101
+ }
102
+
103
+ ((node_table *)node->as.opaque)->n_rows++;
104
+ ((node_table *)node->as.opaque)->n_nonempty_cells += i;
105
+ return 1;
106
+ }
107
+
108
+ // Calculate the number of autocompleted cells.
109
+ static int get_n_autocompleted_cells(cmark_node *node) {
110
+ if (!node || node->type != CMARK_NODE_TABLE) {
111
+ return 0;
112
+ }
113
+
114
+ const node_table *nt = (node_table *)node->as.opaque;
115
+ return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
116
+ }
117
+
86
118
  static uint8_t *get_table_alignments(cmark_node *node) {
87
119
  if (!node || node->type != CMARK_NODE_TABLE)
88
120
  return 0;
@@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
98
130
  return 1;
99
131
  }
100
132
 
133
+ static uint8_t get_cell_alignment(cmark_node *node) {
134
+ if (!node || node->type != CMARK_NODE_TABLE_CELL)
135
+ return 0;
136
+
137
+ const uint8_t *alignments = get_table_alignments(node->parent->parent);
138
+ int i = node->as.cell_index;
139
+ return alignments[i];
140
+ }
141
+
142
+ static int set_cell_index(cmark_node *node, int i) {
143
+ if (!node || node->type != CMARK_NODE_TABLE_CELL)
144
+ return 0;
145
+
146
+ node->as.cell_index = i;
147
+ return 1;
148
+ }
149
+
101
150
  static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
102
151
  {
103
152
  cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
@@ -257,7 +306,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
257
306
  unsigned char *input, int len) {
258
307
  cmark_node *table_header;
259
308
  table_row *header_row = NULL;
260
- table_row *marker_row = NULL;
309
+ table_row *delimiter_row = NULL;
261
310
  node_table_row *ntr;
262
311
  const char *parent_string;
263
312
  uint16_t i;
@@ -270,16 +319,16 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
270
319
  return parent_container;
271
320
  }
272
321
 
273
- // Since scan_table_start was successful, we must have a marker row.
274
- marker_row = row_from_string(self, parser,
275
- input + cmark_parser_get_first_nonspace(parser),
276
- len - cmark_parser_get_first_nonspace(parser));
322
+ // Since scan_table_start was successful, we must have a delimiter row.
323
+ delimiter_row = row_from_string(
324
+ self, parser, input + cmark_parser_get_first_nonspace(parser),
325
+ len - cmark_parser_get_first_nonspace(parser));
277
326
  // assert may be optimized out, don't rely on it for security boundaries
278
- if (!marker_row) {
327
+ if (!delimiter_row) {
279
328
  return parent_container;
280
329
  }
281
-
282
- assert(marker_row);
330
+
331
+ assert(delimiter_row);
283
332
 
284
333
  cmark_arena_push();
285
334
 
@@ -289,8 +338,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
289
338
  parent_string = cmark_node_get_string_content(parent_container);
290
339
  header_row = row_from_string(self, parser, (unsigned char *)parent_string,
291
340
  (int)strlen(parent_string));
292
- if (!header_row || header_row->n_columns != marker_row->n_columns) {
293
- free_table_row(parser->mem, marker_row);
341
+ if (!header_row || header_row->n_columns != delimiter_row->n_columns) {
342
+ free_table_row(parser->mem, delimiter_row);
294
343
  free_table_row(parser->mem, header_row);
295
344
  cmark_arena_pop();
296
345
  parent_container->flags |= CMARK_NODE__TABLE_VISITED;
@@ -298,14 +347,14 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
298
347
  }
299
348
 
300
349
  if (cmark_arena_pop()) {
301
- marker_row = row_from_string(
350
+ delimiter_row = row_from_string(
302
351
  self, parser, input + cmark_parser_get_first_nonspace(parser),
303
352
  len - cmark_parser_get_first_nonspace(parser));
304
353
  header_row = row_from_string(self, parser, (unsigned char *)parent_string,
305
354
  (int)strlen(parent_string));
306
355
  // row_from_string can return NULL, add additional check to ensure n_columns match
307
- if (!marker_row || !header_row || header_row->n_columns != marker_row->n_columns) {
308
- free_table_row(parser->mem, marker_row);
356
+ if (!delimiter_row || !header_row || header_row->n_columns != delimiter_row->n_columns) {
357
+ free_table_row(parser->mem, delimiter_row);
309
358
  free_table_row(parser->mem, header_row);
310
359
  return parent_container;
311
360
  }
@@ -313,7 +362,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
313
362
 
314
363
  if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
315
364
  free_table_row(parser->mem, header_row);
316
- free_table_row(parser->mem, marker_row);
365
+ free_table_row(parser->mem, delimiter_row);
317
366
  return parent_container;
318
367
  }
319
368
 
@@ -326,12 +375,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
326
375
  parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
327
376
  set_n_table_columns(parent_container, header_row->n_columns);
328
377
 
329
- // allocate alignments based on marker_row->n_columns
330
- // since we populate the alignments array based on marker_row->cells
378
+ // allocate alignments based on delimiter_row->n_columns
379
+ // since we populate the alignments array based on delimiter_row->cells
331
380
  uint8_t *alignments =
332
- (uint8_t *)parser->mem->calloc(marker_row->n_columns, sizeof(uint8_t));
333
- for (i = 0; i < marker_row->n_columns; ++i) {
334
- node_cell *node = &marker_row->cells[i];
381
+ (uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t));
382
+ for (i = 0; i < delimiter_row->n_columns; ++i) {
383
+ node_cell *node = &delimiter_row->cells[i];
335
384
  bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
336
385
 
337
386
  if (left && right)
@@ -353,25 +402,26 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
353
402
  table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
354
403
  ntr->is_header = true;
355
404
 
356
- {
357
- for (i = 0; i < header_row->n_columns; ++i) {
358
- node_cell *cell = &header_row->cells[i];
359
- cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
360
- CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
361
- header_cell->start_line = header_cell->end_line = parent_container->start_line;
362
- header_cell->internal_offset = cell->internal_offset;
363
- header_cell->end_column = parent_container->start_column + cell->end_offset;
364
- cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
365
- cmark_node_set_syntax_extension(header_cell, self);
366
- }
405
+ for (i = 0; i < header_row->n_columns; ++i) {
406
+ node_cell *cell = &header_row->cells[i];
407
+ cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
408
+ CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
409
+ header_cell->start_line = header_cell->end_line = parent_container->start_line;
410
+ header_cell->internal_offset = cell->internal_offset;
411
+ header_cell->end_column = parent_container->start_column + cell->end_offset;
412
+ cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
413
+ cmark_node_set_syntax_extension(header_cell, self);
414
+ set_cell_index(header_cell, i);
367
415
  }
368
416
 
417
+ incr_table_row_count(parent_container, i);
418
+
369
419
  cmark_parser_advance_offset(
370
420
  parser, (char *)input,
371
421
  (int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
372
422
 
373
423
  free_table_row(parser->mem, header_row);
374
- free_table_row(parser->mem, marker_row);
424
+ free_table_row(parser->mem, delimiter_row);
375
425
  return parent_container;
376
426
  }
377
427
 
@@ -385,6 +435,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
385
435
  if (cmark_parser_is_blank(parser))
386
436
  return NULL;
387
437
 
438
+ if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
439
+ return NULL;
440
+ }
441
+
388
442
  table_row_block =
389
443
  cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
390
444
  parent_container->start_column);
@@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
412
466
  node->end_column = parent_container->start_column + cell->end_offset;
413
467
  cmark_node_set_string_content(node, (char *) cell->buf->ptr);
414
468
  cmark_node_set_syntax_extension(node, self);
469
+ set_cell_index(node, i);
415
470
  }
416
471
 
472
+ incr_table_row_count(parent_container, i);
473
+
417
474
  for (; i < table_columns; ++i) {
418
475
  cmark_node *node = cmark_parser_add_child(
419
476
  parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
420
477
  cmark_node_set_syntax_extension(node, self);
478
+ set_cell_index(node, i);
421
479
  }
422
480
  }
423
481
 
@@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
602
660
  cmark_node *node) {
603
661
  if (node->type == CMARK_NODE_TABLE_CELL) {
604
662
  if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
605
- uint8_t *alignments = get_table_alignments(node->parent->parent);
606
- int i = 0;
607
- cmark_node *n;
608
- for (n = node->parent->first_child; n; n = n->next, ++i)
609
- if (n == node)
610
- break;
611
- switch (alignments[i]) {
663
+ switch (get_cell_alignment(node)) {
612
664
  case 'l': return " align=\"left\"";
613
665
  case 'c': return " align=\"center\"";
614
666
  case 'r': return " align=\"right\"";
@@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
696
748
  cmark_event_type ev_type, int options) {
697
749
  bool entering = (ev_type == CMARK_EVENT_ENTER);
698
750
  cmark_strbuf *html = renderer->html;
699
- cmark_node *n;
700
751
 
701
752
  // XXX: we just monopolise renderer->opaque.
702
753
  struct html_table_state *table_state =
@@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
745
796
  }
746
797
  }
747
798
  } else if (node->type == CMARK_NODE_TABLE_CELL) {
748
- uint8_t *alignments = get_table_alignments(node->parent->parent);
749
799
  if (entering) {
750
800
  cmark_html_render_cr(html);
751
801
  if (table_state->in_table_header) {
@@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
754
804
  cmark_strbuf_puts(html, "<td");
755
805
  }
756
806
 
757
- int i = 0;
758
- for (n = node->parent->first_child; n; n = n->next, ++i)
759
- if (n == node)
760
- break;
761
-
762
- switch (alignments[i]) {
807
+ switch (get_cell_alignment(node)) {
763
808
  case 'l': html_table_add_align(html, "left", options); break;
764
809
  case 'c': html_table_add_align(html, "center", options); break;
765
810
  case 'r': html_table_add_align(html, "right", options); break;
@@ -11,6 +11,7 @@
11
11
  #include "syntax_extension.h"
12
12
 
13
13
  #define BUFFER_SIZE 100
14
+ #define MAX_INDENT 40
14
15
 
15
16
  // Functions to convert cmark_nodes to XML strings.
16
17
 
@@ -26,7 +27,7 @@ struct render_state {
26
27
 
27
28
  static CMARK_INLINE void indent(struct render_state *state) {
28
29
  int i;
29
- for (i = 0; i < state->indent; i++) {
30
+ for (i = 0; i < state->indent && i < MAX_INDENT; i++) {
30
31
  cmark_strbuf_putc(state->xml, ' ');
31
32
  }
32
33
  }
@@ -129,8 +129,12 @@ module CommonMarker
129
129
  out("<em>", :children, "</em>")
130
130
  end
131
131
 
132
- def strong(_)
133
- out("<strong>", :children, "</strong>")
132
+ def strong(node)
133
+ if node.parent&.type == :strong
134
+ out(:children)
135
+ else
136
+ out("<strong>", :children, "</strong>")
137
+ end
134
138
  end
135
139
 
136
140
  def link(node)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CommonMarker
4
- VERSION = "0.23.8"
4
+ VERSION = "0.23.10"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commonmarker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.8
4
+ version: 0.23.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-01-31 00:00:00.000000000 Z
12
+ date: 2023-07-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: awesome_print