commonmarker 0.23.8 → 0.23.10

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: 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