From b5951e08c6886d71087c44398be22f7afba91ff6 Mon Sep 17 00:00:00 2001 From: Adrien Loison Date: Fri, 24 Jun 2016 14:18:01 +0200 Subject: [PATCH 001/106] Create .gitattributes When using this library, all the files related to tests can be ignored. Tests are only useful when working on the library itself. --- .gitattributes | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..001c45c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Ignore all tests for archive +/test export-ignore +/.gitattributes export-ignore +/.travis.yml export-ignore +/phpunit.xml.dist export-ignore From e1bcc1c47279a945af7e04e7f5c1f3615b01a825 Mon Sep 17 00:00:00 2001 From: Daniel Rudolf Date: Mon, 5 Sep 2016 04:51:28 +0200 Subject: [PATCH 002/106] 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 003/106] 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 004/106] 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 005/106] 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 006/106] 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 007/106] 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 008/106] 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 009/106] 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 010/106] =?UTF-8?q?Fix=20Issue=20#358=20=E2=80=93=20preven?= =?UTF-8?q?ting=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 011/106] 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 012/106] 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 013/106] 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 014/106] 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 015/106] 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 016/106] 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 017/106] 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 018/106] 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 019/106] 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 020/106] 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 8876c0984e64ace957f3e38abfc3b3961bc40b88 Mon Sep 17 00:00:00 2001 From: James Vickery Date: Tue, 25 Oct 2016 15:10:22 +0100 Subject: [PATCH 021/106] Tiny grammar correction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ef230f..4ce37c5 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t **How does Parsedown work?** -It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belong to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). +It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belongs to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages. From 7a92a31739be3401e6cd961d548398dcc67bc064 Mon Sep 17 00:00:00 2001 From: James Vickery Date: Tue, 25 Oct 2016 15:22:17 +0100 Subject: [PATCH 022/106] Grammar update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ce37c5..7907787 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [t **How does Parsedown work?** -It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belongs to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). +It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line starts with a `-` then perhaps it belongs to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines). We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages. From bc21988fe5e6cca8aae6928c0374779fd82710ed Mon Sep 17 00:00:00 2001 From: Yoan Blanc Date: Tue, 1 Nov 2016 18:27:37 +0100 Subject: [PATCH 023/106] Fix include from ParsedownTest I wasn't able to run all the tests from ParsedownExtra because of it. --- test/ParsedownTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php index c922ab1..323dace 100644 --- a/test/ParsedownTest.php +++ b/test/ParsedownTest.php @@ -139,7 +139,7 @@ EXPECTED_HTML; public function testLateStaticBinding() { - include 'test/TestParsedown.php'; + include __DIR__ . '/TestParsedown.php'; $parsedown = Parsedown::instance(); $this->assertInstanceOf('Parsedown', $parsedown); From 48351504deb0336e265a9453cf9acab04fadae6d Mon Sep 17 00:00:00 2001 From: gene_sis Date: Fri, 6 Jan 2017 20:40:19 +0100 Subject: [PATCH 024/106] adjust two regex pattern within inlineLink() to reduce backtracking add test with base64 image --- Parsedown.php | 4 ++-- test/data/inline_link.html | 3 ++- test/data/inline_link.md | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 4737eee..d7b5537 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1204,7 +1204,7 @@ class Parsedown $remainder = $Excerpt['text']; - if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) { $Element['text'] = $matches[1]; @@ -1217,7 +1217,7 @@ class Parsedown return; } - if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) + if (preg_match('/^[(]((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) { $Element['attributes']['href'] = $matches[1]; diff --git a/test/data/inline_link.html b/test/data/inline_link.html index 5ad564a..cef29cf 100644 --- a/test/data/inline_link.html +++ b/test/data/inline_link.html @@ -3,4 +3,5 @@

(link) in parentheses

link

MD Logo

-

MD Logo and text

\ No newline at end of file +

MD Logo and text

+

