Skip to content

Commit db8cd37

Browse files
authored
Merge pull request #247 from anticomputer/update-to-0.29.0.gfm.13
Update to 0.29.0.gfm.13
2 parents 42cfc90 + e1e450c commit db8cd37

File tree

10 files changed

+114
-54
lines changed

10 files changed

+114
-54
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## [v0.23.10] (2023-07-31)
4+
5+
- 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).
6+
- Of note to users of this library, GFM releases `0.29.0.gfm.12` and `0.29.0.gfm.13` also:
7+
- Normalized marker row vs. delimiter row nomenclature ([#273](https://github.com/github/cmark-gfm/pull/273))
8+
- Exposed CMARK_NODE_FOOTNOTE_DEFINITION literal value ([#336](https://github.com/github/cmark-gfm/pull/336))
9+
10+
311
## [v0.23.4](https://github.com/gjtorikian/commonmarker/tree/v0.23.4) (2022-03-03)
412

513
[Full Changelog](https://github.com/gjtorikian/commonmarker/compare/v0.23.2...v0.23.4)

ext/commonmarker/autolink.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
296296
// inline was finished in inlines.c.
297297
}
298298

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) {
300300
size_t len = strlen(protocol);
301301

302302
if (len > (max_rewind - rewind)) {

ext/commonmarker/blocks.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -1217,15 +1217,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
12171217
parser->first_nonspace + 1);
12181218
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
12191219
} else if (!indented &&
1220-
parser->options & CMARK_OPT_FOOTNOTES &&
1220+
(parser->options & CMARK_OPT_FOOTNOTES) &&
1221+
depth < MAX_LIST_DEPTH &&
12211222
(matched = scan_footnote_definition(input, parser->first_nonspace))) {
12221223
cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
1223-
cmark_chunk_to_cstr(parser->mem, &c);
12241224

12251225
while (c.data[c.len - 1] != ']')
12261226
--c.len;
12271227
--c.len;
12281228

1229+
cmark_chunk_to_cstr(parser->mem, &c);
1230+
12291231
S_advance_offset(parser, input, parser->first_nonspace + matched - parser->offset, false);
12301232
*container = add_child(parser, *container, CMARK_NODE_FOOTNOTE_DEFINITION, parser->first_nonspace + matched + 1);
12311233
(*container)->as.literal = c;

ext/commonmarker/commonmark.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
165165
char fencechar[2] = {'\0', '\0'};
166166
size_t info_len, code_len;
167167
char listmarker[LISTMARKER_SIZE];
168-
char *emph_delim;
168+
const char *emph_delim;
169169
bool first_in_list_item;
170170
bufsize_t marker_width;
171171
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&

ext/commonmarker/node.c

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
377377
case CMARK_NODE_HTML_INLINE:
378378
case CMARK_NODE_CODE:
379379
case CMARK_NODE_FOOTNOTE_REFERENCE:
380+
case CMARK_NODE_FOOTNOTE_DEFINITION:
380381
return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal);
381382

382383
case CMARK_NODE_CODE_BLOCK:

ext/commonmarker/node.h

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct cmark_node {
105105
cmark_link link;
106106
cmark_custom custom;
107107
int html_block_type;
108+
int cell_index; // For keeping track of TABLE_CELL table alignments
108109
void *opaque;
109110
} as;
110111
};

ext/commonmarker/table.c

+91-46
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include "table.h"
1212
#include "cmark-gfm-core-extensions.h"
1313

14+
// Limit to prevent a malicious input from causing a denial of service.
15+
#define MAX_AUTOCOMPLETED_CELLS 0x80000
16+
1417
// Custom node flag, initialized in `create_table_extension`.
1518
static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
1619

@@ -31,6 +34,8 @@ typedef struct {
3134
typedef struct {
3235
uint16_t n_columns;
3336
uint8_t *alignments;
37+
int n_rows;
38+
int n_nonempty_cells;
3439
} node_table;
3540

3641
typedef struct {
@@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
8388
return 1;
8489
}
8590

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+
86118
static uint8_t *get_table_alignments(cmark_node *node) {
87119
if (!node || node->type != CMARK_NODE_TABLE)
88120
return 0;
@@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
98130
return 1;
99131
}
100132

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+
101150
static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
102151
{
103152
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,
257306
unsigned char *input, int len) {
258307
cmark_node *table_header;
259308
table_row *header_row = NULL;
260-
table_row *marker_row = NULL;
309+
table_row *delimiter_row = NULL;
261310
node_table_row *ntr;
262311
const char *parent_string;
263312
uint16_t i;
@@ -270,16 +319,16 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
270319
return parent_container;
271320
}
272321

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));
277326
// assert may be optimized out, don't rely on it for security boundaries
278-
if (!marker_row) {
327+
if (!delimiter_row) {
279328
return parent_container;
280329
}
281-
282-
assert(marker_row);
330+
331+
assert(delimiter_row);
283332

284333
cmark_arena_push();
285334

@@ -289,31 +338,31 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
289338
parent_string = cmark_node_get_string_content(parent_container);
290339
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
291340
(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);
294343
free_table_row(parser->mem, header_row);
295344
cmark_arena_pop();
296345
parent_container->flags |= CMARK_NODE__TABLE_VISITED;
297346
return parent_container;
298347
}
299348

300349
if (cmark_arena_pop()) {
301-
marker_row = row_from_string(
350+
delimiter_row = row_from_string(
302351
self, parser, input + cmark_parser_get_first_nonspace(parser),
303352
len - cmark_parser_get_first_nonspace(parser));
304353
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
305354
(int)strlen(parent_string));
306355
// 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);
309358
free_table_row(parser->mem, header_row);
310359
return parent_container;
311360
}
312361
}
313362

