commonmarker 0.23.1 → 0.23.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of commonmarker might be problematic. Click here for more details.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +56 -55
  3. data/bin/commonmarker +2 -7
  4. data/commonmarker.gemspec +27 -26
  5. data/ext/commonmarker/blocks.c +13 -2
  6. data/ext/commonmarker/cmark-gfm_version.h +2 -2
  7. data/ext/commonmarker/commonmark.c +14 -4
  8. data/ext/commonmarker/commonmarker.c +30 -44
  9. data/ext/commonmarker/ext_scanners.c +360 -640
  10. data/ext/commonmarker/footnotes.c +23 -0
  11. data/ext/commonmarker/footnotes.h +2 -0
  12. data/ext/commonmarker/html.c +40 -19
  13. data/ext/commonmarker/inlines.c +69 -11
  14. data/ext/commonmarker/node.h +7 -0
  15. data/ext/commonmarker/scanners.c +2438 -2450
  16. data/ext/commonmarker/table.c +98 -53
  17. data/lib/commonmarker/config.rb +1 -1
  18. data/lib/commonmarker/node/inspect.rb +8 -18
  19. data/lib/commonmarker/node.rb +6 -6
  20. data/lib/commonmarker/renderer/html_renderer.rb +37 -37
  21. data/lib/commonmarker/renderer.rb +5 -5
  22. data/lib/commonmarker/version.rb +1 -1
  23. data/lib/commonmarker.rb +9 -11
  24. metadata +6 -57
  25. data/test/benchmark.rb +0 -32
  26. data/test/fixtures/curly.md +0 -1
  27. data/test/fixtures/dingus.md +0 -10
  28. data/test/fixtures/strong.md +0 -1
  29. data/test/fixtures/table.md +0 -10
  30. data/test/test_attributes.rb +0 -24
  31. data/test/test_basics.rb +0 -35
  32. data/test/test_commands.rb +0 -72
  33. data/test/test_commonmark.rb +0 -36
  34. data/test/test_doc.rb +0 -130
  35. data/test/test_encoding.rb +0 -23
  36. data/test/test_extensions.rb +0 -116
  37. data/test/test_footnotes.rb +0 -48
  38. data/test/test_gc.rb +0 -47
  39. data/test/test_helper.rb +0 -71
  40. data/test/test_linebreaks.rb +0 -15
  41. data/test/test_maliciousness.rb +0 -262
  42. data/test/test_node.rb +0 -89
  43. data/test/test_options.rb +0 -37
  44. data/test/test_pathological_inputs.rb +0 -94
  45. data/test/test_plaintext.rb +0 -46
  46. data/test/test_renderer.rb +0 -47
  47. data/test/test_smartpunct.rb +0 -27
  48. data/test/test_spec.rb +0 -30
  49. data/test/test_tasklists.rb +0 -43
  50. data/test/test_xml.rb +0 -107
@@ -38,3 +38,26 @@ void cmark_footnote_create(cmark_map *map, cmark_node *node) {
38
38
  cmark_map *cmark_footnote_map_new(cmark_mem *mem) {
39
39
  return cmark_map_new(mem, footnote_free);
40
40
  }
41
+
42
+ // Before calling `cmark_map_free` on a map with `cmark_footnotes`, first
43
+ // unlink all of the footnote nodes before freeing their memory.
44
+ //
45
+ // Sometimes, two (unused) footnote nodes can end up referencing each other,
46
+ // which as they get freed up by calling `cmark_map_free` -> `footnote_free` ->
47
+ // etc, can lead to a use-after-free error.
48
+ //
49
+ // Better to `unlink` every footnote node first, setting their next, prev, and
50
+ // parent pointers to NULL, and only then walk thru & free them up.
51
+ void cmark_unlink_footnotes_map(cmark_map *map) {
52
+ cmark_map_entry *ref;
53
+ cmark_map_entry *next;
54
+
55
+ ref = map->refs;
56
+ while(ref) {
57
+ next = ref->next;
58
+ if (((cmark_footnote *)ref)->node) {
59
+ cmark_node_unlink(((cmark_footnote *)ref)->node);
60
+ }
61
+ ref = next;
62
+ }
63
+ }
@@ -18,6 +18,8 @@ typedef struct cmark_footnote cmark_footnote;
18
18
  void cmark_footnote_create(cmark_map *map, cmark_node *node);
19
19
  cmark_map *cmark_footnote_map_new(cmark_mem *mem);
20
20
 
21
+ void cmark_unlink_footnotes_map(cmark_map *map);
22
+
21
23
  #ifdef __cplusplus
22
24
  }
23
25
  #endif