MD Logo and text

\ No newline at end of file diff --git a/test/data/inline_link.md b/test/data/inline_link.md index 6bac0b3..1ba24b7 100644 --- a/test/data/inline_link.md +++ b/test/data/inline_link.md @@ -8,4 +8,6 @@ [![MD Logo](http://parsedown.org/md.png)](http://example.com) -[![MD Logo](http://parsedown.org/md.png) and text](http://example.com) \ No newline at end of file +[![MD Logo](http://parsedown.org/md.png) and text](http://example.com) + +[![MD Logo](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAYAAADskT9PAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEImlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MTwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6Q29tcHJlc3Npb24+NTwvdGlmZjpDb21wcmVzc2lvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjcyPC90aWZmOllSZXNvbHV0aW9uPgogICAgICAgICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MzI8L2V4aWY6UGl4ZWxYRGltZW5zaW9uPgogICAgICAgICA8ZXhpZjpDb2xvclNwYWNlPjE8L2V4aWY6Q29sb3JTcGFjZT4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjIwPC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGRjOnN1YmplY3Q+CiAgICAgICAgICAgIDxyZGY6QmFnLz4KICAgICAgICAgPC9kYzpzdWJqZWN0PgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxNS0wNi0xNFQxOTowNjo1OTwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciAzLjI8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Ch7v5WoAAAGgSURBVEgNYywtLbVnYmLqYmBgMANieoJT//79K2MBWr4CaKsEPW2G2mUGspsFZnlnZycjPR1RXl7+H2Q3Ez0txWbXgDsAFAUYABo8YPH////HdXV1LUZWVFZWFsvIyLgIJoYt+pDNwCYP00swBIAWzaysrNSCaQCxgWLTYHxKaawhgGYoJzC7rC4sLDQBiYPYQIoHTQ3ZXGIcADJci42NDeZreGiQbSuSRmKiABb/CUB9IMwAjAKYGIhLESAYAj9//kwH+t4YaAvM59c4ODiyvn//HotuMzDh9QLFirCIg/I8CPQBE2QxhAkhCYZAf3//d2CJFQpU/h2EQeyGhoYvyIbA2FDDl8H4aPQydMtB8gQdAFLU3t5+DRjsWSAMYoPEcAFOTs5EoNw+NPl9UHE0YQYGglEA09HR0bEAxsZHA0PnFzAqgoBq9gIxKOrOAnEQSBxIYwCiQgBDFwEBYFB/BEaVJ7AQ2wGiQXxcWhhhJRZQ0UBURsSlAVyup4Y4TaKAFIeBouAJUIM0KZqoqPYpEzBrpQANfEFFQ4k16gXIbgCggnKoJ5DJdwAAAABJRU5ErkJggg==) and text](http://example.com) \ No newline at end of file From 0172d779d73d8d7ad94f9c486f8560a77d184389 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sat, 21 Jan 2017 11:06:41 +0000 Subject: [PATCH 025/106] Trim surrounding whitespace from URL in inlineLink Fixes https://github.com/erusev/parsedown-extra/issues/103 --- Parsedown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index d7b5537..3e25b0d 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1217,7 +1217,7 @@ class Parsedown return; } - if (preg_match('/^[(]((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) { $Element['attributes']['href'] = $matches[1]; From 7081afe8cbf4081b49839c6789f45b2ef508ce5b Mon Sep 17 00:00:00 2001 From: Marek Skiba Date: Thu, 2 Mar 2017 12:43:51 +0100 Subject: [PATCH 026/106] Removed double semicolon --- Parsedown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parsedown.php b/Parsedown.php index 3e25b0d..20863a7 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -448,7 +448,7 @@ class Parsedown return $Block; } - $Block['element']['text']['text'] .= "\n".$Line['body'];; + $Block['element']['text']['text'] .= "\n".$Line['body']; return $Block; } From bd0e31a7ddfa3c45744599077b4c6b97ca2fbeb7 Mon Sep 17 00:00:00 2001 From: Haralan Dobrev Date: Fri, 10 Mar 2017 01:04:53 +0200 Subject: [PATCH 027/106] Add Symfony demo to "Who uses it?" https://github.com/symfony/symfony-demo/blob/409a65b373cb47bc346742c452af5c7b1424f5cd/composer.json#L24 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7907787..4e5659a 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,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/) and [more](https://packagist.org/packages/erusev/parsedown/dependents). +[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). **How can I help?** From 0a09d5ad45b0f077f67493131e4e3f3ccde14389 Mon Sep 17 00:00:00 2001 From: Emanuil Rusev Date: Thu, 23 Mar 2017 20:21:05 +0200 Subject: [PATCH 028/106] update tests to reflect changes in phpunit 6.0 --- test/CommonMarkTest.php | 2 +- test/ParsedownTest.php | 2 +- test/bootstrap.php | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php index 9b8d116..37ed9fe 100644 --- a/test/CommonMarkTest.php +++ b/test/CommonMarkTest.php @@ -8,7 +8,7 @@ * @link http://commonmark.org/ CommonMark * @link http://git.io/8WtRvQ JavaScript test runner */ -class CommonMarkTest extends PHPUnit_Framework_TestCase +class CommonMarkTest extends \PHPUnit\Framework\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..7e552e8 100644 --- a/test/ParsedownTest.php +++ b/test/ParsedownTest.php @@ -1,6 +1,6 @@ Date: Wed, 29 Mar 2017 19:04:15 +0300 Subject: [PATCH 029/106] add kbd to text-level elements --- Parsedown.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index 20863a7..f5dd0fa 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -1539,10 +1539,10 @@ class Parsedown 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', 'i', 'rp', 'del', 'code', 'strike', 'marquee', 'q', 'rt', 'ins', 'font', 'strong', - 's', 'tt', 'sub', 'mark', - 'u', 'xm', 'sup', 'nobr', - 'var', 'ruby', - 'wbr', 'span', - 'time', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', ); } From 4367f89a74ecd7414d01f0411a8be6dba7071b08 Mon Sep 17 00:00:00 2001 From: Emanuil Rusev Date: Wed, 29 Mar 2017 19:30:24 +0300 Subject: [PATCH 030/106] attempt to fix failing builds on 5.3 --- test/CommonMarkTest.php | 2 +- test/ParsedownTest.php | 2 +- test/bootstrap.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/CommonMarkTest.php b/test/CommonMarkTest.php index 37ed9fe..9b8d116 100644 --- a/test/CommonMarkTest.php +++ b/test/CommonMarkTest.php @@ -8,7 +8,7 @@ * @link http://commonmark.org/ CommonMark * @link http://git.io/8WtRvQ JavaScript test runner */ -class CommonMarkTest extends \PHPUnit\Framework\TestCase +class CommonMarkTest extends PHPUnit_Framework_TestCase { const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt'; diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php index 7e552e8..323dace 100644 --- a/test/ParsedownTest.php +++ b/test/ParsedownTest.php @@ -1,6 +1,6 @@ Date: Sat, 25 Mar 2017 14:28:43 +0000 Subject: [PATCH 031/106] blockmarkup ends on interrupt by newline (CommonMark compliance) --- Parsedown.php | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index f5dd0fa..e516f6b 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -683,7 +683,7 @@ class Parsedown return; } - if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + if (preg_match('/^<[\/]?+(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) { $element = strtolower($matches[1]); @@ -694,7 +694,6 @@ class Parsedown $Block = array( 'name' => $matches[1], - 'depth' => 0, 'markup' => $Line['text'], ); @@ -730,35 +729,11 @@ class Parsedown protected function blockMarkupContinue($Line, array $Block) { - if (isset($Block['closed'])) + if (isset($Block['closed']) or isset($Block['interrupted'])) { return; } - if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open - { - $Block['depth'] ++; - } - - if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close - { - if ($Block['depth'] > 0) - { - $Block['depth'] --; - } - else - { - $Block['closed'] = true; - } - } - - if (isset($Block['interrupted'])) - { - $Block['markup'] .= "\n"; - - unset($Block['interrupted']); - } - $Block['markup'] .= "\n".$Line['body']; return $Block; From 1d0af35f10ed6b7e2a5841d032ac6a1de5b6f939 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sat, 25 Mar 2017 14:47:36 +0000 Subject: [PATCH 032/106] update test to result generated by CommonMark reference parser --- test/data/sparse_html.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/data/sparse_html.html b/test/data/sparse_html.html index 9e89627..6e0213f 100644 --- a/test/data/sparse_html.html +++ b/test/data/sparse_html.html @@ -1,8 +1,6 @@
line 1 - -line 2 -line 3 - -line 4 +

line 2 +line 3

+

line 4

\ No newline at end of file From 1140613fc7aa1393a6274812deab253b90027ae7 Mon Sep 17 00:00:00 2001 From: naNuke Date: Wed, 21 Jan 2015 03:50:36 +0100 Subject: [PATCH 033/106] 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 034/106] 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 035/106] 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 036/106] 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 037/106] 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 .= ''; @@ -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 038/106] 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

