diff --git a/.travis.yml b/.travis.yml
index fb70614..6360584 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,8 @@ rvm:
- 2.3
- 2.4
- 2.5
- - rbx-3.100
+ - 2.6
+ - 2.7
- ruby-head
matrix:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e18a599..c842a05 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,39 @@
# Changelog
+
* Match fence char and length when matching closing fence in fenced code blocks.
Fixes #208.
*Martin Cizek, Orchitech*
+* Consider `
` 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.
diff --git a/README.markdown b/README.markdown
index a36ea8f..b9764ba 100644
--- a/README.markdown
+++ b/README.markdown
@@ -2,8 +2,8 @@ Redcarpet is written with sugar, spice and everything nice
============================================================
[](https://travis-ci.org/vmg/redcarpet)
-[](https://www.versioneye.com/ruby/redcarpet)
[](https://www.codetriage.com/vmg/redcarpet)
+[](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
diff --git a/ext/redcarpet/html.c b/ext/redcarpet/html.c
index 805ddd8..3e29405 100644
--- a/ext/redcarpet/html.c
+++ b/ext/redcarpet/html.c
@@ -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, "");
- 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, "
");
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)
diff --git a/ext/redcarpet/html_block_names.txt b/ext/redcarpet/html_block_names.txt
new file mode 100644
index 0000000..4eb4859
--- /dev/null
+++ b/ext/redcarpet/html_block_names.txt
@@ -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
diff --git a/ext/redcarpet/html_blocks.h b/ext/redcarpet/html_blocks.h
index cdc1381..379c502 100644
--- a/ext/redcarpet/html_blocks.h
+++ b/ext/redcarpet/html_blocks.h
@@ -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 ."
+#error "gperf generated tables don't work with this execution character set. Please report a bug to ."
#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)
{
diff --git a/ext/redcarpet/rc_render.c b/ext/redcarpet/rc_render.c
index 26568ac..960a1ac 100644
--- a/ext/redcarpet/rc_render.c
+++ b/ext/redcarpet/rc_render.c
@@ -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
diff --git a/lib/redcarpet.rb b/lib/redcarpet.rb
index 4a494a4..c166147 100644
--- a/lib/redcarpet.rb
+++ b/lib/redcarpet.rb
@@ -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
diff --git a/redcarpet.gemspec b/redcarpet.gemspec
index bb0453b..322782b 100644
--- a/redcarpet.gemspec
+++ b/redcarpet.gemspec
@@ -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Ã"]
diff --git a/test/custom_render_test.rb b/test/custom_render_test.rb
index 87c9f07..d86ed35 100644
--- a/test/custom_render_test.rb
+++ b/test/custom_render_test.rb
@@ -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)
+ %(#{content} | )
+ 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?("A | ") }
+ assert { first_output.include?("B | ") }
+
+ assert { second_output.include?("A | ") }
+ assert { second_output.include?("B | ") }
+
+ [first_output, second_output].each do |output|
+ assert { output.include?("C | ") }
+ assert { output.include?("D | ") }
+ end
+ end
end
diff --git a/test/html_toc_render_test.rb b/test/html_toc_render_test.rb
index 0aae905..84759b1 100644
--- a/test/html_toc_render_test.rb
+++ b/test/html_toc_render_test.rb
@@ -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 & Bar" => "foo-bar"
}
titles.each do |title, anchor|
diff --git a/test/markdown_test.rb b/test/markdown_test.rb
index e6b9a7f..b1a6956 100644
--- a/test/markdown_test.rb
+++ b/test/markdown_test.rb
@@ -38,6 +38,20 @@ class MarkdownTest < Redcarpet::TestCase
assert_equal expected, output
end
+ def test_simple_inline_html_center
+ output = render("before\n\n\n foo\n\n\nafter")
+ expected = "before
\n\n\n foo\n\n\nafter
"
+
+ assert_equal expected, output
+ end
+
+ def test_simple_inline_html_i
+ output = render("after")
+ expected = "after
"
+
+ assert_equal expected, output
+ end
+
def test_that_html_blocks_do_not_require_their_own_end_tag_line
output = render("Para 1\n\n\n\nPara 2 [Link](#anchor)")
expected = "Para 1
\n\n\n\nPara 2 Link
"
@@ -220,6 +234,16 @@ class MarkdownTest < Redcarpet::TestCase
assert_equal 'this is a quote
', output
end
+ def test_quote_flag_honors_escape_html
+ text = 'We are not "