From e1bcc1c47279a945af7e04e7f5c1f3615b01a825 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 04:51:28 +0200
Subject: [PATCH 01/82] Fix test/CommonMarkTest.php
---
test/CommonMarkTest.php | 82 ++++++++++++++++++-----------------------
1 file changed, 36 insertions(+), 46 deletions(-)
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php
index 9b8d116..680201b 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTest.php
@@ -1,74 +1,64 @@
parsedown = new Parsedown();
+ $this->parsedown->setUrlsLinked(false);
+ }
+
/**
* @dataProvider data
* @param $section
* @param $markdown
* @param $expectedHtml
*/
- function test_($section, $markdown, $expectedHtml)
+ public function testExample($section, $markdown, $expectedHtml)
{
- $Parsedown = new Parsedown();
- $Parsedown->setUrlsLinked(false);
-
- $actualHtml = $Parsedown->text($markdown);
- $actualHtml = $this->normalizeMarkup($actualHtml);
-
+ $actualHtml = $this->parsedown->text($markdown);
$this->assertEquals($expectedHtml, $actualHtml);
}
- function data()
+ /**
+ * @return array
+ */
+ public function data()
{
$spec = file_get_contents(self::SPEC_URL);
+ if ($spec === false) {
+ $this->fail('Unable to load CommonMark spec from ' . self::SPEC_URL);
+ }
+
+ $spec = str_replace("\r\n", "\n", $spec);
$spec = strstr($spec, '', true);
- $tests = array();
+ $matches = array();
+ preg_match_all('/^(?s)`{32} example\n(.*?)\n\.\n(.*?)\n`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
+
+ $data = array();
$currentSection = '';
+ foreach ($matches as $match) {
+ if (isset($match[3])) {
+ $currentSection = $match[3];
+ } else {
+ $data[] = array(
+ 'section' => $currentSection,
+ 'markdown' => str_replace('→', "\t", $match[1]),
+ 'expectedHtml' => str_replace('→', "\t", $match[2])
+ );
+ }
+ }
- preg_replace_callback(
- '/^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$/m',
- function($matches) use ( & $tests, & $currentSection, & $testCount) {
- if (isset($matches[3]) and $matches[3]) {
- $currentSection = $matches[3];
- } else {
- $testCount++;
- $markdown = $matches[1];
- $markdown = preg_replace('/→/', "\t", $markdown);
- $expectedHtml = $matches[2];
- $expectedHtml = $this->normalizeMarkup($expectedHtml);
- $tests []= array(
- $currentSection, # section
- $markdown, # markdown
- $expectedHtml, # html
- );
- }
- },
- $spec
- );
-
- return $tests;
- }
-
- private function normalizeMarkup($markup)
- {
- $markup = preg_replace("/\n+/", "\n", $markup);
- $markup = preg_replace('/^\s+/m', '', $markup);
- $markup = preg_replace('/^((?:<[\w]+>)+)\n/m', '$1', $markup);
- $markup = preg_replace('/\n((?:<\/[\w]+>)+)$/m', '$1', $markup);
- $markup = trim($markup);
-
- return $markup;
+ return $data;
}
}
From 3a46a31e09e5c54f02dc170af83221e0a4b9fef3 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 14:37:34 +0200
Subject: [PATCH 02/82] Fix test/CommonMarkTest.php example regex
---
test/CommonMarkTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php
index 680201b..fd07a49 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTest.php
@@ -43,7 +43,7 @@ class CommonMarkTest extends PHPUnit_Framework_TestCase
$spec = strstr($spec, '', true);
$matches = array();
- preg_match_all('/^(?s)`{32} example\n(.*?)\n\.\n(.*?)\n`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
+ preg_match_all('/^`{32} example\n((?s).*?)\n\.\n((?s).*?)\n`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
$data = array();
$currentSection = '';
From d33e736fa32cbab800936c2e910d986b9b32781e Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 14:38:47 +0200
Subject: [PATCH 03/82] Add test/CommonMarkTestWeak.php
---
test/CommonMarkTest.php | 4 ++-
test/CommonMarkTestWeak.php | 61 +++++++++++++++++++++++++++++++++++++
test/TestParsedown.php | 4 +++
3 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 test/CommonMarkTestWeak.php
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php
index fd07a49..c7a1d52 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTest.php
@@ -13,7 +13,9 @@ class CommonMarkTest extends PHPUnit_Framework_TestCase
protected function setUp()
{
- $this->parsedown = new Parsedown();
+ require_once(__DIR__ . '/TestParsedown.php');
+
+ $this->parsedown = new TestParsedown();
$this->parsedown->setUrlsLinked(false);
}
diff --git a/test/CommonMarkTestWeak.php b/test/CommonMarkTestWeak.php
new file mode 100644
index 0000000..9b0b730
--- /dev/null
+++ b/test/CommonMarkTestWeak.php
@@ -0,0 +1,61 @@
+parsedown->getTextLevelElements();
+
+ array_walk($textLevelElements, function (&$element) {
+ $element = preg_quote($element, '/');
+ });
+ $this->textLevelElementRegex = '\b(?:' . implode('|', $textLevelElements) . ')\b';
+ }
+
+ /**
+ * @dataProvider data
+ * @param $section
+ * @param $markdown
+ * @param $expectedHtml
+ */
+ public function testExample($section, $markdown, $expectedHtml)
+ {
+ $expectedHtml = $this->cleanupHtml($expectedHtml);
+
+ $actualHtml = $this->parsedown->text($markdown);
+ $actualHtml = $this->cleanupHtml($actualHtml);
+
+ $this->assertEquals($expectedHtml, $actualHtml);
+ }
+
+ protected function cleanupHtml($markup)
+ {
+ // invisible whitespaces at the beginning and end of block elements
+ // however, whitespaces at the beginning of elements do matter
+ $markup = preg_replace(
+ array(
+ '/(<(?!(?:' . $this->textLevelElementRegex . '|\bpre\b))\w+\b[^>]*>(?:<' . $this->textLevelElementRegex . '[^>]*>)?)\s+/s',
+ '/\s+((?:<\/' . $this->textLevelElementRegex . '>)?<\/(?!' . $this->textLevelElementRegex . ')\w+\b>)/s'
+ ),
+ '$1',
+ $markup
+ );
+
+ return $markup;
+ }
+}
diff --git a/test/TestParsedown.php b/test/TestParsedown.php
index 7024dfb..2faa0ab 100644
--- a/test/TestParsedown.php
+++ b/test/TestParsedown.php
@@ -2,4 +2,8 @@
class TestParsedown extends Parsedown
{
+ public function getTextLevelElements()
+ {
+ return $this->textLevelElements;
+ }
}
From 2cacfb8da4d81e8a417e17da20e52e99d5a0e98e Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 15:17:52 +0200
Subject: [PATCH 04/82] Improve test/CommonMarkTestWeak.php
---
test/CommonMarkTestWeak.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/CommonMarkTestWeak.php b/test/CommonMarkTestWeak.php
index 9b0b730..751cad5 100644
--- a/test/CommonMarkTestWeak.php
+++ b/test/CommonMarkTestWeak.php
@@ -49,8 +49,8 @@ class CommonMarkTestWeak extends CommonMarkTest
// however, whitespaces at the beginning of elements do matter
$markup = preg_replace(
array(
- '/(<(?!(?:' . $this->textLevelElementRegex . '|\bpre\b))\w+\b[^>]*>(?:<' . $this->textLevelElementRegex . '[^>]*>)?)\s+/s',
- '/\s+((?:<\/' . $this->textLevelElementRegex . '>)?<\/(?!' . $this->textLevelElementRegex . ')\w+\b>)/s'
+ '/(<(?!(?:' . $this->textLevelElementRegex . '|\bpre\b))\w+\b[^>]*>(?:<' . $this->textLevelElementRegex . '[^>]*>)*)\s+/s',
+ '/\s+((?:<\/' . $this->textLevelElementRegex . '>)*<\/(?!' . $this->textLevelElementRegex . ')\w+\b>)/s'
),
'$1',
$markup
From 228d5f4754fbd5e2f2e12437cab121c2f05004d8 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 15:31:07 +0200
Subject: [PATCH 05/82] Improve test/CommonMarkTestWeak.php
---
test/CommonMarkTestWeak.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/CommonMarkTestWeak.php b/test/CommonMarkTestWeak.php
index 751cad5..e467201 100644
--- a/test/CommonMarkTestWeak.php
+++ b/test/CommonMarkTestWeak.php
@@ -15,12 +15,13 @@ require_once(__DIR__ . '/CommonMarkTest.php');
*/
class CommonMarkTestWeak extends CommonMarkTest
{
+ protected $textLevelElementRegex;
+
protected function setUp()
{
parent::setUp();
$textLevelElements = $this->parsedown->getTextLevelElements();
-
array_walk($textLevelElements, function (&$element) {
$element = preg_quote($element, '/');
});
From 33a23fbfb22902e6268b8459af2784803aec8848 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Mon, 5 Sep 2016 21:10:23 +0200
Subject: [PATCH 06/82] Refactor PHPUnit bootstrap
This allows Parsedown extensions (like Parsedown Extra) to reuse existing Parsedown tests. See erusev/parsedown-extra#96 for details.
---
test/CommonMarkTest.php | 2 --
test/ParsedownTest.php | 7 +++----
test/bootstrap.php | 16 +++++++++++++++-
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php
index c7a1d52..18bca8b 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTest.php
@@ -13,8 +13,6 @@ class CommonMarkTest extends PHPUnit_Framework_TestCase
protected function setUp()
{
- require_once(__DIR__ . '/TestParsedown.php');
-
$this->parsedown = new TestParsedown();
$this->parsedown->setUrlsLinked(false);
}
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index c922ab1..7d07183 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -27,7 +27,7 @@ class ParsedownTest extends PHPUnit_Framework_TestCase
*/
protected function initParsedown()
{
- $Parsedown = new Parsedown();
+ $Parsedown = new TestParsedown();
return $Parsedown;
}
@@ -132,15 +132,14 @@ color: red;
comment
<!-- html comment -->
EXPECTED_HTML;
- $parsedownWithNoMarkup = new Parsedown();
+
+ $parsedownWithNoMarkup = new TestParsedown();
$parsedownWithNoMarkup->setMarkupEscaped(true);
$this->assertEquals($expectedHtml, $parsedownWithNoMarkup->text($markdownWithHtml));
}
public function testLateStaticBinding()
{
- include 'test/TestParsedown.php';
-
$parsedown = Parsedown::instance();
$this->assertInstanceOf('Parsedown', $parsedown);
diff --git a/test/bootstrap.php b/test/bootstrap.php
index 5f264d2..76011c9 100644
--- a/test/bootstrap.php
+++ b/test/bootstrap.php
@@ -1,3 +1,17 @@
Date: Mon, 5 Sep 2016 22:04:46 +0200
Subject: [PATCH 07/82] Remove PHPUnit bootstrap in favour of composer
---
.travis.yml | 3 +++
composer.json | 8 ++++++++
phpunit.xml.dist | 4 ++--
test/bootstrap.php | 17 -----------------
4 files changed, 13 insertions(+), 19 deletions(-)
delete mode 100644 test/bootstrap.php
diff --git a/.travis.yml b/.travis.yml
index 256dcf1..5d420e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,3 +13,6 @@ matrix:
fast_finish: true
allow_failures:
- php: hhvm-nightly
+
+install:
+ - composer install
diff --git a/composer.json b/composer.json
index 28145af..b6a376d 100644
--- a/composer.json
+++ b/composer.json
@@ -17,5 +17,13 @@
},
"autoload": {
"psr-0": {"Parsedown": ""}
+ },
+ "autoload-dev": {
+ "psr-0": {
+ "TestParsedown": "test/",
+ "ParsedownTest": "test/",
+ "CommonMarkTest": "test/",
+ "CommonMarkTestWeak": "test/"
+ }
}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index b2d5e9d..4fe3177 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,8 +1,8 @@
-
+
test/ParsedownTest.php
-
\ No newline at end of file
+
diff --git a/test/bootstrap.php b/test/bootstrap.php
deleted file mode 100644
index 76011c9..0000000
--- a/test/bootstrap.php
+++ /dev/null
@@ -1,17 +0,0 @@
-
Date: Thu, 22 Sep 2016 12:21:39 +0200
Subject: [PATCH 08/82] Update Parsedown.php
Made parsedown compatible with html-tags containing dashes.
see https://github.com/erusev/parsedown/issues/407#issuecomment-248833563
---
Parsedown.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 646af86..6425b49 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -673,7 +673,7 @@ class Parsedown
return;
}
- if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
+ if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
$element = strtolower($matches[1]);
@@ -1258,7 +1258,7 @@ class Parsedown
return;
}
- if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
+ if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
@@ -1274,7 +1274,7 @@ class Parsedown
);
}
- if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
+ if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
From d6d5f53ff4dfd221596e15536ee9a5956133a29f Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sat, 1 Oct 2016 15:56:14 +0100
Subject: [PATCH 09/82] =?UTF-8?q?Fix=20Issue=20#358=20=E2=80=93=20preventi?=
=?UTF-8?q?ng=20double=20nested=20links?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. Add the ability for a parsed element to enforce that the line handler not parse any (immediate) child elements of a specified type.
2. Use 1. to allow parsed Url elements to tell the line handler not to parse any child Links or Urls where they are immediate children.
---
Parsedown.php | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 646af86..e34efe8 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -987,7 +987,7 @@ class Parsedown
# ~
#
- public function line($text)
+ public function line($text, $non_nestables=array())
{
$markup = '';
@@ -1003,6 +1003,12 @@ class Parsedown
foreach ($this->InlineTypes[$marker] as $inlineType)
{
+
+ if(in_array($inlineType, $non_nestables))
+ {
+ continue;
+ }
+
$Inline = $this->{'inline'.$inlineType}($Excerpt);
if ( ! isset($Inline))
@@ -1183,6 +1189,7 @@ class Parsedown
$Element = array(
'name' => 'a',
'handler' => 'line',
+ 'non_nestables' => array('Url', 'Link'),
'text' => null,
'attributes' => array(
'href' => null,
@@ -1410,9 +1417,11 @@ class Parsedown
{
$markup .= '>';
+ if(!isset($Element['non_nestables'])) $Element['non_nestables'] = array();
+
if (isset($Element['handler']))
{
- $markup .= $this->{$Element['handler']}($Element['text']);
+ $markup .= $this->{$Element['handler']}($Element['text'], $Element['non_nestables']);
}
else
{
From 4d3600f273cc3613cf420408c9016635216c15ea Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 2 Oct 2016 17:37:08 +0100
Subject: [PATCH 10/82] Extend disallowed assertion depth capabilities
I've built on the functionality of feature 1. in the previous commit to allow non nestables to be asserted indefinitely, or to a specified depth.
---
Parsedown.php | 39 ++++++++++++++++++++++++++++++++++++---
1 file changed, 36 insertions(+), 3 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index e34efe8..b708236 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1003,10 +1003,23 @@ class Parsedown
foreach ($this->InlineTypes[$marker] as $inlineType)
{
-
- if(in_array($inlineType, $non_nestables))
+ foreach ($non_nestables as $key => $non_nestable)
{
- continue;
+ if (is_array($non_nestable))
+ {
+
+ if ($non_nestable[0] === $inlineType)
+ {
+ continue 2;
+ }
+ }
+ else
+ {
+ if ($non_nestable === $inlineType)
+ {
+ continue 2;
+ }
+ }
}
$Inline = $this->{'inline'.$inlineType}($Excerpt);
@@ -1030,6 +1043,26 @@ class Parsedown
$Inline['position'] = $markerPosition;
}
+ foreach ($non_nestables as $key => $non_nestable)
+ {
+ if (is_array($non_nestable) && isset($non_nestable[1]) && is_int($non_nestable[1])){
+ if($non_nestable[1] > 1)
+ {
+ $Inline['element']['non_nestables'][] = array($non_nestable[0], $non_nestable[1] -1);
+ }
+
+ }
+ elseif (is_array($non_nestable) && ! isset($non_nestable[1]))
+ {
+ $Inline['element']['non_nestables'][] = array($non_nestable[0]);
+ }
+ elseif ( ! is_array($non_nestable))
+ {
+ $Inline['element']['non_nestables'][] = $non_nestable;
+ }
+ }
+
+
# the text that comes before the inline
$unmarkedText = substr($text, 0, $Inline['position']);
From 50952b3243cbc4585b2e3f6f609445d34bc4d794 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 2 Oct 2016 18:26:13 +0100
Subject: [PATCH 11/82] Line handler may prevent specified element nesting
This commit serves to add comments detailing parts of the new functionality, and to adjust syntax preferences to match that of the surrounding document. The commit title also now reflects the most significant change made.
---
Parsedown.php | 42 ++++++++++++++++++++++--------------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index b708236..6b6df9c 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1003,22 +1003,19 @@ class Parsedown
foreach ($this->InlineTypes[$marker] as $inlineType)
{
+ # check to see if the current inline type is nestable in the current context
+
foreach ($non_nestables as $key => $non_nestable)
{
- if (is_array($non_nestable))
+ # case that we used array syntax
+ if (is_array($non_nestable) and $non_nestable[0] === $inlineType)
{
-
- if ($non_nestable[0] === $inlineType)
- {
- continue 2;
- }
+ continue 2;
}
- else
+ # case that we used plain string syntax
+ elseif ( ! is_array($non_nestable) and $non_nestable === $inlineType)
{
- if ($non_nestable === $inlineType)
- {
- continue 2;
- }
+ continue 2;
}
}
@@ -1043,25 +1040,27 @@ class Parsedown
$Inline['position'] = $markerPosition;
}
+ # cause the new element to 'inherit' our non nestables, if appropriate
+
foreach ($non_nestables as $key => $non_nestable)
{
- if (is_array($non_nestable) && isset($non_nestable[1]) && is_int($non_nestable[1])){
- if($non_nestable[1] > 1)
- {
- $Inline['element']['non_nestables'][] = array($non_nestable[0], $non_nestable[1] -1);
- }
-
+ # array syntax, and depth is sufficient to pass on
+ if (is_array($non_nestable) and isset($non_nestable[1]) and
+ is_int($non_nestable[1]) and $non_nestable[1] > 1)
+ {
+ $Inline['element']['non_nestables'][] = array($non_nestable[0], $non_nestable[1] -1);
}
- elseif (is_array($non_nestable) && ! isset($non_nestable[1]))
+ # array syntax, and depth is indefinite
+ elseif (is_array($non_nestable) and ! isset($non_nestable[1]))
{
$Inline['element']['non_nestables'][] = array($non_nestable[0]);
}
+ # string syntax, so depth is indefinite
elseif ( ! is_array($non_nestable))
{
$Inline['element']['non_nestables'][] = $non_nestable;
}
}
-
# the text that comes before the inline
$unmarkedText = substr($text, 0, $Inline['position']);
@@ -1450,7 +1449,10 @@ class Parsedown
{
$markup .= '>';
- if(!isset($Element['non_nestables'])) $Element['non_nestables'] = array();
+ if (!isset($Element['non_nestables']))
+ {
+ $Element['non_nestables'] = array();
+ }
if (isset($Element['handler']))
{
From a81aedeb109ed16270bbf6aaead7b41e5593caeb Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 4 Oct 2016 15:27:11 +0100
Subject: [PATCH 12/82] Line handler may prevent specified element nesting
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Removed granularity controls – elements are assumed to be non nestable indefinitely once declared.
---
Parsedown.php | 34 +++++-----------------------------
1 file changed, 5 insertions(+), 29 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 6b6df9c..f1e09d9 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1005,18 +1005,9 @@ class Parsedown
{
# check to see if the current inline type is nestable in the current context
- foreach ($non_nestables as $key => $non_nestable)
+ if (in_array($inlineType, $non_nestables))
{
- # case that we used array syntax
- if (is_array($non_nestable) and $non_nestable[0] === $inlineType)
- {
- continue 2;
- }
- # case that we used plain string syntax
- elseif ( ! is_array($non_nestable) and $non_nestable === $inlineType)
- {
- continue 2;
- }
+ continue;
}
$Inline = $this->{'inline'.$inlineType}($Excerpt);
@@ -1040,26 +1031,11 @@ class Parsedown
$Inline['position'] = $markerPosition;
}
- # cause the new element to 'inherit' our non nestables, if appropriate
+ # cause the new element to 'inherit' our non nestables
- foreach ($non_nestables as $key => $non_nestable)
+ foreach ($non_nestables as $non_nestable)
{
- # array syntax, and depth is sufficient to pass on
- if (is_array($non_nestable) and isset($non_nestable[1]) and
- is_int($non_nestable[1]) and $non_nestable[1] > 1)
- {
- $Inline['element']['non_nestables'][] = array($non_nestable[0], $non_nestable[1] -1);
- }
- # array syntax, and depth is indefinite
- elseif (is_array($non_nestable) and ! isset($non_nestable[1]))
- {
- $Inline['element']['non_nestables'][] = array($non_nestable[0]);
- }
- # string syntax, so depth is indefinite
- elseif ( ! is_array($non_nestable))
- {
- $Inline['element']['non_nestables'][] = $non_nestable;
- }
+ $Inline['element']['non_nestables'][] = $non_nestable;
}
# the text that comes before the inline
From 543a6c4175a1debd148e933fb271f5b8f088b199 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 4 Oct 2016 18:59:36 +0100
Subject: [PATCH 13/82] Line handler may prevent specified element nesting
Check if array is empty to shave some performance hits in the case than no non nestables are present.
---
Parsedown.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index f1e09d9..fc2158f 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1005,7 +1005,7 @@ class Parsedown
{
# check to see if the current inline type is nestable in the current context
- if (in_array($inlineType, $non_nestables))
+ if ( ! empty($non_nestables) and in_array($inlineType, $non_nestables))
{
continue;
}
From 3aef89b3994d942d4eb85d6df0c8c43a157ac5d7 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sat, 8 Oct 2016 17:54:04 +0100
Subject: [PATCH 14/82] Line handler may prevent specified element nesting
Swap `under_scores` for `camelCasing`
---
Parsedown.php | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index fc2158f..e2b9d60 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -987,7 +987,7 @@ class Parsedown
# ~
#
- public function line($text, $non_nestables=array())
+ public function line($text, $nonNestables=array())
{
$markup = '';
@@ -1005,7 +1005,7 @@ class Parsedown
{
# check to see if the current inline type is nestable in the current context
- if ( ! empty($non_nestables) and in_array($inlineType, $non_nestables))
+ if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
{
continue;
}
@@ -1033,9 +1033,9 @@ class Parsedown
# cause the new element to 'inherit' our non nestables
- foreach ($non_nestables as $non_nestable)
+ foreach ($nonNestables as $non_nestable)
{
- $Inline['element']['non_nestables'][] = $non_nestable;
+ $Inline['element']['nonNestables'][] = $non_nestable;
}
# the text that comes before the inline
@@ -1197,7 +1197,7 @@ class Parsedown
$Element = array(
'name' => 'a',
'handler' => 'line',
- 'non_nestables' => array('Url', 'Link'),
+ 'nonNestables' => array('Url', 'Link'),
'text' => null,
'attributes' => array(
'href' => null,
@@ -1425,14 +1425,14 @@ class Parsedown
{
$markup .= '>';
- if (!isset($Element['non_nestables']))
+ if (!isset($Element['nonNestables']))
{
- $Element['non_nestables'] = array();
+ $Element['nonNestables'] = array();
}
if (isset($Element['handler']))
{
- $markup .= $this->{$Element['handler']}($Element['text'], $Element['non_nestables']);
+ $markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
}
else
{
From f0587d41a9783e4c3ad817e74909744d072d2874 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Sun, 9 Oct 2016 14:17:03 +0200
Subject: [PATCH 15/82] Add test/CommonMarkTestWeak.php to .travis.yml
Failing tests don't break builds on purpose, Parsedown doesn't fully comply with the CommonMark specs at the moment. We should switch to test/CommonMarkTest.php later, see #423 for details.
---
.travis.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.travis.yml b/.travis.yml
index 5d420e1..09c8e2b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,3 +16,6 @@ matrix:
install:
- composer install
+
+script:
+ - phpunit test/CommonMarkTestWeak.php || true
From be671e72a39484ff9354cd7f1177d5901e1bdf9b Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Sun, 9 Oct 2016 14:21:17 +0200
Subject: [PATCH 16/82] Don't let Travis skip Parsedown's phpunit tests
---
.travis.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.travis.yml b/.travis.yml
index 09c8e2b..3320963 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,4 +18,5 @@ install:
- composer install
script:
+ - phpunit
- phpunit test/CommonMarkTestWeak.php || true
From 2423644d728075dfd55199e6314d4ad3b24d9072 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Wed, 12 Oct 2016 02:01:40 +0200
Subject: [PATCH 17/82] Move test/CommonMarkTest.php to
test/CommonMarkTestStrict.php
Add parameter `$id` to CommonMark tests
---
test/{CommonMarkTest.php => CommonMarkTestStrict.php} | 7 +++++--
test/CommonMarkTestWeak.php | 7 ++++---
2 files changed, 9 insertions(+), 5 deletions(-)
rename test/{CommonMarkTest.php => CommonMarkTestStrict.php} (88%)
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTestStrict.php
similarity index 88%
rename from test/CommonMarkTest.php
rename to test/CommonMarkTestStrict.php
index 18bca8b..bd16ffa 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTestStrict.php
@@ -5,7 +5,7 @@
*
* @link http://commonmark.org/ CommonMark
*/
-class CommonMarkTest extends PHPUnit_Framework_TestCase
+class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
{
const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt';
@@ -19,11 +19,12 @@ class CommonMarkTest extends PHPUnit_Framework_TestCase
/**
* @dataProvider data
+ * @param $id
* @param $section
* @param $markdown
* @param $expectedHtml
*/
- public function testExample($section, $markdown, $expectedHtml)
+ public function testExample($id, $section, $markdown, $expectedHtml)
{
$actualHtml = $this->parsedown->text($markdown);
$this->assertEquals($expectedHtml, $actualHtml);
@@ -46,12 +47,14 @@ class CommonMarkTest extends PHPUnit_Framework_TestCase
preg_match_all('/^`{32} example\n((?s).*?)\n\.\n((?s).*?)\n`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
$data = array();
+ $currentId = 0;
$currentSection = '';
foreach ($matches as $match) {
if (isset($match[3])) {
$currentSection = $match[3];
} else {
$data[] = array(
+ 'id' => ++$currentId,
'section' => $currentSection,
'markdown' => str_replace('→', "\t", $match[1]),
'expectedHtml' => str_replace('→', "\t", $match[2])
diff --git a/test/CommonMarkTestWeak.php b/test/CommonMarkTestWeak.php
index e467201..ef4081a 100644
--- a/test/CommonMarkTestWeak.php
+++ b/test/CommonMarkTestWeak.php
@@ -1,5 +1,5 @@
cleanupHtml($expectedHtml);
From a9f696f7bb413cbf7b14b744806fce504353e072 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Thu, 13 Oct 2016 22:16:46 +0200
Subject: [PATCH 18/82] Improve CommonMark spec example regex
CommonMark spec example [#170](http://spec.commonmark.org/0.26/#example-170) has a empty HTML result.
---
test/CommonMarkTestStrict.php | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/test/CommonMarkTestStrict.php b/test/CommonMarkTestStrict.php
index bd16ffa..3837738 100644
--- a/test/CommonMarkTestStrict.php
+++ b/test/CommonMarkTestStrict.php
@@ -7,7 +7,7 @@
*/
class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
{
- const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt';
+ const SPEC_URL = 'https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt';
protected $parsedown;
@@ -44,7 +44,7 @@ class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
$spec = strstr($spec, '', true);
$matches = array();
- preg_match_all('/^`{32} example\n((?s).*?)\n\.\n((?s).*?)\n`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
+ preg_match_all('/^`{32} example\n((?s).*?)\n\.\n(?:|((?s).*?)\n)`{32}$|^#{1,6} *(.*?)$/m', $spec, $matches, PREG_SET_ORDER);
$data = array();
$currentId = 0;
@@ -53,11 +53,15 @@ class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
if (isset($match[3])) {
$currentSection = $match[3];
} else {
- $data[] = array(
- 'id' => ++$currentId,
+ $currentId++;
+ $markdown = str_replace('→', "\t", $match[1]);
+ $expectedHtml = isset($match[2]) ? str_replace('→', "\t", $match[2]) : '';
+
+ $data[$currentId] = array(
+ 'id' => $currentId,
'section' => $currentSection,
- 'markdown' => str_replace('→', "\t", $match[1]),
- 'expectedHtml' => str_replace('→', "\t", $match[2])
+ 'markdown' => $markdown,
+ 'expectedHtml' => $expectedHtml
);
}
}
From ae0211a84c92a1eab7892a7c1876b5a5f7402f28 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Thu, 13 Oct 2016 22:17:03 +0200
Subject: [PATCH 19/82] Travis: Add PHP nightly
---
.travis.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.travis.yml b/.travis.yml
index 3320963..3259ca8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,12 +6,14 @@ php:
- 5.5
- 5.4
- 5.3
+ - nightly
- hhvm
- hhvm-nightly
matrix:
fast_finish: true
allow_failures:
+ - php: nightly
- php: hhvm-nightly
install:
From 1140613fc7aa1393a6274812deab253b90027ae7 Mon Sep 17 00:00:00 2001
From: naNuke
Date: Wed, 21 Jan 2015 03:50:36 +0100
Subject: [PATCH 20/82] Prevent various XSS attacks
---
Parsedown.php | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index f5dd0fa..5d810de 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -75,6 +75,15 @@ class Parsedown
protected $urlsLinked = true;
+ function setSafeLinksEnabled($safeLinksEnabled)
+ {
+ $this->safeLinksEnabled = $safeLinksEnabled;
+
+ return $this;
+ }
+
+ protected $safeLinksEnabled = true;
+
#
# Lines
#
@@ -1253,7 +1262,13 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
- $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
+ if ( $this->safeLinksEnabled && stripos($Element['attributes']['href'], 'javascript:') === 0 )
+ {
+ return;
+ }
+
+ $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href']);
+ $Element['text'] = htmlspecialchars($Element['text']);
return array(
'extent' => $extent,
From bf5105cb1a7a2656d134ab35456a727959574e7d Mon Sep 17 00:00:00 2001
From: naNuke
Date: Sat, 24 Jan 2015 22:37:14 +0100
Subject: [PATCH 21/82] Improve safeLinks with whitelist.
---
Parsedown.php | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 5d810de..94dbe20 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1262,13 +1262,18 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
- if ( $this->safeLinksEnabled && stripos($Element['attributes']['href'], 'javascript:') === 0 )
+ if ( $this->safeLinksEnabled && preg_match("/^(\/|https?:\/\/|ftps?:\/\/)/ui", $Element['attributes']['href']) === 0 )
{
return;
}
- $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href']);
- $Element['text'] = htmlspecialchars($Element['text']);
+ $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES);
+ $Element['text'] = htmlspecialchars($Element['text'], ENT_QUOTES);
+
+ if ( $Element['attributes']['title'] !== null )
+ {
+ $Element['attributes']['title'] = htmlspecialchars($Element['attributes']['title'], ENT_QUOTES);
+ }
return array(
'extent' => $extent,
From 1d4296f34d938758ff88755e4ac44ae1d3fc6857 Mon Sep 17 00:00:00 2001
From: naNuke
Date: Sun, 25 Jan 2015 19:47:32 +0100
Subject: [PATCH 22/82] Customizable whitelist of schemas for safeLinks
---
Parsedown.php | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 94dbe20..9882810 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -84,6 +84,14 @@ class Parsedown
protected $safeLinksEnabled = true;
+ protected $safeLinksWhitelist = array(
+ 'http://',
+ 'https://',
+ '/',
+ 'ftp://',
+ 'ftps://'
+ );
+
#
# Lines
#
@@ -1262,9 +1270,22 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
- if ( $this->safeLinksEnabled && preg_match("/^(\/|https?:\/\/|ftps?:\/\/)/ui", $Element['attributes']['href']) === 0 )
+ if ( $this->safeLinksEnabled )
{
- return;
+ $matched = false;
+ foreach ( $this->safeLinksWhitelist as $scheme )
+ {
+ if ( stripos($Element['attributes']['href'], $scheme) === 0 )
+ {
+ $matched = true;
+ break;
+ }
+ }
+
+ if ( ! $matched )
+ {
+ return;
+ }
}
$Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES);
From b3d45c4bb9bc798e4b297da38c4b015ac8bb44c6 Mon Sep 17 00:00:00 2001
From: naNuke
Date: Mon, 26 Jan 2015 18:49:17 +0100
Subject: [PATCH 23/82] Add html escaping to all attributes capable of holding
user input.
---
Parsedown.php | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 9882810..b162d8e 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -422,7 +422,7 @@ class Parsedown
if (isset($matches[1]))
{
- $class = 'language-'.$matches[1];
+ $class = 'language-'.htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
$Element['attributes'] = array(
'class' => $class,
@@ -532,10 +532,10 @@ class Parsedown
),
);
- if($name === 'ol')
+ if($name === 'ol')
{
$listStart = stristr($matches[0], '.', true);
-
+
if($listStart !== '1')
{
$Block['element']['attributes'] = array('start' => $listStart);
@@ -1108,7 +1108,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
{
- $url = $matches[1];
+ $url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
if ( ! isset($matches[2]))
{
@@ -1288,12 +1288,12 @@ class Parsedown
}
}
- $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES);
- $Element['text'] = htmlspecialchars($Element['text'], ENT_QUOTES);
+ $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES, 'UTF-8');
+ $Element['text'] = htmlspecialchars($Element['text'], ENT_QUOTES, 'UTF-8');
if ( $Element['attributes']['title'] !== null )
{
- $Element['attributes']['title'] = htmlspecialchars($Element['attributes']['title'], ENT_QUOTES);
+ $Element['attributes']['title'] = htmlspecialchars($Element['attributes']['title'], ENT_QUOTES, 'UTF-8');
}
return array(
@@ -1384,14 +1384,16 @@ class Parsedown
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
+ $url = htmlspecialchars($matches[0][0], ENT_QUOTES, 'UTF-8');
+
$Inline = array(
'extent' => strlen($matches[0][0]),
'position' => $matches[0][1],
'element' => array(
'name' => 'a',
- 'text' => $matches[0][0],
+ 'text' => $url,
'attributes' => array(
- 'href' => $matches[0][0],
+ 'href' => $url,
),
),
);
@@ -1404,7 +1406,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
{
- $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
+ $url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
return array(
'extent' => strlen($matches[0]),
From 6bb66db00f7faf29932f96b47fc4ac60a4333e8e Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Mon, 1 May 2017 03:24:40 +0100
Subject: [PATCH 24/82] anti-xss
protect all attributes and content from xss via element method
filter special attributes (a href, img src)
expand url whitelist slightly to permit data images and mailto links
---
Parsedown.php | 94 ++++++++++++++++++++++++++++++---------------------
1 file changed, 56 insertions(+), 38 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index b162d8e..8571cd7 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -89,7 +89,9 @@ class Parsedown
'https://',
'/',
'ftp://',
- 'ftps://'
+ 'ftps://',
+ 'mailto:',
+ 'data:image/png;',
);
#
@@ -359,8 +361,6 @@ class Parsedown
{
$text = $Block['element']['text']['text'];
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
$Block['element']['text']['text'] = $text;
return $Block;
@@ -422,7 +422,7 @@ class Parsedown
if (isset($matches[1]))
{
- $class = 'language-'.htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
+ $class = 'language-'.$matches[1];
$Element['attributes'] = array(
'class' => $class,
@@ -474,8 +474,6 @@ class Parsedown
{
$text = $Block['element']['text']['text'];
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
$Block['element']['text']['text'] = $text;
return $Block;
@@ -1091,7 +1089,6 @@ class Parsedown
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
{
- $url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
+ $url = $matches[1];
if ( ! isset($matches[2]))
{
@@ -1270,32 +1267,6 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
- if ( $this->safeLinksEnabled )
- {
- $matched = false;
- foreach ( $this->safeLinksWhitelist as $scheme )
- {
- if ( stripos($Element['attributes']['href'], $scheme) === 0 )
- {
- $matched = true;
- break;
- }
- }
-
- if ( ! $matched )
- {
- return;
- }
- }
-
- $Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES, 'UTF-8');
- $Element['text'] = htmlspecialchars($Element['text'], ENT_QUOTES, 'UTF-8');
-
- if ( $Element['attributes']['title'] !== null )
- {
- $Element['attributes']['title'] = htmlspecialchars($Element['attributes']['title'], ENT_QUOTES, 'UTF-8');
- }
-
return array(
'extent' => $extent,
'element' => $Element,
@@ -1384,7 +1355,7 @@ class Parsedown
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
- $url = htmlspecialchars($matches[0][0], ENT_QUOTES, 'UTF-8');
+ $url = $matches[0][0];
$Inline = array(
'extent' => strlen($matches[0][0]),
@@ -1406,7 +1377,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
{
- $url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
+ $url = $matches[1];
return array(
'extent' => strlen($matches[0]),
@@ -1444,6 +1415,8 @@ class Parsedown
protected function element(array $Element)
{
+ $Element = $this->sanitiseElement($Element);
+
$markup = '<'.$Element['name'];
if (isset($Element['attributes']))
@@ -1455,7 +1428,7 @@ class Parsedown
continue;
}
- $markup .= ' '.$name.'="'.$value.'"';
+ $markup .= ' '.$name.'="'.self::escape($value).'"';
}
}
@@ -1469,7 +1442,7 @@ class Parsedown
}
else
{
- $markup .= $Element['text'];
+ $markup .= self::escape($Element['text'], true);
}
$markup .= ''.$Element['name'].'>';
@@ -1528,10 +1501,55 @@ class Parsedown
return $markup;
}
+ protected function sanitiseElement(array $Element)
+ {
+ $safeUrlNameToAtt = array(
+ 'a' => 'href',
+ 'img' => 'src',
+ );
+
+ if (isset($safeUrlNameToAtt[$Element['name']]))
+ {
+ $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
+ }
+
+ return $Element;
+ }
+
+ protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
+ {
+ if ($this->safeLinksEnabled)
+ {
+ $safe = false;
+
+ foreach ($this->safeLinksWhitelist as $scheme)
+ {
+ if (stripos($Element['attributes'][$attribute], $scheme) === 0)
+ {
+ $safe = true;
+
+ break;
+ }
+ }
+
+ if ( ! $safe)
+ {
+ unset($Element['attributes'][$attribute]);
+ }
+ }
+
+ return $Element;
+ }
+
#
# Static Methods
#
+ protected static function escape($text, $allowQuotes = false)
+ {
+ return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
+ }
+
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
From af04ac92e2ff852309891ebc767fa5a6bf179f39 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Mon, 1 May 2017 03:33:49 +0100
Subject: [PATCH 25/82] add xss tests
---
test/ParsedownTest.php | 2 ++
test/data/xss_attribute_encoding.html | 6 ++++++
test/data/xss_attribute_encoding.md | 11 ++++++++++
test/data/xss_bad_url.html | 16 ++++++++++++++
test/data/xss_bad_url.md | 31 +++++++++++++++++++++++++++
test/data/xss_text_encoding.html | 7 ++++++
test/data/xss_text_encoding.md | 12 +++++++++++
7 files changed, 85 insertions(+)
create mode 100644 test/data/xss_attribute_encoding.html
create mode 100644 test/data/xss_attribute_encoding.md
create mode 100644 test/data/xss_bad_url.html
create mode 100644 test/data/xss_bad_url.md
create mode 100644 test/data/xss_text_encoding.html
create mode 100644 test/data/xss_text_encoding.md
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index 323dace..5fbf7f1 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -46,6 +46,8 @@ class ParsedownTest extends PHPUnit_Framework_TestCase
$expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup);
$expectedMarkup = str_replace("\r", "\n", $expectedMarkup);
+ $this->Parsedown->setMarkupEscaped($test === 'xss_text_encoding');
+
$actualMarkup = $this->Parsedown->text($markdown);
$this->assertEquals($expectedMarkup, $actualMarkup);
diff --git a/test/data/xss_attribute_encoding.html b/test/data/xss_attribute_encoding.html
new file mode 100644
index 0000000..287ff51
--- /dev/null
+++ b/test/data/xss_attribute_encoding.html
@@ -0,0 +1,6 @@
+xss
+
+xss
+
+
+
\ No newline at end of file
diff --git a/test/data/xss_attribute_encoding.md b/test/data/xss_attribute_encoding.md
new file mode 100644
index 0000000..3d8e0c8
--- /dev/null
+++ b/test/data/xss_attribute_encoding.md
@@ -0,0 +1,11 @@
+[xss](https://www.example.com")
+
+![xss](https://www.example.com")
+
+[xss](https://www.example.com')
+
+![xss](https://www.example.com')
+
+![xss"](https://www.example.com)
+
+![xss'](https://www.example.com)
\ No newline at end of file
diff --git a/test/data/xss_bad_url.html b/test/data/xss_bad_url.html
new file mode 100644
index 0000000..93dd0d8
--- /dev/null
+++ b/test/data/xss_bad_url.html
@@ -0,0 +1,16 @@
+xss
+xss
+xss
+xss
+
+
+
+
+xss
+xss
+xss
+xss
+
+
+
+
\ No newline at end of file
diff --git a/test/data/xss_bad_url.md b/test/data/xss_bad_url.md
new file mode 100644
index 0000000..a730952
--- /dev/null
+++ b/test/data/xss_bad_url.md
@@ -0,0 +1,31 @@
+[xss](javascript:alert(1))
+
+[xss]( javascript:alert(1))
+
+[xss](javascript://alert(1))
+
+[xss](javascript:alert(1))
+
+![xss](javascript:alert(1))
+
+![xss]( javascript:alert(1))
+
+![xss](javascript://alert(1))
+
+![xss](javascript:alert(1))
+
+[xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+[xss]( data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+[xss](data://text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+[xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+![xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+![xss]( data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+![xss](data://text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
+
+![xss](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)
\ No newline at end of file
diff --git a/test/data/xss_text_encoding.html b/test/data/xss_text_encoding.html
new file mode 100644
index 0000000..e6b3fc5
--- /dev/null
+++ b/test/data/xss_text_encoding.html
@@ -0,0 +1,7 @@
+<script>alert(1)</script>
+<script>
+alert(1)
+</script>
+<script>
+alert(1)
+</script>
\ No newline at end of file
diff --git a/test/data/xss_text_encoding.md b/test/data/xss_text_encoding.md
new file mode 100644
index 0000000..b1051a2
--- /dev/null
+++ b/test/data/xss_text_encoding.md
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
From 131ba758514c5e905663bd82fc7c1c8271c7edc5 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Mon, 1 May 2017 15:44:04 +0100
Subject: [PATCH 26/82] filter onevent attributes
---
Parsedown.php | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Parsedown.php b/Parsedown.php
index 8571cd7..2fadec0 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1513,6 +1513,22 @@ class Parsedown
$Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
}
+ if ( ! empty($Element['attributes']))
+ {
+ # clear out nulls
+ $Element['attributes'] = array_filter(
+ $Element['attributes'],
+ function ($v) {return $v !== null;}
+ );
+
+ $onEventAttributes = preg_grep('/^\s*+on/i', array_flip($Element['attributes']));
+
+ foreach ($onEventAttributes as $att)
+ {
+ unset($Element['attributes'][$att]);
+ }
+ }
+
return $Element;
}
From 6d0156d70714fcd89c1b9c9eb573adade13bfb27 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 2 May 2017 00:30:04 +0100
Subject: [PATCH 27/82] dump attributes that contain characters that are
impossible for validity, or very unlikely
---
Parsedown.php | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 2fadec0..488af4b 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1503,7 +1503,8 @@ class Parsedown
protected function sanitiseElement(array $Element)
{
- $safeUrlNameToAtt = array(
+ static $badAttributeChars = "\"'= \t\n\r\0\x0B";
+ static $safeUrlNameToAtt = array(
'a' => 'href',
'img' => 'src',
);
@@ -1515,13 +1516,21 @@ class Parsedown
if ( ! empty($Element['attributes']))
{
- # clear out nulls
- $Element['attributes'] = array_filter(
- $Element['attributes'],
- function ($v) {return $v !== null;}
- );
+ foreach ($Element['attributes'] as $att => $val)
+ {
+ # clear out nulls
+ if ($val === null)
+ {
+ unset($Element['attributes'][$att]);
+ }
+ # filter out badly parsed attribute
+ elseif (strpbrk($att, $badAttributeChars) !== false)
+ {
+ unset($Element['attributes'][$att]);
+ }
+ }
- $onEventAttributes = preg_grep('/^\s*+on/i', array_flip($Element['attributes']));
+ $onEventAttributes = preg_grep('/^on/i', array_flip($Element['attributes']));
foreach ($onEventAttributes as $att)
{
From e4bb12329e8b895bfef4564296624cd47057d2be Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 2 May 2017 01:25:33 +0100
Subject: [PATCH 28/82] array_keys is probably faster
---
Parsedown.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 488af4b..d42e4f5 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1530,9 +1530,9 @@ class Parsedown
}
}
- $onEventAttributes = preg_grep('/^on/i', array_flip($Element['attributes']));
+ $onEventAttributeKeys = preg_grep('/^on/i', array_keys($Element['attributes']));
- foreach ($onEventAttributes as $att)
+ foreach ($onEventAttributeKeys as $att)
{
unset($Element['attributes'][$att]);
}
From 4dc98b635d18527000e74a0eb4cd400db2bf5af3 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 2 May 2017 19:48:08 +0100
Subject: [PATCH 29/82] whitelist changes: * add gif and jpg as allowed data
images * ensure that user controlled content fall only in the "data section"
of the data URI (and does not intersect content-type definition in any way
(best to be safe than sorry ;-))) "data section" as defined in:
https://tools.ietf.org/html/rfc2397#section-3
---
Parsedown.php | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index d42e4f5..7e72d69 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -91,7 +91,9 @@ class Parsedown
'ftp://',
'ftps://',
'mailto:',
- 'data:image/png;',
+ 'data:image/png;base64,',
+ 'data:image/gif;base64,',
+ 'data:image/jpg;base64,',
);
#
From aee3963e6b97186b1e5526c118bf5d2d872cd8ee Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 2 May 2017 19:55:03 +0100
Subject: [PATCH 30/82] jpeg, not jpg
---
Parsedown.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index 7e72d69..c319a19 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -93,7 +93,7 @@ class Parsedown
'mailto:',
'data:image/png;base64,',
'data:image/gif;base64,',
- 'data:image/jpg;base64,',
+ 'data:image/jpeg;base64,',
);
#
From 4bae1c9834382d3c7aa900a7cbf77771b3864c56 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Wed, 3 May 2017 00:39:01 +0100
Subject: [PATCH 31/82] whitelist regex for good attribute (no no chars that
could form a delimiter allowed
---
Parsedown.php | 21 +++++++--------------
1 file changed, 7 insertions(+), 14 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index c319a19..0bd81e2 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1505,7 +1505,7 @@ class Parsedown
protected function sanitiseElement(array $Element)
{
- static $badAttributeChars = "\"'= \t\n\r\0\x0B";
+ static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
static $safeUrlNameToAtt = array(
'a' => 'href',
'img' => 'src',
@@ -1520,23 +1520,16 @@ class Parsedown
{
foreach ($Element['attributes'] as $att => $val)
{
- # clear out nulls
- if ($val === null)
- {
- unset($Element['attributes'][$att]);
- }
# filter out badly parsed attribute
- elseif (strpbrk($att, $badAttributeChars) !== false)
+ if ( ! preg_match($goodAttribute, $att))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ # dump onevent attribute
+ elseif (preg_match('/^on/i', $att))
{
unset($Element['attributes'][$att]);
}
- }
-
- $onEventAttributeKeys = preg_grep('/^on/i', array_keys($Element['attributes']));
-
- foreach ($onEventAttributeKeys as $att)
- {
- unset($Element['attributes'][$att]);
}
}
From 054ba3c48729036fd8ab1166fb0b1d0b4dd1e465 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Wed, 3 May 2017 17:01:27 +0100
Subject: [PATCH 32/82] urlencode urls that are potentially unsafe:
this should break urls that attempt to include a protocol, or port (these are absolute URLs and should have a whitelisted protocol for use)
but URLs that are relative, or relative from the site root should be preserved (though characters non essential for the URL structure may be urlencoded)
this approach has significant advantages over attempting to locate something like `javascript:alert(1)` or `javascript:alert(1)` (which are both valid) because browsers have been known to ignore ridiculous characters when encountered (meaning something like `jav\ta\0\0script:alert(1)` would be xss :( ). Instead of trying to chase down a way to interpret a URL to decide whether there is a protocol, this approach ensures that two essential characters needed to achieve a colon are encoded `:` (obviously) and `;` (from `:`). If these characters appear in a relative URL then they are equivalent to their URL encoded form and so this change will be non breaking for that case.
---
Parsedown.php | 10 ++++++++--
test/data/inline_link.html | 2 +-
test/data/xss_bad_url.html | 32 ++++++++++++++++----------------
3 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 0bd81e2..702b041 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -87,7 +87,6 @@ class Parsedown
protected $safeLinksWhitelist = array(
'http://',
'https://',
- '/',
'ftp://',
'ftps://',
'mailto:',
@@ -1554,7 +1553,14 @@ class Parsedown
if ( ! $safe)
{
- unset($Element['attributes'][$attribute]);
+ $Element['attributes'][$attribute] = preg_replace_callback(
+ '/[^\/#?&=%]++/',
+ function (array $match)
+ {
+ return urlencode($match[0]);
+ },
+ $Element['attributes'][$attribute]
+ );
}
}
diff --git a/test/data/inline_link.html b/test/data/inline_link.html
index cef29cf..7a3131b 100644
--- a/test/data/inline_link.html
+++ b/test/data/inline_link.html
@@ -1,5 +1,5 @@
link
-link with parentheses in URL
+link with parentheses in URL
(link) in parentheses
link
diff --git a/test/data/xss_bad_url.html b/test/data/xss_bad_url.html
index 93dd0d8..8e43877 100644
--- a/test/data/xss_bad_url.html
+++ b/test/data/xss_bad_url.html
@@ -1,16 +1,16 @@
-xss
-xss
-xss
-xss
-
-
-
-
-xss
-xss
-xss
-xss
-
-
-
-
\ No newline at end of file
+xss
+xss
+xss
+xss
+
+
+
+
+xss
+xss
+xss
+xss
+
+
+
+
\ No newline at end of file
From dc30cb441c0834357b7f145444f6e53cbe67154e Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 5 May 2017 21:32:27 +0100
Subject: [PATCH 33/82] add more protocols to the whitelist
---
Parsedown.php | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Parsedown.php b/Parsedown.php
index 702b041..6ef12d3 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -93,6 +93,14 @@ class Parsedown
'data:image/png;base64,',
'data:image/gif;base64,',
'data:image/jpeg;base64,',
+ 'irc:',
+ 'ircs:',
+ 'git:',
+ 'ssh:',
+ 'ftp:',
+ 'ftps:',
+ 'news:',
+ 'steam:',
);
#
From 2e4afde68dae7f410af8c9e3a0664fcf97ce8a3d Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 5 May 2017 21:55:58 +0100
Subject: [PATCH 34/82] faster check substr at beginning of string
---
Parsedown.php | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 6ef12d3..096c399 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1533,7 +1533,7 @@ class Parsedown
unset($Element['attributes'][$att]);
}
# dump onevent attribute
- elseif (preg_match('/^on/i', $att))
+ elseif (self::striAtStart($att, 'on'))
{
unset($Element['attributes'][$att]);
}
@@ -1551,7 +1551,7 @@ class Parsedown
foreach ($this->safeLinksWhitelist as $scheme)
{
- if (stripos($Element['attributes'][$attribute], $scheme) === 0)
+ if (self::striAtStart($Element['attributes'][$attribute], $scheme))
{
$safe = true;
@@ -1584,6 +1584,20 @@ class Parsedown
return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
}
+ protected static function striAtStart($string, $needle)
+ {
+ $len = strlen($needle);
+
+ if ($len > strlen($string))
+ {
+ return false;
+ }
+ else
+ {
+ return strtolower(substr($string, 0, $len)) === strtolower($needle);
+ }
+ }
+
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
From 226f636360d29060f9d6d15ec47ce5de575a226c Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 7 May 2017 13:45:59 +0100
Subject: [PATCH 35/82] remove $safe flag
---
Parsedown.php | 26 ++++++++++----------------
1 file changed, 10 insertions(+), 16 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 096c399..42b8298 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1547,29 +1547,23 @@ class Parsedown
{
if ($this->safeLinksEnabled)
{
- $safe = false;
-
foreach ($this->safeLinksWhitelist as $scheme)
{
if (self::striAtStart($Element['attributes'][$attribute], $scheme))
{
- $safe = true;
-
- break;
+ return $Element;
}
}
- if ( ! $safe)
- {
- $Element['attributes'][$attribute] = preg_replace_callback(
- '/[^\/#?&=%]++/',
- function (array $match)
- {
- return urlencode($match[0]);
- },
- $Element['attributes'][$attribute]
- );
- }
+ $Element['attributes'][$attribute] = preg_replace_callback(
+ '/[^\/#?&=%]++/',
+ function (array $match)
+ {
+ return urlencode($match[0]);
+ },
+ $Element['attributes'][$attribute]
+ );
+
}
return $Element;
From c63b690a799cb6ae1641d345d33f67ed6efc6fb2 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 9 May 2017 14:50:15 +0100
Subject: [PATCH 36/82] remove duplicates
---
Parsedown.php | 2 --
1 file changed, 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 42b8298..0695c6f 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -97,8 +97,6 @@ class Parsedown
'ircs:',
'git:',
'ssh:',
- 'ftp:',
- 'ftps:',
'news:',
'steam:',
);
From b1e5aebaf6c8162cda482e96a1370155fd2162b5 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 9 May 2017 19:22:58 +0100
Subject: [PATCH 37/82] add single safeMode option that encompasses protection
from link destination xss and plain markup based xss into a single on/off
switch
---
Parsedown.php | 14 +++++++-------
test/ParsedownTest.php | 2 +-
test/data/inline_link.html | 2 +-
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 0695c6f..0dbf40c 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -75,14 +75,14 @@ class Parsedown
protected $urlsLinked = true;
- function setSafeLinksEnabled($safeLinksEnabled)
+ function setSafeMode($safeMode)
{
- $this->safeLinksEnabled = $safeLinksEnabled;
+ $this->safeMode = (bool) $safeMode;
return $this;
}
- protected $safeLinksEnabled = true;
+ protected $safeMode;
protected $safeLinksWhitelist = array(
'http://',
@@ -378,7 +378,7 @@ class Parsedown
protected function blockComment($Line)
{
- if ($this->markupEscaped)
+ if ($this->markupEscaped or $this->safeMode)
{
return;
}
@@ -700,7 +700,7 @@ class Parsedown
protected function blockMarkup($Line)
{
- if ($this->markupEscaped)
+ if ($this->markupEscaped or $this->safeMode)
{
return;
}
@@ -1282,7 +1282,7 @@ class Parsedown
protected function inlineMarkup($Excerpt)
{
- if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
+ if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
{
return;
}
@@ -1543,7 +1543,7 @@ class Parsedown
protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
{
- if ($this->safeLinksEnabled)
+ if ($this->safeMode)
{
foreach ($this->safeLinksWhitelist as $scheme)
{
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index 5fbf7f1..c7e3a82 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -46,7 +46,7 @@ class ParsedownTest extends PHPUnit_Framework_TestCase
$expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup);
$expectedMarkup = str_replace("\r", "\n", $expectedMarkup);
- $this->Parsedown->setMarkupEscaped($test === 'xss_text_encoding');
+ $this->Parsedown->setSafeMode(substr($test, 0, 3) === 'xss');
$actualMarkup = $this->Parsedown->text($markdown);
diff --git a/test/data/inline_link.html b/test/data/inline_link.html
index 7a3131b..cef29cf 100644
--- a/test/data/inline_link.html
+++ b/test/data/inline_link.html
@@ -1,5 +1,5 @@
link
-link with parentheses in URL
+link with parentheses in URL
(link) in parentheses
link
From bbb7687f31d6904f3a0e11e97bc61852a62cfe90 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 9 May 2017 19:31:36 +0100
Subject: [PATCH 38/82] safeMode will either apply all sanitisation techniques
to an element or none (note that encoding HTML entities is done regardless
because it speaks to character context, and that the only attributes/elements
we should permit are the ones we actually mean to create)
---
Parsedown.php | 33 ++++++++++++++++-----------------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 0dbf40c..c540d12 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1422,7 +1422,10 @@ class Parsedown
protected function element(array $Element)
{
- $Element = $this->sanitiseElement($Element);
+ if ($this->safeMode)
+ {
+ $Element = $this->sanitiseElement($Element);
+ }
$markup = '<'.$Element['name'];
@@ -1543,27 +1546,23 @@ class Parsedown
protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
{
- if ($this->safeMode)
+ foreach ($this->safeLinksWhitelist as $scheme)
{
- foreach ($this->safeLinksWhitelist as $scheme)
+ if (self::striAtStart($Element['attributes'][$attribute], $scheme))
{
- if (self::striAtStart($Element['attributes'][$attribute], $scheme))
- {
- return $Element;
- }
+ return $Element;
}
-
- $Element['attributes'][$attribute] = preg_replace_callback(
- '/[^\/#?&=%]++/',
- function (array $match)
- {
- return urlencode($match[0]);
- },
- $Element['attributes'][$attribute]
- );
-
}
+ $Element['attributes'][$attribute] = preg_replace_callback(
+ '/[^\/#?&=%]++/',
+ function (array $match)
+ {
+ return urlencode($match[0]);
+ },
+ $Element['attributes'][$attribute]
+ );
+
return $Element;
}
From 67c3efbea0d33c4433c6b18ed163b45c01395867 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 9 May 2017 19:37:13 +0100
Subject: [PATCH 39/82] according to
https://tools.ietf.org/html/rfc3986#section-3 the colon is a required part of
the syntax, other methods of achieving the colon character (as to browser
interpretation) should be taken care of by htmlencoding that is done on all
attribute content
---
Parsedown.php | 9 +--------
test/data/xss_bad_url.html | 32 ++++++++++++++++----------------
2 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index c540d12..110d6e3 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1554,14 +1554,7 @@ class Parsedown
}
}
- $Element['attributes'][$attribute] = preg_replace_callback(
- '/[^\/#?&=%]++/',
- function (array $match)
- {
- return urlencode($match[0]);
- },
- $Element['attributes'][$attribute]
- );
+ $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
return $Element;
}
diff --git a/test/data/xss_bad_url.html b/test/data/xss_bad_url.html
index 8e43877..0b216d1 100644
--- a/test/data/xss_bad_url.html
+++ b/test/data/xss_bad_url.html
@@ -1,16 +1,16 @@
-xss
-xss
-xss
-xss
-
-
-
-
-xss
-xss
-xss
-xss
-
-
-
-
\ No newline at end of file
+xss
+xss
+xss
+xss
+
+
+
+
+xss
+xss
+xss
+xss
+
+
+
+
\ No newline at end of file
From 44042011755697dd1b8972b6a79c2b87c759660e Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 20 Aug 2017 10:28:46 +0100
Subject: [PATCH 40/82] Properly support fenced code block infostring
Reference: http://spec.commonmark.org/0.28/#info-string
---
Parsedown.php | 6 +++---
test/data/fenced_code_block.html | 3 ++-
test/data/fenced_code_block.md | 4 ++++
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index f5dd0fa..00f61f3 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -396,7 +396,7 @@ class Parsedown
protected function blockFencedCode($Line)
{
- if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
+ if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches))
{
$Element = array(
'name' => 'code',
@@ -515,10 +515,10 @@ class Parsedown
),
);
- if($name === 'ol')
+ if($name === 'ol')
{
$listStart = stristr($matches[0], '.', true);
-
+
if($listStart !== '1')
{
$Block['element']['attributes'] = array('start' => $listStart);
diff --git a/test/data/fenced_code_block.html b/test/data/fenced_code_block.html
index 8bdabba..ce8dfd0 100644
--- a/test/data/fenced_code_block.html
+++ b/test/data/fenced_code_block.html
@@ -3,4 +3,5 @@
$message = 'fenced code block';
echo $message;
tilde
-echo 'language identifier';
\ No newline at end of file
+echo 'language identifier';
+echo 'language identifier with non words';
\ No newline at end of file
diff --git a/test/data/fenced_code_block.md b/test/data/fenced_code_block.md
index cbed8eb..9176ef4 100644
--- a/test/data/fenced_code_block.md
+++ b/test/data/fenced_code_block.md
@@ -11,4 +11,8 @@ tilde
```php
echo 'language identifier';
+```
+
+```c#
+echo 'language identifier with non words';
```
\ No newline at end of file
From 07c937583d56776b7cfd0564b542fa95a7765012 Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Sun, 22 Oct 2017 15:57:58 +0300
Subject: [PATCH 41/82] improve readme
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index ffe3116..0459e3c 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,8 @@ Better Markdown Parser in PHP
* Tested in 5.3 to 7.1 and in HHVM
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
+Note that when you deal with untrusted content (ex: user commnets) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
+
### Installation
Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown).
From 16aadff2ed7f03e3ea19bff15cac980e339a84b9 Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Sun, 22 Oct 2017 16:00:43 +0300
Subject: [PATCH 42/82] improve readme
---
README.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 0459e3c..9a2d56e 100644
--- a/README.md
+++ b/README.md
@@ -21,8 +21,6 @@ Better Markdown Parser in PHP
* Tested in 5.3 to 7.1 and in HHVM
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
-Note that when you deal with untrusted content (ex: user commnets) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
-
### Installation
Include `Parsedown.php` or install [the composer package](https://packagist.org/packages/erusev/parsedown).
@@ -37,6 +35,10 @@ echo $Parsedown->text('Hello _Parsedown_!'); # prints: Hello Parsedown
Date: Sun, 22 Oct 2017 16:01:34 +0300
Subject: [PATCH 43/82] improve readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9a2d56e..7073a95 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t
### Security
-Parsedownw does not sanitize the outut HTML. When you deal with untrusted content (ex: user commnets) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
+Parsedown does not sanitize the HTML that it generates. When you deal with untrusted content (ex: user commnets) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
### Questions
From af6affdc2c7dcdfb1c14bc642c374005193b10ca Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Mon, 6 Nov 2017 16:54:00 +0200
Subject: [PATCH 44/82] improve readme
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 7073a95..5f5c38e 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ Better Markdown Parser in PHP
### Features
* One File
+* No Dependencies
* Super Fast
* Extensible
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
From 691e36b1f241e7000fa36d176a2b13de4820b64c Mon Sep 17 00:00:00 2001
From: Gabriel Caruso
Date: Sat, 11 Nov 2017 00:56:03 -0200
Subject: [PATCH 45/82] Use PHPUnit\Framework\TestCase instead of
PHPUnit_Framework_TestCase
---
composer.json | 3 +++
phpunit.xml.dist | 2 +-
test/CommonMarkTest.php | 5 ++++-
test/ParsedownTest.php | 4 +++-
test/bootstrap.php | 4 ----
5 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/composer.json b/composer.json
index 28145af..b7f8aea 100644
--- a/composer.json
+++ b/composer.json
@@ -15,6 +15,9 @@
"require": {
"php": ">=5.3.0"
},
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35"
+ },
"autoload": {
"psr-0": {"Parsedown": ""}
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index b2d5e9d..dd9da22 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -5,4 +5,4 @@
test/ParsedownTest.php
-
\ No newline at end of file
+
diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php
index 9b8d116..7111f0b 100644
--- a/test/CommonMarkTest.php
+++ b/test/CommonMarkTest.php
@@ -8,7 +8,10 @@
* @link http://commonmark.org/ CommonMark
* @link http://git.io/8WtRvQ JavaScript test runner
*/
-class CommonMarkTest extends PHPUnit_Framework_TestCase
+
+use PHPUnit\Framework\TestCase;
+
+class CommonMarkTest extends TestCase
{
const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt';
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index 323dace..3b4c7d9 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -1,6 +1,8 @@
Date: Sat, 11 Nov 2017 01:02:11 -0200
Subject: [PATCH 46/82] Make Travis CI use installed PHPUnit version, not
global one
---
.travis.yml | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 6799ce6..58831c9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,5 +14,10 @@ matrix:
fast_finish: true
allow_failures:
- php: nightly
+ - php: 5.3
-sudo: false
\ No newline at end of file
+before_script:
+ - composer install --prefer-dist --no-interaction --no-progress
+
+script:
+ - vendor/bin/phpunit
From 09827f542c09ef729e34a01ff9eafa1c832f7559 Mon Sep 17 00:00:00 2001
From: Gabriel Caruso
Date: Tue, 14 Nov 2017 15:19:24 -0200
Subject: [PATCH 47/82] Rewrite Travis CI
---
.travis.yml | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 58831c9..cfb4d8d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,24 @@
language: php
-php:
- - 7.1
- - 7.0
- - 5.6
- - 5.5
- - 5.4
- - 5.3
- - hhvm
- - nightly
+dist: trusty
+sudo: false
matrix:
+ include:
+ - php: 5.3
+ dist: precise
+ - php: 5.4
+ - php: 5.5
+ - php: 5.6
+ - php: 7.0
+ - php: 7.1
+ - php: nightly
+ - php: hhvm
+ - php: hhvm-nightly
fast_finish: true
allow_failures:
- php: nightly
- - php: 5.3
+ - php: hhvm-nightly
before_script:
- composer install --prefer-dist --no-interaction --no-progress
From 089789dfff34a552e96998933803d75b230f465b Mon Sep 17 00:00:00 2001
From: John Bafford
Date: Tue, 14 Nov 2017 17:13:31 -0500
Subject: [PATCH 48/82] Fix typo in README
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5f5c38e..e1f2b53 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t
### Security
-Parsedown does not sanitize the HTML that it generates. When you deal with untrusted content (ex: user commnets) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
+Parsedown does not sanitize the HTML that it generates. When you deal with untrusted content (ex: user comments) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
### Questions
From d98d60aaf336ebdfbd612a51cce4dc748409d659 Mon Sep 17 00:00:00 2001
From: Miguel Piedrafita
Date: Sun, 31 Dec 2017 22:10:48 +0100
Subject: [PATCH 49/82] Update license year
---
LICENSE.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/LICENSE.txt b/LICENSE.txt
index baca86f..4552494 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2013 Emanuil Rusev, erusev.com
+Copyright (c) 2018 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
From 1244122b841f3930052a16c9f4ddd6858dc1060c Mon Sep 17 00:00:00 2001
From: Miguel Piedrafita
Date: Mon, 1 Jan 2018 14:09:31 +0100
Subject: [PATCH 50/82] Update LICENSE.txt
---
LICENSE.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/LICENSE.txt b/LICENSE.txt
index 4552494..8e7c764 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2018 Emanuil Rusev, erusev.com
+Copyright (c) 2013-2018 Emanuil Rusev, erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
From 0e1043a8d6bb5bae93bfc65c99bd109e7f486b7b Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 19 Feb 2017 16:12:04 +0000
Subject: [PATCH 51/82] consistent li items for loose list
---
Parsedown.php | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 757666e..877f5b8 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -515,10 +515,10 @@ class Parsedown
),
);
- if($name === 'ol')
+ if($name === 'ol')
{
$listStart = stristr($matches[0], '.', true);
-
+
if($listStart !== '1')
{
$Block['element']['attributes'] = array('start' => $listStart);
@@ -547,6 +547,8 @@ class Parsedown
{
$Block['li']['text'] []= '';
+ $Block['loose'] = true;
+
unset($Block['interrupted']);
}
@@ -595,6 +597,22 @@ class Parsedown
}
}
+ protected function blockListComplete(array $Block)
+ {
+ if (isset($Block['loose']))
+ {
+ foreach ($Block['element']['text'] as &$li)
+ {
+ if (end($li['text']) !== '')
+ {
+ $li['text'] []= '';
+ }
+ }
+ }
+
+ return $Block;
+ }
+
#
# Quote
From 7fd92a8fbd76dc9312dc7fb47fe05c8fb02fe3b7 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 19 Feb 2017 16:19:55 +0000
Subject: [PATCH 52/82] update tests
---
test/data/paragraph_list.html | 4 +++-
test/data/sparse_dense_list.html | 8 ++++++--
test/data/sparse_list.html | 4 +++-
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/test/data/paragraph_list.html b/test/data/paragraph_list.html
index ced1c43..00a612c 100644
--- a/test/data/paragraph_list.html
+++ b/test/data/paragraph_list.html
@@ -8,5 +8,7 @@
li
-li
+
+li
+
\ No newline at end of file
diff --git a/test/data/sparse_dense_list.html b/test/data/sparse_dense_list.html
index 095bc73..58923f8 100644
--- a/test/data/sparse_dense_list.html
+++ b/test/data/sparse_dense_list.html
@@ -2,6 +2,10 @@
li
-li
-li
+
+li
+
+
+li
+
\ No newline at end of file
diff --git a/test/data/sparse_list.html b/test/data/sparse_list.html
index 452b2b8..9803d27 100644
--- a/test/data/sparse_list.html
+++ b/test/data/sparse_list.html
@@ -2,7 +2,9 @@
li
-li
+
+li
+
From 722b776684eb10a0cb4e59c45b51156670cf6465 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Mon, 29 Jan 2018 14:25:00 +0100
Subject: [PATCH 53/82] Test multiple multiline lists
---
test/data/multiline_lists.html | 10 ++++++++++
test/data/multiline_lists.md | 5 +++++
2 files changed, 15 insertions(+)
create mode 100644 test/data/multiline_lists.html
create mode 100644 test/data/multiline_lists.md
diff --git a/test/data/multiline_lists.html b/test/data/multiline_lists.html
new file mode 100644
index 0000000..a223792
--- /dev/null
+++ b/test/data/multiline_lists.html
@@ -0,0 +1,10 @@
+
+-
+
One
+First body copy
+
+-
+
Two
+Last body copy
+
+
\ No newline at end of file
diff --git a/test/data/multiline_lists.md b/test/data/multiline_lists.md
new file mode 100644
index 0000000..6251115
--- /dev/null
+++ b/test/data/multiline_lists.md
@@ -0,0 +1,5 @@
+1. One
+ First body copy
+
+2. Two
+ Last body copy
From e69374af0d3bc849272c0a6eec09910f220d6136 Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Mon, 29 Jan 2018 20:52:27 +0200
Subject: [PATCH 54/82] improve readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e1f2b53..92d7202 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-> You might also like [Caret](https://caret.io?ref=parsedown) - our Markdown editor for Mac / Windows / Linux.
+> I also make [Caret](https://caret.io?ref=parsedown), a Markdown editor for Mac, Windows and Linux.
## Parsedown
From e938ab4ffe74ebf84afa43d8875ab418fcfc8e4d Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Mon, 29 Jan 2018 20:54:40 +0200
Subject: [PATCH 55/82] improve readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 92d7202..7bc3be1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-> I also make [Caret](https://caret.io?ref=parsedown), a Markdown editor for Mac, Windows and Linux.
+> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac, Windows and Linux.
## Parsedown
From c999a4b61bfc15d3eed74a40ce814afe6faa21ea Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Mon, 29 Jan 2018 20:55:30 +0200
Subject: [PATCH 56/82] improve readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7bc3be1..76b6905 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac, Windows and Linux.
+> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac and PC.
## Parsedown
From ad62bf5a6fc25052949f4ebba29375363045a009 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Wed, 28 Feb 2018 17:01:31 +0000
Subject: [PATCH 57/82] Talk about safe mode in the README
---
README.md | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 76b6905..68f216e 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,20 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t
### Security
-Parsedown does not sanitize the HTML that it generates. When you deal with untrusted content (ex: user comments) you should also use a HTML sanitizer like [HTML Purifier](http://htmlpurifier.org/).
+Parsedown is capable of escaping user-input within the HTML that it generates.
+Additionally Parsedown can attempt to sanitize additional scriping vectors (such
+as scripting link destinations). To tell Parsedown that it is processing untrusted
+user input, use the following:
+```php
+$parsedown = new Parsedown;
+$parsedown->setSafeMode(true);
+```
+
+It is recommended that when you deal with untrusted content (ex: user comments)
+you should employ defense-in-depth measures, like making use of a HTML sanitizer
+that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
+Additionally, you should strongly consider
+[deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/).
### Questions
From e2f3961f8092730980b0e09a8e78b66f81b62045 Mon Sep 17 00:00:00 2001
From: Hari KT
Date: Wed, 28 Feb 2018 23:25:38 +0530
Subject: [PATCH 58/82] Add test case to make sure issue 232 no longer exists
---
test/data/fenced_code_block.html | 6 +++++-
test/data/fenced_code_block.md | 7 +++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/test/data/fenced_code_block.html b/test/data/fenced_code_block.html
index ce8dfd0..565a541 100644
--- a/test/data/fenced_code_block.html
+++ b/test/data/fenced_code_block.html
@@ -4,4 +4,8 @@ $message = 'fenced code block';
echo $message;
tilde
echo 'language identifier';
-echo 'language identifier with non words';
\ No newline at end of file
+echo 'language identifier with non words';
+<?php
+echo "Hello World";
+?>
+<a href="http://auraphp.com" >Aura Project</a>
\ No newline at end of file
diff --git a/test/data/fenced_code_block.md b/test/data/fenced_code_block.md
index 9176ef4..62db24a 100644
--- a/test/data/fenced_code_block.md
+++ b/test/data/fenced_code_block.md
@@ -15,4 +15,11 @@ echo 'language identifier';
```c#
echo 'language identifier with non words';
+```
+
+```html+php
+
+Aura Project
```
\ No newline at end of file
From 096e16475639a3e934777f21715ac8152a03d136 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Wed, 28 Feb 2018 18:59:34 +0100
Subject: [PATCH 59/82] Update README.md
Sort "Who uses it" alphabetically, add Laravel + Pico
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 76b6905..2f65e56 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
**Who uses it?**
-[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [Kirby CMS](http://getkirby.com/), [Grav CMS](http://getgrav.org/), [Statamic CMS](http://www.statamic.com/), [Herbie CMS](http://www.getherbie.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
+[Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [Laravel](https://laravel.com/), [October CMS](http://octobercms.com/), [phpDocumentor](http://www.phpdoc.org/), [Pico](http://picocms.org), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
From cc53d5ae2959b3ea946a7b4f79b82cd55c7225c8 Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Wed, 28 Feb 2018 19:12:19 +0100
Subject: [PATCH 60/82] Travis: Issue build error when Parsedown::version isn't
up-to-date
---
.travis.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.travis.yml b/.travis.yml
index db5d725..b9361a6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,3 +26,4 @@ install:
script:
- vendor/bin/phpunit
- vendor/bin/phpunit test/CommonMarkTestWeak.php || true
+ - '[ -z "$TRAVIS_TAG" ] || [ "$TRAVIS_TAG" == "$(php -r "require(\"Parsedown.php\"); echo Parsedown::version;")" ]'
From fa89f0d743792dac266780d24f5ff7fe77c2ca6d Mon Sep 17 00:00:00 2001
From: Daniel Rudolf
Date: Wed, 28 Feb 2018 20:42:25 +0100
Subject: [PATCH 61/82] Add mbstring dependency to composer.json
---
composer.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/composer.json b/composer.json
index dc349d4..f8b40f8 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,8 @@
}
],
"require": {
- "php": ">=5.3.0"
+ "php": ">=5.3.0",
+ "ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
From 72d30d33bc126676bff7c13c27155bd3e45c3a9e Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Tue, 13 Jun 2017 20:28:32 +0100
Subject: [PATCH 62/82] allow element to have no name
---
Parsedown.php | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index e0ce3ac..cd93e9c 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1460,26 +1460,33 @@ class Parsedown
$Element = $this->sanitiseElement($Element);
}
- $markup = '<'.$Element['name'];
+ $hasName = isset($Element['name']);
- if (isset($Element['attributes']))
+ $markup = '';
+
+ if ($hasName)
{
- foreach ($Element['attributes'] as $name => $value)
- {
- if ($value === null)
- {
- continue;
- }
+ $markup .= '<'.$Element['name'];
- $markup .= ' '.$name.'="'.self::escape($value).'"';
+ if (isset($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $name => $value)
+ {
+ if ($value === null)
+ {
+ continue;
+ }
+
+ $markup .= ' '.$name.'="'.self::escape($value).'"';
+ }
}
}
if (isset($Element['text']))
{
- $markup .= '>';
+ $markup .= $hasName ? '>' : '';
- if (!isset($Element['nonNestables']))
+ if (!isset($Element['nonNestables']))
{
$Element['nonNestables'] = array();
}
@@ -1493,9 +1500,9 @@ class Parsedown
$markup .= self::escape($Element['text'], true);
}
- $markup .= ''.$Element['name'].'>';
+ $markup .= $hasName ? ''.$Element['name'].'>' : '';
}
- else
+ elseif ($hasName)
{
$markup .= ' />';
}
From 90439ef882500d98f581e725833bec370ece6bb2 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 1 Mar 2018 18:44:11 +0000
Subject: [PATCH 63/82] Rewrite section
---
README.md | 32 +++++++++++++++++++++++++-------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 68f216e..9bc3657 100644
--- a/README.md
+++ b/README.md
@@ -39,19 +39,37 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t
### Security
Parsedown is capable of escaping user-input within the HTML that it generates.
-Additionally Parsedown can attempt to sanitize additional scriping vectors (such
-as scripting link destinations). To tell Parsedown that it is processing untrusted
-user input, use the following:
+Additionally Parsedown will apply sanitisation to additional scripting vectors (such
+as scripting link destinations) that are introduced by the markdown syntax itself.
+To tell Parsedown that it is processing untrusted user-input, use the following:
```php
$parsedown = new Parsedown;
$parsedown->setSafeMode(true);
```
-It is recommended that when you deal with untrusted content (ex: user comments)
-you should employ defense-in-depth measures, like making use of a HTML sanitizer
+If instead, you wish to allow HTML within untrusted user input, but still want
+output to be free from XSS it is recommended that you make use of a HTML sanitiser
that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
-Additionally, you should strongly consider
-[deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/).
+
+In both cases you should strongly consider employing defence-in-depth measures,
+like [deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/)
+(making use of browser security feature) so that your page is likely to be safe even if an
+attacker finds a vulnerability in one of the first lines of defence above.
+
+#### Security of Parsedown Extensions
+
+Safe mode does not necessarily yield safe results when using extensions to Parsedown. Extensions should be evaluated on their own to determine their specific safety against XSS.
+
+### Escaping HTML
+> ⚠️ **WARNING:** This method isn't safe from XSS!
+
+If you wish to escape HTML **in trusted input**, you can use the following:
+```php
+$parsedown = new Parsedown;
+$parsedown->setMarkupEscaped(true);
+```
+
+Beware that this still allows users to insert unsafe scripting vectors, such as links like `[xss](javascript:alert%281%29)`.
### Questions
From 9b1f54b9d3bbe45b5a204a3a3c731fb5dccca695 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 1 Mar 2018 18:45:38 +0000
Subject: [PATCH 64/82] Lets be consistent with hyphenation
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9bc3657..b67a886 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ $parsedown = new Parsedown;
$parsedown->setSafeMode(true);
```
-If instead, you wish to allow HTML within untrusted user input, but still want
+If instead, you wish to allow HTML within untrusted user-input, but still want
output to be free from XSS it is recommended that you make use of a HTML sanitiser
that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
From f3068df45a80f98e96666682423b025c51cf301d Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 1 Mar 2018 19:54:58 +0000
Subject: [PATCH 65/82] Remove extra line breaks
---
README.md | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index b67a886..e950bd2 100644
--- a/README.md
+++ b/README.md
@@ -38,23 +38,17 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t
### Security
-Parsedown is capable of escaping user-input within the HTML that it generates.
-Additionally Parsedown will apply sanitisation to additional scripting vectors (such
-as scripting link destinations) that are introduced by the markdown syntax itself.
+Parsedown is capable of escaping user-input within the HTML that it generates. Additionally Parsedown will apply sanitisation to additional scripting vectors (such as scripting link destinations) that are introduced by the markdown syntax itself.
+
To tell Parsedown that it is processing untrusted user-input, use the following:
```php
$parsedown = new Parsedown;
$parsedown->setSafeMode(true);
```
-If instead, you wish to allow HTML within untrusted user-input, but still want
-output to be free from XSS it is recommended that you make use of a HTML sanitiser
-that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
+If instead, you wish to allow HTML within untrusted user-input, but still want output to be free from XSS it is recommended that you make use of a HTML sanitiser that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
-In both cases you should strongly consider employing defence-in-depth measures,
-like [deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/)
-(making use of browser security feature) so that your page is likely to be safe even if an
-attacker finds a vulnerability in one of the first lines of defence above.
+In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (making use of browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above.
#### Security of Parsedown Extensions
From 33b51eaefa8c0ef4d87b089493f5ed0df78f5108 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 2 Mar 2018 01:13:58 +0000
Subject: [PATCH 66/82] Fix typo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1b629e6..fc0419c 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ $parsedown->setSafeMode(true);
If instead, you wish to allow HTML within untrusted user-input, but still want output to be free from XSS it is recommended that you make use of a HTML sanitiser that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
-In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Secuity-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (making use of browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above.
+In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Security-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (a browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above.
#### Security of Parsedown Extensions
From e5bf9560d72cd38fcc5ad73d3f1a76faa94f1c1e Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Fri, 2 Mar 2018 17:37:16 +0200
Subject: [PATCH 67/82] add Laravel to who uses it
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index fc0419c..ed47c11 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
**Who uses it?**
-[Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [Laravel](https://laravel.com/), [October CMS](http://octobercms.com/), [phpDocumentor](http://www.phpdoc.org/), [Pico](http://picocms.org), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
+[Laravel](https://github.com/laravel/framework), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [Laravel](https://laravel.com/), [October CMS](http://octobercms.com/), [phpDocumentor](http://www.phpdoc.org/), [Pico](http://picocms.org), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
From a18bf495edd81d68e30e6c5e7f9c602d4e503cc5 Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Fri, 2 Mar 2018 17:40:21 +0200
Subject: [PATCH 68/82] refactor who uses it section in readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index ed47c11..c6006d4 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
**Who uses it?**
-[Laravel](https://github.com/laravel/framework), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [Laravel](https://laravel.com/), [October CMS](http://octobercms.com/), [phpDocumentor](http://www.phpdoc.org/), [Pico](http://picocms.org), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
+[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
From 253822057aae085f7ae8c405905743b17d7b04c9 Mon Sep 17 00:00:00 2001
From: Emanuil Rusev
Date: Fri, 2 Mar 2018 17:46:45 +0200
Subject: [PATCH 69/82] refactor who uses it section in readme a bit more
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index c6006d4..b5d9ed2 100644
--- a/README.md
+++ b/README.md
@@ -79,7 +79,7 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
**Who uses it?**
-[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Statamic CMS](http://www.statamic.com/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
+[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [Statamic CMS](http://www.statamic.com/), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
From ae7e8e50672dc5f16cf59097f7b147a0e26b2b55 Mon Sep 17 00:00:00 2001
From: "Luiz Paulo \"Bills"
Date: Wed, 7 Mar 2018 21:51:35 -0300
Subject: [PATCH 70/82] bump version
---
Parsedown.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index e0ce3ac..e3f045f 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -17,7 +17,7 @@ class Parsedown
{
# ~
- const version = '1.6.0';
+ const version = '1.7.0';
# ~
From 98573341861280a80cca9b6223e1540de3f1d6fb Mon Sep 17 00:00:00 2001
From: "Luiz Paulo \"Bills"
Date: Wed, 7 Mar 2018 22:04:55 -0300
Subject: [PATCH 71/82] bump version
---
Parsedown.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index e3f045f..87d612a 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -17,7 +17,7 @@ class Parsedown
{
# ~
- const version = '1.7.0';
+ const version = '1.7.1';
# ~
From f70d96479aa9ebca173c0e2829e8ee051f16c200 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 9 Mar 2018 16:48:32 +0000
Subject: [PATCH 72/82] Add test case for email surrounded by tags
---
test/data/email.html | 3 ++-
test/data/email.md | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/test/data/email.html b/test/data/email.html
index c40759c..93e0705 100644
--- a/test/data/email.html
+++ b/test/data/email.html
@@ -1 +1,2 @@
-my email is me@example.com
\ No newline at end of file
+my email is me@example.com
+html tags shouldn't start an email autolink first.last@example.com
\ No newline at end of file
diff --git a/test/data/email.md b/test/data/email.md
index 26b7b6c..00b6969 100644
--- a/test/data/email.md
+++ b/test/data/email.md
@@ -1 +1,3 @@
-my email is
\ No newline at end of file
+my email is
+
+html tags shouldn't start an email autolink first.last@example.com
\ No newline at end of file
From 721b885dd3d569b64377bed548efeb743ce5cbf7 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 9 Mar 2018 16:49:04 +0000
Subject: [PATCH 73/82] Fix #565 by validating email as defined in commonmark
spec
---
Parsedown.php | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 87d612a..685d6df 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1142,8 +1142,14 @@ class Parsedown
protected function inlineEmailTag($Excerpt)
{
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
- {
+ $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9]'
+ .'(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'
+ .'(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*'
+ ;
+
+ if (strpos($Excerpt['text'], '>') !== false
+ and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches)
+ ){
$url = $matches[1];
if ( ! isset($matches[2]))
@@ -1479,7 +1485,7 @@ class Parsedown
{
$markup .= '>';
- if (!isset($Element['nonNestables']))
+ if (!isset($Element['nonNestables']))
{
$Element['nonNestables'] = array();
}
From 19f1bb9353506f9c25c8b803333606cd6568cfc3 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 9 Mar 2018 16:54:21 +0000
Subject: [PATCH 74/82] Disable backtracking where the regex doesn't need it
---
Parsedown.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index 685d6df..27a35bf 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1142,7 +1142,7 @@ class Parsedown
protected function inlineEmailTag($Excerpt)
{
- $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9]'
+ $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@[a-zA-Z0-9]'
.'(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'
.'(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*'
;
From 6830c3339f70046183280ceaba83bcbc5c60634b Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Fri, 9 Mar 2018 17:38:41 +0000
Subject: [PATCH 75/82] Readability
Thanks @PhrozenByte for the suggestion :)
---
Parsedown.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 27a35bf..c0dbf08 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1142,10 +1142,10 @@ class Parsedown
protected function inlineEmailTag($Excerpt)
{
- $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@[a-zA-Z0-9]'
- .'(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'
- .'(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*'
- ;
+ $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';
+
+ $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@'
+ . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';
if (strpos($Excerpt['text'], '>') !== false
and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches)
From e6444bb57e60d56648ea60cf6b30f737c052e2c1 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 15 Mar 2018 10:42:29 +0000
Subject: [PATCH 76/82] Add unsafeHtml option for extensions to use on trusted
input
---
Parsedown.php | 21 +++++++++++++++++++--
test/ParsedownTest.php | 12 ++++++++++++
test/UnsafeExtension.php | 14 ++++++++++++++
3 files changed, 45 insertions(+), 2 deletions(-)
create mode 100644 test/UnsafeExtension.php
diff --git a/Parsedown.php b/Parsedown.php
index 2725170..b274f52 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1488,7 +1488,20 @@ class Parsedown
}
}
+ $unsafeHtml = false;
if (isset($Element['text']))
+ {
+ $text = $Element['text'];
+ }
+ // very strongly consider an alternative if you're writing an
+ // extension
+ elseif (isset($Element['unsafeHtml']) and !$this->safeMode)
+ {
+ $text = $Element['unsafeHtml'];
+ $unsafeHtml = true;
+ }
+
+ if (isset($text))
{
$markup .= $hasName ? '>' : '';
@@ -1499,11 +1512,15 @@ class Parsedown
if (isset($Element['handler']))
{
- $markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
+ $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
+ }
+ elseif ($unsafeHtml !== true or $this->safeMode)
+ {
+ $markup .= self::escape($text, true);
}
else
{
- $markup .= self::escape($Element['text'], true);
+ $markup .= $text;
}
$markup .= $hasName ? ''.$Element['name'].'>' : '';
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index c28cedf..3cd796e 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -1,4 +1,5 @@
assertEquals($expectedMarkup, $actualMarkup);
}
+ function testUnsafeHtml()
+ {
+ $markdown = "```php\nfoobar\n```";
+ $expectedMarkup = 'foobar
';
+
+ $unsafeExtension = new UnsafeExtension;
+ $actualMarkup = $unsafeExtension->text($markdown);
+
+ $this->assertEquals($expectedMarkup, $actualMarkup);
+ }
+
function data()
{
$data = array();
diff --git a/test/UnsafeExtension.php b/test/UnsafeExtension.php
new file mode 100644
index 0000000..f2343c4
--- /dev/null
+++ b/test/UnsafeExtension.php
@@ -0,0 +1,14 @@
+$text
";
+
+ return $Block;
+ }
+}
From e4c5be026d4c776261edf5e8ef802e258bfaee9f Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 15 Mar 2018 11:00:03 +0000
Subject: [PATCH 77/82] Further attempt to dissuade this feature's use
---
test/UnsafeExtension.php | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test/UnsafeExtension.php b/test/UnsafeExtension.php
index f2343c4..9a8dcc7 100644
--- a/test/UnsafeExtension.php
+++ b/test/UnsafeExtension.php
@@ -7,6 +7,11 @@ class UnsafeExtension extends Parsedown
$text = $Block['element']['text']['text'];
unset($Block['element']['text']['text']);
+ // WARNING: There is almost always a better way of doing things!
+ //
+ // This example is one of them, unsafe behaviour is NOT needed here.
+ // Only use this if you trust the input and have no idea what
+ // the output HTML will look like (e.g. using an external parser).
$Block['element']['text']['unsafeHtml'] = "$text
";
return $Block;
From ef7ed7b66cf22b268c459a98e4fe3f7f809d40b5 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 15 Mar 2018 11:09:55 +0000
Subject: [PATCH 78/82] Still grab the text if safe mode enabled, but output it
escaped
---
Parsedown.php | 3 ++-
test/ParsedownTest.php | 6 ++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/Parsedown.php b/Parsedown.php
index b274f52..9558525 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1495,9 +1495,10 @@ class Parsedown
}
// very strongly consider an alternative if you're writing an
// extension
- elseif (isset($Element['unsafeHtml']) and !$this->safeMode)
+ elseif (isset($Element['unsafeHtml']))
{
$text = $Element['unsafeHtml'];
+
$unsafeHtml = true;
}
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index 3cd796e..8f3e6c8 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -60,11 +60,17 @@ class ParsedownTest extends TestCase
{
$markdown = "```php\nfoobar\n```";
$expectedMarkup = 'foobar
';
+ $expectedSafeMarkup = '<p>foobar</p>
';
$unsafeExtension = new UnsafeExtension;
$actualMarkup = $unsafeExtension->text($markdown);
$this->assertEquals($expectedMarkup, $actualMarkup);
+
+ $unsafeExtension->setSafeMode(true);
+ $actualSafeMarkup = $unsafeExtension->text($markdown);
+
+ $this->assertEquals($expectedSafeMarkup, $actualSafeMarkup);
}
function data()
From 3fc54bc966caea29a633dba41ebb6728a917ee67 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 15 Mar 2018 19:46:03 +0000
Subject: [PATCH 79/82] Allow extension to "vouch" for raw HTML they produce
Rename "unsafeHtml" to "rawHtml"
---
Parsedown.php | 25 ++++++++++++++++++++-----
test/ParsedownTest.php | 21 +++++++++++++++++++--
test/SampleExtensions.php | 39 +++++++++++++++++++++++++++++++++++++++
test/UnsafeExtension.php | 19 -------------------
4 files changed, 78 insertions(+), 26 deletions(-)
create mode 100644 test/SampleExtensions.php
delete mode 100644 test/UnsafeExtension.php
diff --git a/Parsedown.php b/Parsedown.php
index 9558525..160594e 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1488,18 +1488,33 @@ class Parsedown
}
}
- $unsafeHtml = false;
+ $permitRawHtml = false;
+
if (isset($Element['text']))
{
$text = $Element['text'];
}
// very strongly consider an alternative if you're writing an
// extension
- elseif (isset($Element['unsafeHtml']))
+ elseif (isset($Element['rawHtml']))
{
- $text = $Element['unsafeHtml'];
+ $text = $Element['rawHtml'];
- $unsafeHtml = true;
+ $allowRawHtmlInSafeMode = false;
+
+ if (isset($Element['allowRawHtmlInSafeMode']))
+ {
+ $allowRawHtmlInSafeMode = (true === $Element['allowRawHtmlInSafeMode']);
+ }
+
+ if ($this->safeMode !== true)
+ {
+ $permitRawHtml = true;
+ }
+ elseif ($this->safeMode and $allowRawHtmlInSafeMode)
+ {
+ $permitRawHtml = true;
+ }
}
if (isset($text))
@@ -1515,7 +1530,7 @@ class Parsedown
{
$markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
}
- elseif ($unsafeHtml !== true or $this->safeMode)
+ elseif ($permitRawHtml !== true)
{
$markup .= self::escape($text, true);
}
diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php
index 8f3e6c8..cc0cc1d 100644
--- a/test/ParsedownTest.php
+++ b/test/ParsedownTest.php
@@ -1,5 +1,5 @@
assertEquals($expectedMarkup, $actualMarkup);
}
- function testUnsafeHtml()
+ function testRawHtml()
{
$markdown = "```php\nfoobar\n```";
$expectedMarkup = 'foobar
';
@@ -73,6 +73,23 @@ class ParsedownTest extends TestCase
$this->assertEquals($expectedSafeMarkup, $actualSafeMarkup);
}
+ function testTrustDelegatedRawHtml()
+ {
+ $markdown = "```php\nfoobar\n```";
+ $expectedMarkup = 'foobar
';
+ $expectedSafeMarkup = $expectedMarkup;
+
+ $unsafeExtension = new TrustDelegatedExtension;
+ $actualMarkup = $unsafeExtension->text($markdown);
+
+ $this->assertEquals($expectedMarkup, $actualMarkup);
+
+ $unsafeExtension->setSafeMode(true);
+ $actualSafeMarkup = $unsafeExtension->text($markdown);
+
+ $this->assertEquals($expectedSafeMarkup, $actualSafeMarkup);
+ }
+
function data()
{
$data = array();
diff --git a/test/SampleExtensions.php b/test/SampleExtensions.php
new file mode 100644
index 0000000..6d7ec9f
--- /dev/null
+++ b/test/SampleExtensions.php
@@ -0,0 +1,39 @@
+$text";
+
+ return $Block;
+ }
+}
+
+
+class TrustDelegatedExtension extends Parsedown
+{
+ protected function blockFencedCodeComplete($Block)
+ {
+ $text = $Block['element']['text']['text'];
+ unset($Block['element']['text']['text']);
+
+ // WARNING: There is almost always a better way of doing things!
+ //
+ // This example is one of them, unsafe behaviour is NOT needed here.
+ // Only use this if you trust the input and have no idea what
+ // the output HTML will look like (e.g. using an external parser).
+ $Block['element']['text']['rawHtml'] = "$text
";
+ $Block['element']['text']['allowRawHtmlInSafeMode'] = true;
+
+ return $Block;
+ }
+}
diff --git a/test/UnsafeExtension.php b/test/UnsafeExtension.php
deleted file mode 100644
index 9a8dcc7..0000000
--- a/test/UnsafeExtension.php
+++ /dev/null
@@ -1,19 +0,0 @@
-$text";
-
- return $Block;
- }
-}
From 624a08b7eb8c3a4b0b99e7175251b76483e387cf Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Thu, 15 Mar 2018 19:55:33 +0000
Subject: [PATCH 80/82] Update commment
---
test/SampleExtensions.php | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/test/SampleExtensions.php b/test/SampleExtensions.php
index 6d7ec9f..c66190c 100644
--- a/test/SampleExtensions.php
+++ b/test/SampleExtensions.php
@@ -28,9 +28,10 @@ class TrustDelegatedExtension extends Parsedown
// WARNING: There is almost always a better way of doing things!
//
- // This example is one of them, unsafe behaviour is NOT needed here.
- // Only use this if you trust the input and have no idea what
- // the output HTML will look like (e.g. using an external parser).
+ // This behaviour is NOT needed in the demonstrated case.
+ // Only use this if you are sure that the result being added into
+ // rawHtml is safe.
+ // (e.g. using an external parser with escaping capabilities).
$Block['element']['text']['rawHtml'] = "$text
";
$Block['element']['text']['allowRawHtmlInSafeMode'] = true;
From 88dc94989039d37e09f186e94ec3a3964af82b36 Mon Sep 17 00:00:00 2001
From: Aidan Woods
Date: Sun, 18 Mar 2018 19:42:14 +0000
Subject: [PATCH 81/82] Refactor based on suggestion by @PhrozenByte
---
Parsedown.php | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/Parsedown.php b/Parsedown.php
index 160594e..bf08700 100644
--- a/Parsedown.php
+++ b/Parsedown.php
@@ -1500,21 +1500,8 @@ class Parsedown
{
$text = $Element['rawHtml'];
- $allowRawHtmlInSafeMode = false;
-
- if (isset($Element['allowRawHtmlInSafeMode']))
- {
- $allowRawHtmlInSafeMode = (true === $Element['allowRawHtmlInSafeMode']);
- }
-
- if ($this->safeMode !== true)
- {
- $permitRawHtml = true;
- }
- elseif ($this->safeMode and $allowRawHtmlInSafeMode)
- {
- $permitRawHtml = true;
- }
+ $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
+ $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
}
if (isset($text))
@@ -1530,7 +1517,7 @@ class Parsedown
{
$markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
}
- elseif ($permitRawHtml !== true)
+ elseif (!$permitRawHtml)
{
$markup .= self::escape($text, true);
}
From 972648ff64228ee4c775febef962e863d16c853e Mon Sep 17 00:00:00 2001
From: Carsten Brandt
Date: Tue, 20 Mar 2018 16:56:40 +0100
Subject: [PATCH 82/82] Added inline example to README
see https://github.com/erusev/parsedown/issues/562
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index b5d9ed2..9a4bb36 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,8 @@ Include `Parsedown.php` or install [the composer package](https://packagist.org/
$Parsedown = new Parsedown();
echo $Parsedown->text('Hello _Parsedown_!'); # prints: Hello Parsedown!
+// you can also parse inline markdown only
+echo $Parsedown->line('Hello _Parsedown_!'); # prints: Hello Parsedown!
```
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI).