Merge branch 'master' into match-closing-fence-type-and-length

This commit is contained in:
Matt Rogers
2021-07-01 13:07:11 -05:00
committed by GitHub
12 changed files with 225 additions and 48 deletions

View File

@@ -18,7 +18,8 @@ rvm:
- 2.3
- 2.4
- 2.5
- rbx-3.100
- 2.6
- 2.7
- ruby-head
matrix:

View File

@@ -1,11 +1,39 @@
# Changelog
* Match fence char and length when matching closing fence in fenced code blocks.
Fixes #208.
*Martin Cizek, Orchitech*
* Consider `<center>` as a block-level element.
Refs #702.
*momijizukamori*
* Properly provide a third argument to the `table_cell` callback indicating
whether the current cell is part of the header or not.
The previous implementation with two parameters is still supported.
Fixes #604, Refs #605.
*Mark Lambley*
* Fix anchor generation on titles with ampersands.
Fixes #696.
## Version 3.5.1 (Security)
* Fix a security vulnerability using `:quote` in combination with the
`:escape_html` option.
Reported by *Johan Smits*.
## Version 3.5.0
* Avoid mutating the options hash passed to a render object.

View File

@@ -2,8 +2,8 @@ Redcarpet is written with sugar, spice and everything nice
============================================================
[![Build Status](https://travis-ci.org/vmg/redcarpet.svg?branch=master)](https://travis-ci.org/vmg/redcarpet)
[![Dependency Status](https://www.versioneye.com/ruby/redcarpet/badge.svg)](https://www.versioneye.com/ruby/redcarpet)
[![Help Contribute to Open Source](https://www.codetriage.com/vmg/redcarpet/badges/users.svg)](https://www.codetriage.com/vmg/redcarpet)
[![Gem Version](https://badge.fury.io/rb/redcarpet.svg)](https://badge.fury.io/rb/redcarpet)
Redcarpet is a Ruby library for Markdown processing that smells like
butterflies and popcorn.
@@ -258,7 +258,7 @@ end
* paragraph(text)
* table(header, body)
* table_row(content)
* table_cell(content, alignment)
* table_cell(content, alignment, header)
### Span-level calls

View File

@@ -255,8 +255,15 @@ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
if (!text || !text->size)
return 0;
struct html_renderopt *options = opaque;
BUFPUTSL(ob, "<q>");
bufput(ob, text->data, text->size);
if (options->flags & HTML_ESCAPE)
escape_html(ob, text->data, text->size);
else
bufput(ob, text->data, text->size);
BUFPUTSL(ob, "</q>");
return 1;
@@ -270,6 +277,22 @@ rndr_linebreak(struct buf *ob, void *opaque)
return 1;
}
static int html_entity_ahead(const uint8_t *text, size_t start, size_t size) {
size_t i = start;
if (text[i] != '&')
return 0;
for (; i < size; ++i) {
if (text[i] == ' ')
return 0;
else if (text[i] == ';')
return 1;
}
return 0;
}
static void
rndr_header_anchor(struct buf *out, const struct buf *anchor)
{
@@ -286,10 +309,11 @@ rndr_header_anchor(struct buf *out, const struct buf *anchor)
while (i < size && a[i] != '>')
i++;
// skip html entities
} else if (a[i] == '&') {
} else if (a[i] == '&' && html_entity_ahead(a, i, size)) {
while (i < size && a[i] != ';')
i++;
}
// replace non-ascii or invalid characters with dashes
else if (!isascii(a[i]) || strchr(STRIPPED, a[i])) {
if (inserted && !stripped)

View File

@@ -0,0 +1,44 @@
abbr
address
article
aside
audio
blockquote
canvas
center
dd
del
details
div
dl
fieldset
figcaption
figure
footer
form
h1
h2
h3
h4
h5
h6
header
hgroup
hr
iframe
ins
math
nav
noscript
ol
output
p
pre
script
section
style
summary
table
tfoot
ul
video

View File

@@ -1,6 +1,5 @@
/* C code produced by gperf version 3.0.3 */
/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt */
/* See https://git.io/vPLqa for the list of recognized elements */
/* Computed positions: -k'1-2' */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
@@ -27,7 +26,7 @@
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
#endif
/* maximum key range = 72, duplicates = 0 */
@@ -60,10 +59,7 @@ static unsigned char gperf_downcase[256] =
#ifndef GPERF_CASE_STRNCMP
#define GPERF_CASE_STRNCMP 1
static int
gperf_case_strncmp (s1, s2, n)
register const char *s1;
register const char *s2;
register unsigned int n;
gperf_case_strncmp (register const char *s1, register const char *s2, register size_t n)
{
for (; n > 0;)
{
@@ -88,9 +84,7 @@ inline
#endif
#endif
static unsigned int
hash_block_tag (str, len)
register const char *str;
register unsigned int len;
hash_block_tag (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
@@ -99,13 +93,13 @@ hash_block_tag (str, len)
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
26, 60, 55, 45, 40, 35, 73, 73, 73, 73,
73, 73, 73, 73, 73, 20, 15, 15, 0, 35,
0, 25, 10, 10, 5, 73, 73, 0, 15, 15,
16, 55, 50, 45, 40, 5, 73, 73, 73, 73,
73, 73, 73, 73, 73, 20, 15, 25, 0, 45,
0, 30, 10, 0, 5, 73, 73, 0, 15, 35,
0, 73, 73, 15, 20, 10, 10, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 20, 15, 15,
0, 35, 0, 25, 10, 10, 5, 73, 73, 0,
15, 15, 0, 73, 73, 15, 20, 10, 10, 73,
73, 73, 73, 73, 73, 73, 73, 20, 15, 25,
0, 45, 0, 30, 10, 0, 5, 73, 73, 0,
15, 35, 0, 73, 73, 15, 20, 10, 10, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
@@ -136,13 +130,11 @@ hash_block_tag (str, len)
}
const char *
find_block_tag (str, len)
register const char *str;
register unsigned int len;
find_block_tag (register const char *str, register size_t len)
{
enum
{
TOTAL_KEYWORDS = 43,
TOTAL_KEYWORDS = 44,
MIN_WORD_LENGTH = 1,
MAX_WORD_LENGTH = 10,
MIN_HASH_VALUE = 1,
@@ -167,7 +159,7 @@ find_block_tag (str, len)
"",
"figcaption",
"header",
"ol",
"h6",
"pre",
"math",
"video",
@@ -178,42 +170,45 @@ find_block_tag (str, len)
"blockquote",
"hgroup",
"hr",
"ins",
"h1",
"",
"style",
"output",
"center",
"summary",
"nav",
"",
"audio",
"canvas",
"dd",
"h1",
"abbr",
"table",
"iframe",
"ol",
"ins",
"",
"table",
"",
"article",
"", "",
"aside",
"canvas",
"dd",
"",
"h6",
"abbr",
"",
"output",
"h5",
"", "",
"tfoot",
"",
"h5",
"", "", "", "",
"h4",
"", "", "", "",
"address",
"", "", "", "",
"h3",
"", "", "", "",
"h2"
"h2",
"", "", "", "",
"address"
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
unsigned int key = hash_block_tag (str, len);
register unsigned int key = hash_block_tag (str, len);
if (key <= MAX_HASH_VALUE)
{

View File

@@ -113,9 +113,10 @@ rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)
static void
rndr_tablecell(struct buf *ob, const struct buf *text, int align, void *opaque)
{
VALUE rb_align;
VALUE rb_align, rb_header;
VALUE rb_callback, rb_callback_arity;
switch (align) {
switch (align & MKD_TABLE_ALIGNMASK) {
case MKD_TABLE_ALIGN_L:
rb_align = CSTR2SYM("left");
break;
@@ -133,7 +134,25 @@ rndr_tablecell(struct buf *ob, const struct buf *text, int align, void *opaque)
break;
}
BLOCK_CALLBACK("table_cell", 2, buf2str(text), rb_align);
if (align & MKD_TABLE_HEADER) {
rb_header = Qtrue;
} else {
rb_header = Qfalse;
}
struct redcarpet_renderopt *opt = opaque;
rb_callback = rb_funcall(opt->self, rb_intern("method"), 1, CSTR2SYM("table_cell"));
rb_callback_arity = rb_funcall(rb_callback, rb_intern("arity"), 0);
/* For backward compatibility, let's ensure that the erasure with
only two parameters is still supported. */
if (FIX2INT(rb_callback_arity) == 3) {
BLOCK_CALLBACK("table_cell", 3, buf2str(text), rb_align, rb_header);
} else {
BLOCK_CALLBACK("table_cell", 2, buf2str(text), rb_align);
}
}
static void

View File

@@ -2,7 +2,7 @@ require 'redcarpet.so'
require 'redcarpet/compat'
module Redcarpet
VERSION = '3.5.0'
VERSION = '3.5.1'
class Markdown
attr_reader :renderer

View File

@@ -1,10 +1,10 @@
# encoding: utf-8
Gem::Specification.new do |s|
s.name = 'redcarpet'
s.version = '3.5.0'
s.version = '3.5.1'
s.summary = "Markdown that smells nice"
s.description = 'A fast, safe and extensible Markdown to (X)HTML parser'
s.date = '2019-07-29'
s.date = '2020-12-15'
s.email = 'vicent@github.com'
s.homepage = 'http://github.com/vmg/redcarpet'
s.authors = ["Natacha Porté", "Vicent Martí"]

View File

@@ -64,4 +64,43 @@ class CustomRenderTest < Redcarpet::TestCase
assert_equal "", parser.render(%(a "quote"))
end
def test_table_cell_callback_having_either_two_or_three_args
two = Class.new(Redcarpet::Render::HTML) do
def table_cell(content, alignment)
%(<td>#{content}</td>)
end
end
three = Class.new(Redcarpet::Render::HTML) do
def table_cell(content, alignment, header)
tag = header ? "th" : "td"
%(<#{tag}>#{content}</#{tag}>)
end
end
markdown = <<-Md.chomp.strip_heredoc
| A | B |
|---|---|
| C | D |
Md
first_parser = Redcarpet::Markdown.new(two.new, tables: true)
second_parser = Redcarpet::Markdown.new(three.new, tables: true)
first_output = first_parser.render(markdown)
second_output = second_parser.render(markdown)
assert { first_output.include?("<td>A</td>") }
assert { first_output.include?("<td>B</td>") }
assert { second_output.include?("<th>A</th>") }
assert { second_output.include?("<th>B</th>") }
[first_output, second_output].each do |output|
assert { output.include?("<td>C</td>") }
assert { output.include?("<td>D</td>") }
end
end
end

View File

@@ -66,14 +66,17 @@ class HTMLTOCRenderTest < Redcarpet::TestCase
end
def test_anchor_generation_with_edge_cases
# Imported from ActiveSupport::Inflector#parameterize's tests
# Mostly imported from ActiveSupport::Inflector#parameterize's tests
titles = {
"Donald E. Knuth" => "donald-e-knuth",
"Random text with *(bad)* characters" => "random-text-with-bad-characters",
"!@#Surrounding bad characters!@#" => "surrounding-bad-characters",
"Squeeze separators" => "squeeze-separators",
"Test with + sign" => "test-with-sign",
"Test with a Namespaced::Class" => "test-with-a-namespaced-class"
"Test with a Namespaced::Class" => "test-with-a-namespaced-class",
"Foo & Bar" => "foo-bar",
"Foo&Bar" => "foo-bar",
"Foo &amp; Bar" => "foo-bar"
}
titles.each do |title, anchor|

View File

@@ -38,6 +38,20 @@ class MarkdownTest < Redcarpet::TestCase
assert_equal expected, output
end
def test_simple_inline_html_center
output = render("before\n\n<center>\n foo\n</center>\n\nafter")
expected = "<p>before</p>\n\n<center>\n foo\n</center>\n\n<p>after</p>"
assert_equal expected, output
end
def test_simple_inline_html_i
output = render("<i>after</i>")
expected = "<p><i>after</i></p>"
assert_equal expected, output
end
def test_that_html_blocks_do_not_require_their_own_end_tag_line
output = render("Para 1\n\n<div><pre>HTML block\n</pre></div>\n\nPara 2 [Link](#anchor)")
expected = "<p>Para 1</p>\n\n<div><pre>HTML block\n</pre></div>\n\n<p>Para 2 <a href=\"#anchor\">Link</a></p>"
@@ -220,6 +234,16 @@ class MarkdownTest < Redcarpet::TestCase
assert_equal '<p>this is a <q>quote</q></p>', output
end
def test_quote_flag_honors_escape_html
text = 'We are not "<svg/onload=pwned>"'
output_enabled = render(text, with: [:quote, :escape_html])
output_disabled = render(text, with: [:quote])
assert_equal "<p>We are not <q>&lt;svg/onload=pwned&gt;</q></p>", output_enabled
assert_equal "<p>We are not <q><svg/onload=pwned></q></p>", output_disabled
end
def test_that_fenced_flag_works
text = <<-fenced.strip_heredoc
This is a simple test