mirror of
https://github.com/vmg/redcarpet.git
synced 2026-01-10 23:28:07 -05:00
Merge branch 'master' into match-closing-fence-type-and-length
This commit is contained in:
@@ -18,7 +18,8 @@ rvm:
|
||||
- 2.3
|
||||
- 2.4
|
||||
- 2.5
|
||||
- rbx-3.100
|
||||
- 2.6
|
||||
- 2.7
|
||||
- ruby-head
|
||||
|
||||
matrix:
|
||||
|
||||
28
CHANGELOG.md
28
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 `<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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
44
ext/redcarpet/html_block_names.txt
Normal file
44
ext/redcarpet/html_block_names.txt
Normal 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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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í"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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><svg/onload=pwned></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
|
||||
|
||||
Reference in New Issue
Block a user