Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package ghc-cmark-gfm for openSUSE:Factory checked in at 2022-08-01 21:28:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-cmark-gfm (Old) and /work/SRC/openSUSE:Factory/.ghc-cmark-gfm.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "ghc-cmark-gfm" Mon Aug 1 21:28:35 2022 rev:12 rq:985804 version:0.2.3 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-cmark-gfm/ghc-cmark-gfm.changes 2020-12-22 11:37:30.277382065 +0100 +++ /work/SRC/openSUSE:Factory/.ghc-cmark-gfm.new.1533/ghc-cmark-gfm.changes 2022-08-01 21:28:36.541348353 +0200 @@ -1,0 +2,8 @@ +Wed Mar 9 03:24:24 UTC 2022 - Peter Simons <psimons@suse.com> + +- Update cmark-gfm to version 0.2.3. + cmark-gfm 0.2.3 (9 Mar 2022) + + * Pull in upstream changes. + +------------------------------------------------------------------- Old: ---- cmark-gfm-0.2.2.tar.gz New: ---- cmark-gfm-0.2.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-cmark-gfm.spec ++++++ --- /var/tmp/diff_new_pack.e2GCBO/_old 2022-08-01 21:28:37.093349937 +0200 +++ /var/tmp/diff_new_pack.e2GCBO/_new 2022-08-01 21:28:37.101349960 +0200 @@ -1,7 +1,7 @@ # # spec file for package ghc-cmark-gfm # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %global pkg_name cmark-gfm %bcond_with tests Name: ghc-%{pkg_name} -Version: 0.2.2 +Version: 0.2.3 Release: 0 Summary: Fast, accurate GitHub Flavored Markdown parser and renderer License: BSD-3-Clause @@ -38,7 +38,7 @@ This package provides Haskell bindings for <https://github.com/github/cmark-gfm libcmark-gfm>, the reference parser for <https://github.github.com/gfm/ GitHub Flavored Markdown>, a fully specified variant of Markdown. It includes sources -for libcmark-gfm (0.29.0.gfm.0) and does not require prior installation of the +for libcmark-gfm (0.29.0.gfm.3) and does not require prior installation of the C library. %package devel ++++++ cmark-gfm-0.2.2.tar.gz -> cmark-gfm-0.2.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/blocks.c new/cmark-gfm-0.2.3/cbits/blocks.c --- old/cmark-gfm-0.2.2/cbits/blocks.c 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/blocks.c 2022-03-09 04:23:19.000000000 +0100 @@ -468,7 +468,6 @@ while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); if (ev_type == CMARK_EVENT_EXIT && cur->type == CMARK_NODE_FOOTNOTE_DEFINITION) { - cmark_node_unlink(cur); cmark_footnote_create(map, cur); } } @@ -485,6 +484,15 @@ if (!footnote->ix) footnote->ix = ++ix; + // store a reference to this footnote reference's footnote definition + // this is used by renderers when generating label ids + cur->parent_footnote_def = footnote->node; + + // keep track of a) count of how many times this footnote def has been + // referenced, and b) which reference index this footnote ref is at. + // this is used by renderers when generating links and backreferences. + cur->footnote.ref_ix = ++footnote->node->footnote.def_count; + char n[32]; snprintf(n, sizeof(n), "%d", footnote->ix); cmark_chunk_free(parser->mem, &cur->as.literal); @@ -515,13 +523,16 @@ qsort(map->sorted, map->size, sizeof(cmark_map_entry *), sort_footnote_by_ix); for (unsigned int i = 0; i < map->size; ++i) { cmark_footnote *footnote = (cmark_footnote *)map->sorted[i]; - if (!footnote->ix) + if (!footnote->ix) { + cmark_node_unlink(footnote->node); continue; + } cmark_node_append_child(parser->root, footnote->node); footnote->node = NULL; } } + cmark_unlink_footnotes_map(map); cmark_map_free(map); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/commonmark.c new/cmark-gfm-0.2.3/cbits/commonmark.c --- old/cmark-gfm-0.2.2/cbits/commonmark.c 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/commonmark.c 2022-03-09 04:23:19.000000000 +0100 @@ -477,7 +477,13 @@ case CMARK_NODE_FOOTNOTE_REFERENCE: if (entering) { LIT("[^"); - OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL); + + char *footnote_label = renderer->mem->calloc(node->parent_footnote_def->as.literal.len + 1, sizeof(char)); + memmove(footnote_label, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len); + + OUT(footnote_label, false, LITERAL); + renderer->mem->free(footnote_label); + LIT("]"); } break; @@ -486,9 +492,13 @@ if (entering) { renderer->footnote_ix += 1; LIT("[^"); - char n[32]; - snprintf(n, sizeof(n), "%d", renderer->footnote_ix); - OUT(n, false, LITERAL); + + char *footnote_label = renderer->mem->calloc(node->as.literal.len + 1, sizeof(char)); + memmove(footnote_label, node->as.literal.data, node->as.literal.len); + + OUT(footnote_label, false, LITERAL); + renderer->mem->free(footnote_label); + LIT("]:\n"); cmark_strbuf_puts(renderer->prefix, " "); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/footnotes.c new/cmark-gfm-0.2.3/cbits/footnotes.c --- old/cmark-gfm-0.2.2/cbits/footnotes.c 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/footnotes.c 2022-03-09 04:23:19.000000000 +0100 @@ -38,3 +38,26 @@ cmark_map *cmark_footnote_map_new(cmark_mem *mem) { return cmark_map_new(mem, footnote_free); } + +// Before calling `cmark_map_free` on a map with `cmark_footnotes`, first +// unlink all of the footnote nodes before freeing their memory. +// +// Sometimes, two (unused) footnote nodes can end up referencing each other, +// which as they get freed up by calling `cmark_map_free` -> `footnote_free` -> +// etc, can lead to a use-after-free error. +// +// Better to `unlink` every footnote node first, setting their next, prev, and +// parent pointers to NULL, and only then walk thru & free them up. +void cmark_unlink_footnotes_map(cmark_map *map) { + cmark_map_entry *ref; + cmark_map_entry *next; + + ref = map->refs; + while(ref) { + next = ref->next; + if (((cmark_footnote *)ref)->node) { + cmark_node_unlink(((cmark_footnote *)ref)->node); + } + ref = next; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/footnotes.h new/cmark-gfm-0.2.3/cbits/footnotes.h --- old/cmark-gfm-0.2.2/cbits/footnotes.h 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/footnotes.h 2022-03-09 04:23:19.000000000 +0100 @@ -18,6 +18,8 @@ void cmark_footnote_create(cmark_map *map, cmark_node *node); cmark_map *cmark_footnote_map_new(cmark_mem *mem); +void cmark_unlink_footnotes_map(cmark_map *map); + #ifdef __cplusplus } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/html.c new/cmark-gfm-0.2.3/cbits/html.c --- old/cmark-gfm-0.2.2/cbits/html.c 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/html.c 2022-03-09 04:23:19.000000000 +0100 @@ -59,16 +59,30 @@ cmark_strbuf_put(html, data, (bufsize_t)len); } -static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html) { +static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *html, cmark_node *node) { if (renderer->written_footnote_ix >= renderer->footnote_ix) return false; renderer->written_footnote_ix = renderer->footnote_ix; - cmark_strbuf_puts(html, "<a href=\"#fnref"); - char n[32]; - snprintf(n, sizeof(n), "%d", renderer->footnote_ix); - cmark_strbuf_puts(html, n); - cmark_strbuf_puts(html, "\" class=\"footnote-backref\">���</a>"); + cmark_strbuf_puts(html, "<a href=\"#fnref-"); + houdini_escape_href(html, node->as.literal.data, node->as.literal.len); + cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">���</a>"); + + if (node->footnote.def_count > 1) + { + for(int i = 2; i <= node->footnote.def_count; i++) { + char n[32]; + snprintf(n, sizeof(n), "%d", i); + + cmark_strbuf_puts(html, " <a href=\"#fnref-"); + houdini_escape_href(html, node->as.literal.data, node->as.literal.len); + cmark_strbuf_puts(html, "-"); + cmark_strbuf_puts(html, n); + cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">���<sup class=\"footnote-ref\">"); + cmark_strbuf_puts(html, n); + cmark_strbuf_puts(html, "</sup></a>"); + } + } return true; } @@ -273,7 +287,7 @@ } else { if (parent->type == CMARK_NODE_FOOTNOTE_DEFINITION && node->next == NULL) { cmark_strbuf_putc(html, ' '); - S_put_footnote_backref(renderer, html); + S_put_footnote_backref(renderer, html, parent); } cmark_strbuf_puts(html, "</p>\n"); } @@ -392,16 +406,15 @@ case CMARK_NODE_FOOTNOTE_DEFINITION: if (entering) { if (renderer->footnote_ix == 0) { - cmark_strbuf_puts(html, "<section class=\"footnotes\">\n<ol>\n"); + cmark_strbuf_puts(html, "<section class=\"footnotes\" data-footnotes>\n<ol>\n"); } ++renderer->footnote_ix; - cmark_strbuf_puts(html, "<li id=\"fn"); - char n[32]; - snprintf(n, sizeof(n), "%d", renderer->footnote_ix); - cmark_strbuf_puts(html, n); + + cmark_strbuf_puts(html, "<li id=\"fn-"); + houdini_escape_href(html, node->as.literal.data, node->as.literal.len); cmark_strbuf_puts(html, "\">\n"); } else { - if (S_put_footnote_backref(renderer, html)) { + if (S_put_footnote_backref(renderer, html, node)) { cmark_strbuf_putc(html, '\n'); } cmark_strbuf_puts(html, "</li>\n"); @@ -410,12 +423,20 @@ case CMARK_NODE_FOOTNOTE_REFERENCE: if (entering) { - cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn"); - cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len); - cmark_strbuf_puts(html, "\" id=\"fnref"); - cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len); - cmark_strbuf_puts(html, "\">"); - cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len); + cmark_strbuf_puts(html, "<sup class=\"footnote-ref\"><a href=\"#fn-"); + houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len); + cmark_strbuf_puts(html, "\" id=\"fnref-"); + houdini_escape_href(html, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len); + + if (node->footnote.ref_ix > 1) { + char n[32]; + snprintf(n, sizeof(n), "%d", node->footnote.ref_ix); + cmark_strbuf_puts(html, "-"); + cmark_strbuf_puts(html, n); + } + + cmark_strbuf_puts(html, "\" data-footnote-ref>"); + houdini_escape_href(html, node->as.literal.data, node->as.literal.len); cmark_strbuf_puts(html, "</a></sup>"); } break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/inlines.c new/cmark-gfm-0.2.3/cbits/inlines.c --- old/cmark-gfm-0.2.2/cbits/inlines.c 2020-09-16 03:41:24.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/inlines.c 2022-03-09 04:23:19.000000000 +0100 @@ -1137,19 +1137,77 @@ // What if we're a footnote link? if (parser->options & CMARK_OPT_FOOTNOTES && opener->inl_text->next && - opener->inl_text->next->type == CMARK_NODE_TEXT && - !opener->inl_text->next->next) { + opener->inl_text->next->type == CMARK_NODE_TEXT) { + cmark_chunk *literal = &opener->inl_text->next->as.literal; - if (literal->len > 1 && literal->data[0] == '^') { - inl = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE); - inl->as.literal = cmark_chunk_dup(literal, 1, literal->len - 1); - inl->start_line = inl->end_line = subj->line; - inl->start_column = opener->inl_text->start_column; - inl->end_column = subj->pos + subj->column_offset + subj->block_offset; - cmark_node_insert_before(opener->inl_text, inl); - cmark_node_free(opener->inl_text->next); - cmark_node_free(opener->inl_text); + + // look back to the opening '[', and skip ahead to the next character + // if we're looking at a '[^' sequence, and there is other text or nodes + // after the ^, let's call it a footnote reference. + if ((literal->len > 0 && literal->data[0] == '^') && (literal->len > 1 || opener->inl_text->next->next)) { + + // Before we got this far, the `handle_close_bracket` function may have + // advanced the current state beyond our footnote's actual closing + // bracket, ie if it went looking for a `link_label`. + // Let's just rewind the subject's position: + subj->pos = initial_pos; + + cmark_node *fnref = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE); + + // the start and end of the footnote ref is the opening and closing brace + // i.e. the subject's current position, and the opener's start_column + int fnref_end_column = subj->pos + subj->column_offset + subj->block_offset; + int fnref_start_column = opener->inl_text->start_column; + + // any given node delineates a substring of the line being processed, + // with the remainder of the line being pointed to thru its 'literal' + // struct member. + // here, we copy the literal's pointer, moving it past the '^' character + // for a length equal to the size of footnote reference text. + // i.e. end_col minus start_col, minus the [ and the ^ characters + // + // this copies the footnote reference string, even if between the + // `opener` and the subject's current position there are other nodes + // + // (first, check for underflows) + if ((fnref_start_column + 2) <= fnref_end_column) { + fnref->as.literal = cmark_chunk_dup(literal, 1, (fnref_end_column - fnref_start_column) - 2); + } else { + fnref->as.literal = cmark_chunk_dup(literal, 1, 0); + } + + fnref->start_line = fnref->end_line = subj->line; + fnref->start_column = fnref_start_column; + fnref->end_column = fnref_end_column; + + // we then replace the opener with this new fnref node, the net effect + // being replacing the opening '[' text node with a `^footnote-ref]` node. + cmark_node_insert_before(opener->inl_text, fnref); + process_emphasis(parser, subj, opener->previous_delimiter); + // sometimes, the footnote reference text gets parsed into multiple nodes + // i.e. '[^example]' parsed into '[', '^exam', 'ple]'. + // this happens for ex with the autolink extension. when the autolinker + // finds the 'w' character, it will split the text into multiple nodes + // in hopes of being able to match a 'www.' substring. + // + // because this function is called one character at a time via the + // `parse_inlines` function, and the current subj->pos is pointing at the + // closing ] brace, and because we copy all the text between the [ ] + // braces, we should be able to safely ignore and delete any nodes after + // the opener->inl_text->next. + // + // therefore, here we walk thru the list and free them all up + cmark_node *next_node; + cmark_node *current_node = opener->inl_text->next; + while(current_node) { + next_node = current_node->next; + cmark_node_free(current_node); + current_node = next_node; + } + + cmark_node_free(opener->inl_text); + pop_bracket(subj); return NULL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/node.h new/cmark-gfm-0.2.3/cbits/node.h --- old/cmark-gfm-0.2.2/cbits/node.h 2020-09-16 03:55:07.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/node.h 2022-03-09 04:23:19.000000000 +0100 @@ -77,6 +77,13 @@ cmark_syntax_extension *extension; union { + int ref_ix; + int def_count; + } footnote; + + cmark_node *parent_footnote_def; + + union { cmark_chunk literal; cmark_list list; cmark_code code; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cbits/table.c new/cmark-gfm-0.2.3/cbits/table.c --- old/cmark-gfm-0.2.2/cbits/table.c 2020-09-16 03:55:07.000000000 +0200 +++ new/cmark-gfm-0.2.3/cbits/table.c 2022-03-09 04:23:19.000000000 +0100 @@ -129,6 +129,7 @@ bufsize_t cell_matched = 1, pipe_matched = 1, offset; int expect_more_cells = 1; int row_end_offset = 0; + int int_overflow_abort = 0; row = (table_row *)parser->mem->calloc(1, sizeof(table_row)); row->n_columns = 0; @@ -161,6 +162,12 @@ ++cell->internal_offset; } + // make sure we never wrap row->n_columns + // offset will != len and our exit will clean up as intended + if (row->n_columns == UINT16_MAX) { + int_overflow_abort = 1; + break; + } row->n_columns += 1; row->cells = cmark_llist_append(parser->mem, row->cells, cell); } @@ -194,7 +201,7 @@ } } - if (offset != len || row->n_columns == 0) { + if (offset != len || row->n_columns == 0 || int_overflow_abort) { free_table_row(parser->mem, row); row = NULL; } @@ -241,6 +248,11 @@ marker_row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser), len - cmark_parser_get_first_nonspace(parser)); + // assert may be optimized out, don't rely on it for security boundaries + if (!marker_row) { + return parent_container; + } + assert(marker_row); cmark_arena_push(); @@ -264,6 +276,12 @@ len - cmark_parser_get_first_nonspace(parser)); header_row = row_from_string(self, parser, (unsigned char *)parent_string, (int)strlen(parent_string)); + // row_from_string can return NULL, add additional check to ensure n_columns match + if (!marker_row || !header_row || header_row->n_columns != marker_row->n_columns) { + free_table_row(parser->mem, marker_row); + free_table_row(parser->mem, header_row); + return parent_container; + } } if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) { @@ -281,8 +299,10 @@ parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table)); set_n_table_columns(parent_container, header_row->n_columns); + // allocate alignments based on marker_row->n_columns + // since we populate the alignments array based on marker_row->cells uint8_t *alignments = - (uint8_t *)parser->mem->calloc(header_row->n_columns, sizeof(uint8_t)); + (uint8_t *)parser->mem->calloc(marker_row->n_columns, sizeof(uint8_t)); cmark_llist *it = marker_row->cells; for (i = 0; it; it = it->next, ++i) { node_cell *node = (node_cell *)it->data; @@ -351,6 +371,12 @@ row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser), len - cmark_parser_get_first_nonspace(parser)); + if (!row) { + // clean up the dangling node + cmark_node_free(table_row_block); + return NULL; + } + { cmark_llist *tmp; int i, table_columns = get_n_table_columns(parent_container); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/changelog new/cmark-gfm-0.2.3/changelog --- old/cmark-gfm-0.2.2/changelog 2020-09-16 03:56:44.000000000 +0200 +++ new/cmark-gfm-0.2.3/changelog 2022-03-09 04:23:25.000000000 +0100 @@ -1,3 +1,7 @@ +cmark-gfm 0.2.3 (9 Mar 2022) + + * Pull in upstream changes. + cmark-gfm 0.2.2 (16 Sep 2020) * Pull in upstream changes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cmark-gfm-0.2.2/cmark-gfm.cabal new/cmark-gfm-0.2.3/cmark-gfm.cabal --- old/cmark-gfm-0.2.2/cmark-gfm.cabal 2020-09-16 03:55:16.000000000 +0200 +++ new/cmark-gfm-0.2.3/cmark-gfm.cabal 2022-03-09 04:23:43.000000000 +0100 @@ -1,12 +1,12 @@ name: cmark-gfm -version: 0.2.2 +version: 0.2.3 synopsis: Fast, accurate GitHub Flavored Markdown parser and renderer description: This package provides Haskell bindings for <https://github.com/github/cmark-gfm libcmark-gfm>, the reference parser for <https://github.github.com/gfm/ GitHub Flavored Markdown>, a fully specified variant of Markdown. It includes sources for - libcmark-gfm (0.29.0.gfm.0) and does not require prior installation of the + libcmark-gfm (0.29.0.gfm.3) and does not require prior installation of the C library. homepage: https://github.com/kivikakk/cmark-gfm-hs @@ -69,7 +69,7 @@ library exposed-modules: CMarkGFM build-depends: base >=4.5 && < 5.0, - text >= 1.0 && < 1.3, + text >= 1.0 && < 2.1, bytestring if impl(ghc < 7.6) build-depends: ghc-prim >= 0.2