@@ -59,16 +59,30 @@ static void filter_html_block(cmark_html_renderer *renderer, uint8_t *data, size
59
59
  cmark_strbuf_put(html, data, (bufsize_t)len);
60
60
  }
61
61
 
62
- static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html) {
62
+ static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html, cmark_node *node) {
63
63
  if (renderer->written_footnote_ix >= renderer->footnote_ix)
64
64
  return false;
65
65
  renderer->written_footnote_ix = renderer->footnote_ix;
66
66
 
67
- cmark_strbuf_puts(html, "<a href=\"#fnref");
68
- char n[32];
69
- snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
70
- cmark_strbuf_puts(html, n);
71
- cmark_strbuf_puts(html, "\" class=\"footnote-backref\">↩</a>");
67
+ cmark_strbuf_puts(html, "<a href=\"#fnref-");
68
+ houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
69
+ cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a>");
70
+
71
+ if (node->footnote.def_count > 1)
72
+ {
73
+ for(int i = 2; i <= node->footnote.def_count; i++) {
74
+ char n[32];
75
+ snprintf(n, sizeof(n), "%d", i);
76
+
77
+ cmark_strbuf_puts(html, " <a href=\"#fnref-");
78
+ houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
79
+ cmark_strbuf_puts(html, "-");
80
+ cmark_strbuf_puts(html, n);
81
+ cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩<sup class=\"footnote-ref\">");
82
+ cmark_strbuf_puts(html, n);
83
+ cmark_strbuf_puts(html, "</sup></a>");
84
+ }
85
+ }
72
86
 
73
87
  return true;
74
88
  }
@@ -273,7 +287,7 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
273
287
  } else {
274
288
  if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) {
275
289
  cmark_strbuf_putc(html, ' ');
276
- S_put_footnote_backref(renderer, html);
290
+ S_put_footnote_backref(renderer, html, parent);
277
291
  }
278
292
  cmark_strbuf_puts(html, "</p>\n");
279
293
  }
@@ -392,16 +406,15 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
392
406
  case CMARK_NODE_FOOTNOTE_DEFINITION:
393
407
  if (entering) {
394
408
  if (renderer->footnote_ix == 0) {
395
- cmark_strbuf_puts(html, "<section class=\"footnotes\">\n<ol>\n");
409
+ cmark_strbuf_puts(html, "<section class=\"footnotes\" data-footnotes>\n<ol>\n");
396
410
  }
397
411
  ++renderer->footnote_ix;
398
- cmark_strbuf_puts(html, "<li id=\"fn");
399
- char n[32];
400
- snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
401
- cmark_strbuf_puts(html, n);
412
+
413
+ cmark_strbuf_puts(html, "<li id=\"fn-");
414
+ houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
402
415
  cmark_strbuf_puts(html, "\">\n");
403
416
  } else {
404
- if (S_put_footnote_backref(renderer, html)) {
417
+ if (S_put_footnote_backref(renderer, html, node)) {
405
418
  cmark_strbuf_putc(html, '\n');
406
419
  }
407
420
  cmark_strbuf_puts(html, "</li>\n");
@@ -410,12 +423,20 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
410
423
 
411
424
  case CMARK_NODE_FOOTNOTE_REFERENCE:
412
425
  if (entering) {
413
- cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn");
414
- cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
415
- cmark_strbuf_puts(html, "\" id=\"fnref");
416
- cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
417
- cmark_strbuf_puts(html, "\">");
418
- cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
426
+ cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn-");
427
+ houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
428
+ cmark_strbuf_puts(html, "\" id=\"fnref-");
429
+ houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
430
+
431
+ if (node->footnote.ref_ix > 1) {
432
+ char n[32];
433
+ snprintf(n, sizeof(n), "%d", node->footnote.ref_ix);
434
+ cmark_strbuf_puts(html, "-");
435
+ cmark_strbuf_puts(html, n);
436
+ }
437
+
438
+ cmark_strbuf_puts(html, "\" data-footnote-ref>");
439
+ houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
419
440
  cmark_strbuf_puts(html, "</a></sup>");
420
441
  }
421
442
  break;
@@ -1137,19 +1137,77 @@ noMatch:
1137
1137
  // What if we're a footnote link?
1138
1138
  if (parser->options & CMARK_OPT_FOOTNOTES &&
1139
1139
  opener->inl_text->next &&
1140
- opener->inl_text->next->type == CMARK_NODE_TEXT &&
1141
- !opener->inl_text->next->next) {
1140
+ opener->inl_text->next->type == CMARK_NODE_TEXT) {
1141
+
1142
1142
  cmark_chunk *literal = &opener->inl_text->next->as.literal;
1143
- if (literal->len > 1 && literal->data[0] == '^') {
1144
- inl = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE);
1145
- inl->as.literal = cmark_chunk_dup(literal, 1, literal->len - 1);
1146
- inl->start_line = inl->end_line = subj->line;
1147
- inl->start_column = opener->inl_text->start_column;
1148
- inl->end_column = subj->pos + subj->column_offset + subj->block_offset;
1149
- cmark_node_insert_before(opener->inl_text, inl);
1150
- cmark_node_free(opener->inl_text->next);
1151
- cmark_node_free(opener->inl_text);
1143
+
1144
+ // look back to the opening '[', and skip ahead to the next character
1145
+ // if we're looking at a '[^' sequence, and there is other text or nodes
1146
+ // after the ^, let's call it a footnote reference.
1147
+ if ((literal->len > 0 && literal->data[0] == '^') && (literal->len > 1 || opener->inl_text->next->next)) {
1148
+
1149
+ // Before we got this far, the `handle_close_bracket` function may have
1150
+ // advanced the current state beyond our footnote's actual closing
1151
+ // bracket, ie if it went looking for a `link_label`.
1152
+ // Let's just rewind the subject's position:
1153
+ subj->pos = initial_pos;
1154
+
1155
+ cmark_node *fnref = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE);
1156
+
1157
+ // the start and end of the footnote ref is the opening and closing brace
1158
+ // i.e. the subject's current position, and the opener's start_column
1159
+ int fnref_end_column = subj->pos + subj->column_offset + subj->block_offset;
1160
+ int fnref_start_column = opener->inl_text->start_column;
1161
+
1162
+ // any given node delineates a substring of the line being processed,
1163
+ // with the remainder of the line being pointed to thru its 'literal'
1164
+ // struct member.
1165
+ // here, we copy the literal's pointer, moving it past the '^' character
1166
+ // for a length equal to the size of footnote reference text.
1167
+ // i.e. end_col minus start_col, minus the [ and the ^ characters
1168
+ //
1169
+ // this copies the footnote reference string, even if between the
1170
+ // `opener` and the subject's current position there are other nodes
1171
+ //
1172
+ // (first, check for underflows)
1173
+ if ((fnref_start_column + 2) <= fnref_end_column) {
1174
+ fnref->as.literal = cmark_chunk_dup(literal, 1, (fnref_end_column - fnref_start_column) - 2);
1175
+ } else {
1176
+ fnref->as.literal = cmark_chunk_dup(literal, 1, 0);
1177
+ }
1178
+
1179
+ fnref->start_line = fnref->end_line = subj->line;
1180
+ fnref->start_column = fnref_start_column;
1181
+ fnref->end_column = fnref_end_column;
1182
+
1183
+ // we then replace the opener with this new fnref node, the net effect
1184
+ // being replacing the opening '[' text node with a `^footnote-ref]` node.
1185
+ cmark_node_insert_before(opener->inl_text, fnref);
1186
+
1152
1187
  process_emphasis(parser, subj, opener->previous_delimiter);
1188
+ // sometimes, the footnote reference text gets parsed into multiple nodes
1189
+ // i.e. '[^example]' parsed into '[', '^exam', 'ple]'.
1190
+ // this happens for ex with the autolink extension. when the autolinker
1191
+ // finds the 'w' character, it will split the text into multiple nodes
1192
+ // in hopes of being able to match a 'www.' substring.
1193
+ //
1194
+ // because this function is called one character at a time via the
1195
+ // `parse_inlines` function, and the current subj->pos is pointing at the
1196
+ // closing ] brace, and because we copy all the text between the [ ]
1197
+ // braces, we should be able to safely ignore and delete any nodes after
1198
+ // the opener->inl_text->next.
1199
+ //
1200
+ // therefore, here we walk thru the list and free them all up
1201
+ cmark_node *next_node;
1202
+ cmark_node *current_node = opener->inl_text->next;
1203
+ while(current_node) {
1204
+ next_node = current_node->next;
1205
+ cmark_node_free(current_node);
1206
+ current_node = next_node;
1207
+ }
1208
+
1209
+ cmark_node_free(opener->inl_text);
1210
+
1153
1211
  pop_bracket(subj);
1154
1212
  return NULL;
1155
1213
  }
@@ -76,6 +76,13 @@ struct cmark_node {
76
76
 
77
77
  cmark_syntax_extension *extension;
78
78
 
79
+ union {
80
+ int ref_ix;
81
+ int def_count;
82
+ } footnote;
83
+
84
+ cmark_node *parent_footnote_def;
85
+
79
86
  union {
80
87
  cmark_chunk literal;
81
88
  cmark_list list;