314363
if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
315364
free_table_row(parser->mem, header_row);
316-
free_table_row(parser->mem, marker_row);
365+
free_table_row(parser->mem, delimiter_row);
317366
return parent_container;
318367
}
319368

@@ -326,12 +375,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
326375
parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
327376
set_n_table_columns(parent_container, header_row->n_columns);
328377

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
331380
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];
335384
bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
336385

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

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);
367415
}
368416

417+
incr_table_row_count(parent_container, i);
418+
369419
cmark_parser_advance_offset(
370420
parser, (char *)input,
371421
(int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
372422

373423
free_table_row(parser->mem, header_row);
374-
free_table_row(parser->mem, marker_row);
424+
free_table_row(parser->mem, delimiter_row);
375425
return parent_container;
376426
}
377427

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

438+
if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
439+
return NULL;
440+
}
441+
388442
table_row_block =
389443
cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
390444
parent_container->start_column);
@@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
412466
node->end_column = parent_container->start_column + cell->end_offset;
413467
cmark_node_set_string_content(node, (char *) cell->buf->ptr);
414468
cmark_node_set_syntax_extension(node, self);
469+
set_cell_index(node, i);
415470
}
416471

472+
incr_table_row_count(parent_container, i);
473+
417474
for (; i < table_columns; ++i) {
418475
cmark_node *node = cmark_parser_add_child(
419476
parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
420477
cmark_node_set_syntax_extension(node, self);
478+
set_cell_index(node, i);
421479
}
422480
}
423481

@@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
602660
cmark_node *node) {
603661
if (node->type == CMARK_NODE_TABLE_CELL) {
604662
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)) {
612664
case 'l': return " align=\"left\"";
613665
case 'c': return " align=\"center\"";
614666
case 'r': return " align=\"right\"";
@@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
696748
cmark_event_type ev_type, int options) {
697749
bool entering = (ev_type == CMARK_EVENT_ENTER);
698750
cmark_strbuf *html = renderer->html;
699-
cmark_node *n;
700751

701752
// XXX: we just monopolise renderer->opaque.
702753
struct html_table_state *table_state =
@@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
745796
}
746797
}
747798
} else if (node->type == CMARK_NODE_TABLE_CELL) {
748-
uint8_t *alignments = get_table_alignments(node->parent->parent);
749799
if (entering) {
750800
cmark_html_render_cr(html);
751801
if (table_state->in_table_header) {
@@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
754804
cmark_strbuf_puts(html, "<td");
755805
}
756806

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)) {
763808
case 'l': html_table_add_align(html, "left", options); break;
764809
case 'c': html_table_add_align(html, "center", options); break;
765810
case 'r': html_table_add_align(html, "right", options); break;

lib/commonmarker/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module CommonMarker
4-
VERSION = "0.23.9"
4+
VERSION = "0.23.10"
55
end

script/update_submodules

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22

3-
set -e
3+
set -euo pipefail
44

55
if [ -z "$1" ]; then
66
BRANCH="main"
@@ -14,7 +14,10 @@ echo "Checking out cmark-upstream"
1414
echo "---------------------"
1515
cd ext/commonmarker/cmark-upstream
1616
git fetch origin
17-
git checkout $BRANCH && git pull
17+
# when checking out a tag, for whatever reason the git pull step fails
18+
# so we comment it out for now:
19+
# git checkout $BRANCH && git pull
20+
git checkout $BRANCH
1821
sha=`git rev-parse HEAD`
1922
cd ../../..
2023
make

0 commit comments

Comments
 (0)