+

xss

+

xss

+

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

+

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 924b26e16c3af33f152f95ecb7b0a19ebdc086cd Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 1 May 2017 03:53:29 +0100 Subject: [PATCH 039/106] replace hhvm nightly with nightly --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa5ca98..e5f1603 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,9 @@ php: - 5.4 - 5.3 - hhvm - - hhvm-nightly + - nightly matrix: fast_finish: true allow_failures: - - php: hhvm-nightly + - php: nightly From 131ba758514c5e905663bd82fc7c1c8271c7edc5 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 1 May 2017 15:44:04 +0100 Subject: [PATCH 040/106] 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 041/106] 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 042/106] 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 043/106] 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 044/106] 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 045/106] 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 046/106] 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

MD Logo

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

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

\ No newline at end of file +

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

\ No newline at end of file From f76b10aaab9ff23a926323450885d0cd224fd147 Mon Sep 17 00:00:00 2001 From: Emanuil Rusev Date: Thu, 4 May 2017 10:28:55 +0300 Subject: [PATCH 047/106] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e5659a..ffe3116 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> You might also like [Caret](http://caret.io?ref=parsedown) - our Markdown editor for Mac / Windows / Linux. +> You might also like [Caret](https://caret.io?ref=parsedown) - our Markdown editor for Mac / Windows / Linux. ## Parsedown From dc30cb441c0834357b7f145444f6e53cbe67154e Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 5 May 2017 21:32:27 +0100 Subject: [PATCH 048/106] 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 049/106] 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 050/106] 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 051/106] 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 052/106] 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

MD Logo

From bbb7687f31d6904f3a0e11e97bc61852a62cfe90 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Tue, 9 May 2017 19:31:36 +0100 Subject: [PATCH 053/106] 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 054/106] 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

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

