Skip to content

Commit

Permalink
Merge pull request #247 from anticomputer/update-to-0.29.0.gfm.13
Browse files Browse the repository at this point in the history
Update to 0.29.0.gfm.13
  • Loading branch information
phillmv committed Jul 31, 2023
2 parents 42cfc90 + e1e450c commit db8cd37
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 54 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [v0.23.10] (2023-07-31)

- Update GFM release to [`0.29.0.gfm.12`](https://github.com/github/cmark-gfm/releases/tag/0.29.0.gfm.12) and [`0.29.0.gfm.13`](https://github.com/github/cmark-gfm/releases/tag/0.29.0.gfm.13), thereby [fixing a polynomial time complexity security vulnerability](https://github.com/github/cmark-gfm/security/advisories/GHSA-w4qg-3vf7-m9x5).
- Of note to users of this library, GFM releases `0.29.0.gfm.12` and `0.29.0.gfm.13` also:
- Normalized marker row vs. delimiter row nomenclature ([#273](https://github.com/github/cmark-gfm/pull/273))
- Exposed CMARK_NODE_FOOTNOTE_DEFINITION literal value ([#336](https://github.com/github/cmark-gfm/pull/336))


## [v0.23.4](https://github.com/gjtorikian/commonmarker/tree/v0.23.4) (2022-03-03)

[Full Changelog](https://github.com/gjtorikian/commonmarker/compare/v0.23.2...v0.23.4)
Expand Down
2 changes: 1 addition & 1 deletion ext/commonmarker/autolink.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
// inline was finished in inlines.c.
}

static bool validate_protocol(char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
static bool validate_protocol(const char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
size_t len = strlen(protocol);

if (len > (max_rewind - rewind)) {
Expand Down
6 changes: 4 additions & 2 deletions ext/commonmarker/blocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1217,15 +1217,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
parser->first_nonspace + 1);
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
} else if (!indented &&
parser->options & CMARK_OPT_FOOTNOTES &&
(parser->options & CMARK_OPT_FOOTNOTES) &&
depth < MAX_LIST_DEPTH &&
(matched = scan_footnote_definition(input, parser->first_nonspace))) {
cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
cmark_chunk_to_cstr(parser->mem, &c);

while (c.data[c.len - 1] != ']')
--c.len;
--c.len;

cmark_chunk_to_cstr(parser->mem, &c);

S_advance_offset(parser, input, parser->first_nonspace + matched - parser->offset, false);
*container = add_child(parser, *container, CMARK_NODE_FOOTNOTE_DEFINITION, parser->first_nonspace + matched + 1);
(*container)->as.literal = c;
Expand Down
2 changes: 1 addition & 1 deletion ext/commonmarker/commonmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
char fencechar[2] = {'\0', '\0'};
size_t info_len, code_len;
char listmarker[LISTMARKER_SIZE];
char *emph_delim;
const char *emph_delim;
bool first_in_list_item;
bufsize_t marker_width;
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
Expand Down
1 change: 1 addition & 0 deletions ext/commonmarker/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
case CMARK_NODE_HTML_INLINE:
case CMARK_NODE_CODE:
case CMARK_NODE_FOOTNOTE_REFERENCE:
case CMARK_NODE_FOOTNOTE_DEFINITION:
return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal);

case CMARK_NODE_CODE_BLOCK:
Expand Down
1 change: 1 addition & 0 deletions ext/commonmarker/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct cmark_node {
cmark_link link;
cmark_custom custom;
int html_block_type;
int cell_index; // For keeping track of TABLE_CELL table alignments
void *opaque;
} as;
};
Expand Down
137 changes: 91 additions & 46 deletions ext/commonmarker/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include "table.h"
#include "cmark-gfm-core-extensions.h"

// Limit to prevent a malicious input from causing a denial of service.
#define MAX_AUTOCOMPLETED_CELLS 0x80000

// Custom node flag, initialized in `create_table_extension`.
static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;

Expand All @@ -31,6 +34,8 @@ typedef struct {
typedef struct {
uint16_t n_columns;
uint8_t *alignments;
int n_rows;
int n_nonempty_cells;
} node_table;

typedef struct {
Expand Down Expand Up @@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
return 1;
}

// Increment the number of rows in the table. Also update n_nonempty_cells,
// which keeps track of the number of cells which were parsed from the
// input file. (If one of the rows is too short, then the trailing cells
// are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
// The purpose of this is to prevent a malicious input from generating a very
// large number of autocompleted cells, which could cause a denial of service
// vulnerability.
static int incr_table_row_count(cmark_node *node, int i) {
if (!node || node->type != CMARK_NODE_TABLE) {
return 0;
}

((node_table *)node->as.opaque)->n_rows++;
((node_table *)node->as.opaque)->n_nonempty_cells += i;
return 1;
}

// Calculate the number of autocompleted cells.
static int get_n_autocompleted_cells(cmark_node *node) {
if (!node || node->type != CMARK_NODE_TABLE) {
return 0;
}

const node_table *nt = (node_table *)node->as.opaque;
return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
}

static uint8_t *get_table_alignments(cmark_node *node) {
if (!node || node->type != CMARK_NODE_TABLE)
return 0;
Expand All @@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
return 1;
}

static uint8_t get_cell_alignment(cmark_node *node) {
if (!node || node->type != CMARK_NODE_TABLE_CELL)
return 0;

const uint8_t *alignments = get_table_alignments(node->parent->parent);
int i = node->as.cell_index;
return alignments[i];
}

static int set_cell_index(cmark_node *node, int i) {
if (!node || node->type != CMARK_NODE_TABLE_CELL)
return 0;

node->as.cell_index = i;
return 1;
}

static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
{
cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
Expand Down Expand Up @@ -257,7 +306,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
unsigned char *input, int len) {
cmark_node *table_header;
table_row *header_row = NULL;
table_row *marker_row = NULL;
table_row *delimiter_row = NULL;
node_table_row *ntr;
const char *parent_string;
uint16_t i;
Expand All @@ -270,16 +319,16 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
return parent_container;
}

// Since scan_table_start was successful, we must have a marker row.
marker_row = row_from_string(self, parser,
input + cmark_parser_get_first_nonspace(parser),
len - cmark_parser_get_first_nonspace(parser));
// Since scan_table_start was successful, we must have a delimiter row.
delimiter_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) {
if (!delimiter_row) {
return parent_container;
}
assert(marker_row);

assert(delimiter_row);

cmark_arena_push();

Expand All @@ -289,31 +338,31 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
parent_string = cmark_node_get_string_content(parent_container);
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
(int)strlen(parent_string));
if (!header_row || header_row->n_columns != marker_row->n_columns) {
free_table_row(parser->mem, marker_row);
if (!header_row || header_row->n_columns != delimiter_row->n_columns) {
free_table_row(parser->mem, delimiter_row);
free_table_row(parser->mem, header_row);
cmark_arena_pop();
parent_container->flags |= CMARK_NODE__TABLE_VISITED;
return parent_container;
}

if (cmark_arena_pop()) {
marker_row = row_from_string(
delimiter_row = row_from_string(
self, parser, input + cmark_parser_get_first_nonspace(parser),
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);
if (!delimiter_row || !header_row || header_row->n_columns != delimiter_row->n_columns) {
free_table_row(parser->mem, delimiter_row);
free_table_row(parser->mem, header_row);
return parent_container;
}
}

if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
free_table_row(parser->mem, header_row);
free_table_row(parser->mem, marker_row);
free_table_row(parser->mem, delimiter_row);
return parent_container;
}

Expand All @@ -326,12 +375,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
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
// allocate alignments based on delimiter_row->n_columns
// since we populate the alignments array based on delimiter_row->cells
uint8_t *alignments =
(uint8_t *)parser->mem->calloc(marker_row->n_columns, sizeof(uint8_t));
for (i = 0; i < marker_row->n_columns; ++i) {
node_cell *node = &marker_row->cells[i];
(uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t));
for (i = 0; i < delimiter_row->n_columns; ++i) {
node_cell *node = &delimiter_row->cells[i];
bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';

if (left && right)
Expand All @@ -353,25 +402,26 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
ntr->is_header = true;

{
for (i = 0; i < header_row->n_columns; ++i) {
node_cell *cell = &header_row->cells[i];
cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
header_cell->start_line = header_cell->end_line = parent_container->start_line;
header_cell->internal_offset = cell->internal_offset;
header_cell->end_column = parent_container->start_column + cell->end_offset;
cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
cmark_node_set_syntax_extension(header_cell, self);
}
for (i = 0; i < header_row->n_columns; ++i) {
node_cell *cell = &header_row->cells[i];
cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
header_cell->start_line = header_cell->end_line = parent_container->start_line;
header_cell->internal_offset = cell->internal_offset;
header_cell->end_column = parent_container->start_column + cell->end_offset;
cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
cmark_node_set_syntax_extension(header_cell, self);
set_cell_index(header_cell, i);
}

incr_table_row_count(parent_container, i);

cmark_parser_advance_offset(
parser, (char *)input,
(int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);

free_table_row(parser->mem, header_row);
free_table_row(parser->mem, marker_row);
free_table_row(parser->mem, delimiter_row);
return parent_container;
}

Expand All @@ -385,6 +435,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
if (cmark_parser_is_blank(parser))
return NULL;

if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
return NULL;
}

table_row_block =
cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
parent_container->start_column);
Expand Down Expand Up @@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
node->end_column = parent_container->start_column + cell->end_offset;
cmark_node_set_string_content(node, (char *) cell->buf->ptr);
cmark_node_set_syntax_extension(node, self);
set_cell_index(node, i);
}

incr_table_row_count(parent_container, i);

for (; i < table_columns; ++i) {
cmark_node *node = cmark_parser_add_child(
parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
cmark_node_set_syntax_extension(node, self);
set_cell_index(node, i);
}
}

Expand Down Expand Up @@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
cmark_node *node) {
if (node->type == CMARK_NODE_TABLE_CELL) {
if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
uint8_t *alignments = get_table_alignments(node->parent->parent);
int i = 0;
cmark_node *n;
for (n = node->parent->first_child; n; n = n->next, ++i)
if (n == node)
break;
switch (alignments[i]) {
switch (get_cell_alignment(node)) {
case 'l': return " align=\"left\"";
case 'c': return " align=\"center\"";
case 'r': return " align=\"right\"";
Expand Down Expand Up @@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
cmark_event_type ev_type, int options) {
bool entering = (ev_type == CMARK_EVENT_ENTER);
cmark_strbuf *html = renderer->html;
cmark_node *n;

// XXX: we just monopolise renderer->opaque.
struct html_table_state *table_state =
Expand Down Expand Up @@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
}
}
} else if (node->type == CMARK_NODE_TABLE_CELL) {
uint8_t *alignments = get_table_alignments(node->parent->parent);
if (entering) {
cmark_html_render_cr(html);
if (table_state->in_table_header) {
Expand All @@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
cmark_strbuf_puts(html, "<td");
}

int i = 0;
for (n = node->parent->first_child; n; n = n->next, ++i)
if (n == node)
break;

switch (alignments[i]) {
switch (get_cell_alignment(node)) {
case 'l': html_table_add_align(html, "left", options); break;
case 'c': html_table_add_align(html, "center", options); break;
case 'r': html_table_add_align(html, "right", options); break;
Expand Down
2 changes: 1 addition & 1 deletion lib/commonmarker/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module CommonMarker
VERSION = "0.23.9"
VERSION = "0.23.10"
end
7 changes: 5 additions & 2 deletions script/update_submodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

set -e
set -euo pipefail

if [ -z "$1" ]; then
BRANCH="main"
Expand All @@ -14,7 +14,10 @@ echo "Checking out cmark-upstream"
echo "---------------------"
cd ext/commonmarker/cmark-upstream
git fetch origin
git checkout $BRANCH && git pull
# when checking out a tag, for whatever reason the git pull step fails
# so we comment it out for now:
# git checkout $BRANCH && git pull
git checkout $BRANCH
sha=`git rev-parse HEAD`
cd ../../..
make
Expand Down

0 comments on commit db8cd37

Please sign in to comment.