-

xss

\ No newline at end of file +

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

+

xss

\ No newline at end of file From c82af01bd6e97fb5c39aaf2f6a41b6d55c9d66e4 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 14 May 2017 14:36:55 +0100 Subject: [PATCH 055/106] add sudo false --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index e5f1603..6799ce6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,3 +14,5 @@ matrix: fast_finish: true allow_failures: - php: nightly + +sudo: false \ No newline at end of file From be963a6531001b7d87083fb3f77b0df525ebc8db Mon Sep 17 00:00:00 2001 From: Daniel Berthereau Date: Mon, 19 Jun 2017 00:00:00 +0200 Subject: [PATCH 056/106] Added tests for consistency when a markdown follows a markup without blank line. --- test/ParsedownTest.php | 3 ++- test/data/markup_consecutive_one.html | 3 +++ test/data/markup_consecutive_one.md | 4 ++++ test/data/markup_consecutive_one_line.html | 4 ++++ test/data/markup_consecutive_one_line.md | 5 +++++ test/data/markup_consecutive_one_stripped.html | 3 +++ test/data/markup_consecutive_one_stripped.md | 4 ++++ test/data/markup_consecutive_two.html | 3 +++ test/data/markup_consecutive_two.md | 4 ++++ test/data/markup_consecutive_two_lines.html | 4 ++++ test/data/markup_consecutive_two_lines.md | 5 +++++ test/data/markup_consecutive_two_stripped.html | 4 ++++ test/data/markup_consecutive_two_stripped.md | 5 +++++ 13 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/data/markup_consecutive_one.html create mode 100644 test/data/markup_consecutive_one.md create mode 100644 test/data/markup_consecutive_one_line.html create mode 100644 test/data/markup_consecutive_one_line.md create mode 100644 test/data/markup_consecutive_one_stripped.html create mode 100644 test/data/markup_consecutive_one_stripped.md create mode 100644 test/data/markup_consecutive_two.html create mode 100644 test/data/markup_consecutive_two.md create mode 100644 test/data/markup_consecutive_two_lines.html create mode 100644 test/data/markup_consecutive_two_lines.md create mode 100644 test/data/markup_consecutive_two_stripped.html create mode 100644 test/data/markup_consecutive_two_stripped.md diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php index 323dace..bef612b 100644 --- a/test/ParsedownTest.php +++ b/test/ParsedownTest.php @@ -10,7 +10,8 @@ class ParsedownTest extends PHPUnit_Framework_TestCase parent::__construct($name, $data, $dataName); } - private $dirs, $Parsedown; + private $dirs; + protected $Parsedown; /** * @return array diff --git a/test/data/markup_consecutive_one.html b/test/data/markup_consecutive_one.html new file mode 100644 index 0000000..a0b1fca --- /dev/null +++ b/test/data/markup_consecutive_one.html @@ -0,0 +1,3 @@ +
Markup
+

No markdown without blank line if strict compliance, but main processors convert it.

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one.md b/test/data/markup_consecutive_one.md new file mode 100644 index 0000000..7a00770 --- /dev/null +++ b/test/data/markup_consecutive_one.md @@ -0,0 +1,4 @@ +
Markup
+_No markdown_ without blank line if strict compliance, but **main** processors convert it. + +**Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_one_line.html b/test/data/markup_consecutive_one_line.html new file mode 100644 index 0000000..2a301aa --- /dev/null +++ b/test/data/markup_consecutive_one_line.html @@ -0,0 +1,4 @@ +
One markup on +two lines
+

Markdown

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one_line.md b/test/data/markup_consecutive_one_line.md new file mode 100644 index 0000000..d40609a --- /dev/null +++ b/test/data/markup_consecutive_one_line.md @@ -0,0 +1,5 @@ +
One markup on +two lines
+_Markdown_ + +**Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_one_stripped.html b/test/data/markup_consecutive_one_stripped.html new file mode 100644 index 0000000..eb5136b --- /dev/null +++ b/test/data/markup_consecutive_one_stripped.html @@ -0,0 +1,3 @@ +

Stripped markup

+

Markdown

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one_stripped.md b/test/data/markup_consecutive_one_stripped.md new file mode 100644 index 0000000..4189580 --- /dev/null +++ b/test/data/markup_consecutive_one_stripped.md @@ -0,0 +1,4 @@ +

Stripped markup

+_Markdown_ + +**Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two.html b/test/data/markup_consecutive_two.html new file mode 100644 index 0000000..efcebcd --- /dev/null +++ b/test/data/markup_consecutive_two.html @@ -0,0 +1,3 @@ +
First markup

and second markup on the same line.

+

Markdown

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two.md b/test/data/markup_consecutive_two.md new file mode 100644 index 0000000..bd663ac --- /dev/null +++ b/test/data/markup_consecutive_two.md @@ -0,0 +1,4 @@ +
First markup

and second markup on the same line.

+_Markdown_ + +**Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two_lines.html b/test/data/markup_consecutive_two_lines.html new file mode 100644 index 0000000..cfa1463 --- /dev/null +++ b/test/data/markup_consecutive_two_lines.html @@ -0,0 +1,4 @@ +
First markup

and partial markup +on two lines.

+

Markdown

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two_lines.md b/test/data/markup_consecutive_two_lines.md new file mode 100644 index 0000000..66318e9 --- /dev/null +++ b/test/data/markup_consecutive_two_lines.md @@ -0,0 +1,5 @@ +
First markup

and partial markup +on two lines.

+_Markdown_ + +**Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two_stripped.html b/test/data/markup_consecutive_two_stripped.html new file mode 100644 index 0000000..1633678 --- /dev/null +++ b/test/data/markup_consecutive_two_stripped.html @@ -0,0 +1,4 @@ +

Stripped markup +on two lines

+

Markdown

+

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two_stripped.md b/test/data/markup_consecutive_two_stripped.md new file mode 100644 index 0000000..8173380 --- /dev/null +++ b/test/data/markup_consecutive_two_stripped.md @@ -0,0 +1,5 @@ +

Stripped markup +on two lines

+_Markdown_ + +**Markdown** \ No newline at end of file From 129f807e32104a31134882c9ca32b279350f3db3 Mon Sep 17 00:00:00 2001 From: Daniel Berthereau Date: Thu, 22 Jun 2017 00:00:00 +0200 Subject: [PATCH 057/106] Inverted checks of consistency for markdown following markups. --- test/data/markup_consecutive_one.html | 2 +- test/data/markup_consecutive_one.md | 2 +- test/data/markup_consecutive_one_line.html | 2 +- test/data/markup_consecutive_one_line.md | 2 +- test/data/markup_consecutive_one_stripped.html | 2 +- test/data/markup_consecutive_one_stripped.md | 2 +- test/data/markup_consecutive_two.html | 2 +- test/data/markup_consecutive_two.md | 2 +- test/data/markup_consecutive_two_lines.html | 2 +- test/data/markup_consecutive_two_lines.md | 2 +- test/data/markup_consecutive_two_stripped.html | 2 +- test/data/markup_consecutive_two_stripped.md | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/data/markup_consecutive_one.html b/test/data/markup_consecutive_one.html index a0b1fca..d4c5005 100644 --- a/test/data/markup_consecutive_one.html +++ b/test/data/markup_consecutive_one.html @@ -1,3 +1,3 @@
Markup
-

No markdown without blank line if strict compliance, but main processors convert it.

+_No markdown_ without blank line for **strict** compliance with CommonMark.

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one.md b/test/data/markup_consecutive_one.md index 7a00770..18b4dcb 100644 --- a/test/data/markup_consecutive_one.md +++ b/test/data/markup_consecutive_one.md @@ -1,4 +1,4 @@
Markup
-_No markdown_ without blank line if strict compliance, but **main** processors convert it. +_No markdown_ without blank line for **strict** compliance with CommonMark. **Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_one_line.html b/test/data/markup_consecutive_one_line.html index 2a301aa..a89b4fd 100644 --- a/test/data/markup_consecutive_one_line.html +++ b/test/data/markup_consecutive_one_line.html @@ -1,4 +1,4 @@
One markup on two lines
-

Markdown

+_No markdown_

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one_line.md b/test/data/markup_consecutive_one_line.md index d40609a..daf945a 100644 --- a/test/data/markup_consecutive_one_line.md +++ b/test/data/markup_consecutive_one_line.md @@ -1,5 +1,5 @@
One markup on two lines
-_Markdown_ +_No markdown_ **Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_one_stripped.html b/test/data/markup_consecutive_one_stripped.html index eb5136b..ccc01f1 100644 --- a/test/data/markup_consecutive_one_stripped.html +++ b/test/data/markup_consecutive_one_stripped.html @@ -1,3 +1,3 @@

Stripped markup

-

Markdown

+_No markdown_

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_one_stripped.md b/test/data/markup_consecutive_one_stripped.md index 4189580..7f8df0c 100644 --- a/test/data/markup_consecutive_one_stripped.md +++ b/test/data/markup_consecutive_one_stripped.md @@ -1,4 +1,4 @@

Stripped markup

-_Markdown_ +_No markdown_ **Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two.html b/test/data/markup_consecutive_two.html index efcebcd..f7e71c7 100644 --- a/test/data/markup_consecutive_two.html +++ b/test/data/markup_consecutive_two.html @@ -1,3 +1,3 @@
First markup

and second markup on the same line.

-

Markdown

+_No markdown_

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two.md b/test/data/markup_consecutive_two.md index bd663ac..83f3af7 100644 --- a/test/data/markup_consecutive_two.md +++ b/test/data/markup_consecutive_two.md @@ -1,4 +1,4 @@
First markup

and second markup on the same line.

-_Markdown_ +_No markdown_ **Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two_lines.html b/test/data/markup_consecutive_two_lines.html index cfa1463..ffa4728 100644 --- a/test/data/markup_consecutive_two_lines.html +++ b/test/data/markup_consecutive_two_lines.html @@ -1,4 +1,4 @@
First markup

and partial markup on two lines.

-

Markdown

+_No markdown_

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two_lines.md b/test/data/markup_consecutive_two_lines.md index 66318e9..dc70bb7 100644 --- a/test/data/markup_consecutive_two_lines.md +++ b/test/data/markup_consecutive_two_lines.md @@ -1,5 +1,5 @@
First markup

and partial markup on two lines.

-_Markdown_ +_No markdown_ **Markdown** \ No newline at end of file diff --git a/test/data/markup_consecutive_two_stripped.html b/test/data/markup_consecutive_two_stripped.html index 1633678..707d6be 100644 --- a/test/data/markup_consecutive_two_stripped.html +++ b/test/data/markup_consecutive_two_stripped.html @@ -1,4 +1,4 @@

Stripped markup on two lines

-

Markdown

+_No markdown_

Markdown

\ No newline at end of file diff --git a/test/data/markup_consecutive_two_stripped.md b/test/data/markup_consecutive_two_stripped.md index 8173380..af5b781 100644 --- a/test/data/markup_consecutive_two_stripped.md +++ b/test/data/markup_consecutive_two_stripped.md @@ -1,5 +1,5 @@

Stripped markup on two lines

-_Markdown_ +_No markdown_ **Markdown** \ No newline at end of file From 6a4afac0d02218b134b922428e5c659ed9360d36 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 22 Jun 2017 00:02:03 +0100 Subject: [PATCH 058/106] remove ability for htmlblock to allow paragraph after if it closes on the same line --- Parsedown.php | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index e516f6b..f6daad2 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); @@ -697,32 +697,6 @@ class Parsedown 'markup' => $Line['text'], ); - $length = strlen($matches[0]); - - $remainder = substr($Line['text'], $length); - - if (trim($remainder) === '') - { - if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) - { - $Block['closed'] = true; - - $Block['void'] = true; - } - } - else - { - if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) - { - return; - } - - if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) - { - $Block['closed'] = true; - } - } - return $Block; } } From c05bff047ad6703b1b4abd58bca9de78f32fa05a Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 22 Jun 2017 00:03:12 +0100 Subject: [PATCH 059/106] correct test to match CommonMark specified input for output --- test/data/self-closing_html.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/data/self-closing_html.md b/test/data/self-closing_html.md index acb2032..61d16a3 100644 --- a/test/data/self-closing_html.md +++ b/test/data/self-closing_html.md @@ -1,12 +1,18 @@
+ paragraph
+ paragraph
+ paragraph
+ paragraph
+ paragraph
+ paragraph \ 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 060/106] 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 061/106] 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 062/106] 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 063/106] 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 064/106] 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 065/106] 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 066/106] 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 067/106] 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 068/106] 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 069/106] 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 070/106] 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 071/106] 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 072/106] 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 073/106] 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 @@ +
        +
      1. +

        One +First body copy

        +
      2. +
      3. +

        Two +Last body copy

        +
      4. +
      \ 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 074/106] 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 075/106] 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 076/106] 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 077/106] 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 078/106] 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 079/106] 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 080/106] 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 081/106] 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 082/106] 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 .= ''; + $markup .= $hasName ? '' : ''; } - 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 083/106] 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 084/106] 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 085/106] 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 086/106] 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 087/106] 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 088/106] 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 089/106] 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 090/106] 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 091/106] 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 092/106] 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 093/106] 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 094/106] 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 095/106] 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 096/106] 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 ? '' : ''; 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 097/106] 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 098/106] 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 099/106] 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 100/106] 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 101/106] 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 102/106] 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). From 913e04782f970bb8ff1fa1441aa127dfa2155867 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 25 Mar 2018 22:50:16 +0100 Subject: [PATCH 103/106] Add failing test cases to be fixed --- test/data/setext_header_spaces.html | 12 ++++++++++++ test/data/setext_header_spaces.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/data/setext_header_spaces.html create mode 100644 test/data/setext_header_spaces.md diff --git a/test/data/setext_header_spaces.html b/test/data/setext_header_spaces.html new file mode 100644 index 0000000..daf97c0 --- /dev/null +++ b/test/data/setext_header_spaces.html @@ -0,0 +1,12 @@ +

    trailing space

    +

    trailing space

    +

    leading and trailing space

    +

    leading and trailing space

    +

    1 leading space

    +

    1 leading space

    +

    3 leading spaces

    +

    3 leading spaces

    +

    too many leading spaces +==

    +

    too many leading spaces +--

    \ No newline at end of file diff --git a/test/data/setext_header_spaces.md b/test/data/setext_header_spaces.md new file mode 100644 index 0000000..4ac35bb --- /dev/null +++ b/test/data/setext_header_spaces.md @@ -0,0 +1,29 @@ +trailing space +== + +trailing space +-- + +leading and trailing space + == + +leading and trailing space + -- + +1 leading space + == + +1 leading space + -- + +3 leading spaces + == + +3 leading spaces + -- + +too many leading spaces + == + +too many leading spaces + -- \ No newline at end of file From f71bec00f4053854ebaf2b842724e35aec6cd5b6 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Sun, 25 Mar 2018 22:50:42 +0100 Subject: [PATCH 104/106] Fix space handling in setext headings --- Parsedown.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Parsedown.php b/Parsedown.php index bf08700..844a504 100644 --- a/Parsedown.php +++ b/Parsedown.php @@ -705,8 +705,10 @@ class Parsedown return; } - if (chop($Line['text'], $Line['text'][0]) === '') - { + if ( + chop(chop($Line['text'], ' '), $Line['text'][0]) === '' + and $Line['indent'] < 4 + ) { $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; return $Block; From e7fbbf537b36d2748bef4dddbff0f976d3e7cafe Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 26 Mar 2018 18:45:34 +0100 Subject: [PATCH 105/106] Add repo specific paths to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8a7996 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +composer.lock +vendor/ From dd9f4036ee92e2d281b8f5b0501c3e9e3f34d11c Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 26 Mar 2018 18:47:33 +0100 Subject: [PATCH 106/106] Add .gitignore to export ignore in .gitattribtutes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 001c45c..c1a5d9a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ # Ignore all tests for archive /test export-ignore /.gitattributes export-ignore +/.gitignore export-ignore /.travis.yml export-ignore /phpunit.xml.dist export-ignore