mirror of
https://github.com/erusev/parsedown.git
synced 2023-08-10 21:13:06 +03:00
Merge branch 'master' into fix/consistency_follow
This commit is contained in:
@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Test Parsedown against the CommonMark spec.
|
||||
*
|
||||
* Some code based on the original JavaScript test runner by jgm.
|
||||
*
|
||||
* @link http://commonmark.org/ CommonMark
|
||||
* @link http://git.io/8WtRvQ JavaScript test runner
|
||||
*/
|
||||
class CommonMarkTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
const SPEC_URL = 'https://raw.githubusercontent.com/jgm/stmd/master/spec.txt';
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
* @param $section
|
||||
* @param $markdown
|
||||
* @param $expectedHtml
|
||||
*/
|
||||
function test_($section, $markdown, $expectedHtml)
|
||||
{
|
||||
$Parsedown = new Parsedown();
|
||||
$Parsedown->setUrlsLinked(false);
|
||||
|
||||
$actualHtml = $Parsedown->text($markdown);
|
||||
$actualHtml = $this->normalizeMarkup($actualHtml);
|
||||
|
||||
$this->assertEquals($expectedHtml, $actualHtml);
|
||||
}
|
||||
|
||||
function data()
|
||||
{
|
||||
$spec = file_get_contents(self::SPEC_URL);
|
||||
$spec = strstr($spec, '<!-- END TESTS -->', true);
|
||||
|
||||
$tests = array();
|
||||
$currentSection = '';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
71
test/CommonMarkTestStrict.php
Normal file
71
test/CommonMarkTestStrict.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Test Parsedown against the CommonMark spec
|
||||
*
|
||||
* @link http://commonmark.org/ CommonMark
|
||||
*/
|
||||
class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
const SPEC_URL = 'https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt';
|
||||
|
||||
protected $parsedown;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->parsedown = new TestParsedown();
|
||||
$this->parsedown->setUrlsLinked(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
* @param $id
|
||||
* @param $section
|
||||
* @param $markdown
|
||||
* @param $expectedHtml
|
||||
*/
|
||||
public function testExample($id, $section, $markdown, $expectedHtml)
|
||||
{
|
||||
$actualHtml = $this->parsedown->text($markdown);
|
||||
$this->assertEquals($expectedHtml, $actualHtml);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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, '<!-- END TESTS -->', true);
|
||||
|
||||
$matches = array();
|
||||
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 {
|
||||
$currentId++;
|
||||
$markdown = str_replace('→', "\t", $match[1]);
|
||||
$expectedHtml = isset($match[2]) ? str_replace('→', "\t", $match[2]) : '';
|
||||
|
||||
$data[$currentId] = array(
|
||||
'id' => $currentId,
|
||||
'section' => $currentSection,
|
||||
'markdown' => $markdown,
|
||||
'expectedHtml' => $expectedHtml
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
63
test/CommonMarkTestWeak.php
Normal file
63
test/CommonMarkTestWeak.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
require_once(__DIR__ . '/CommonMarkTestStrict.php');
|
||||
|
||||
/**
|
||||
* Test Parsedown against the CommonMark spec, but less aggressive
|
||||
*
|
||||
* The resulting HTML markup is cleaned up before comparison, so examples
|
||||
* which would normally fail due to actually invisible differences (e.g.
|
||||
* superfluous whitespaces), don't fail. However, cleanup relies on block
|
||||
* element detection. The detection doesn't work correctly when a element's
|
||||
* `display` CSS property is manipulated. According to that this test is only
|
||||
* a interim solution on Parsedown's way to full CommonMark compatibility.
|
||||
*
|
||||
* @link http://commonmark.org/ CommonMark
|
||||
*/
|
||||
class CommonMarkTestWeak extends CommonMarkTestStrict
|
||||
{
|
||||
protected $textLevelElementRegex;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$textLevelElements = $this->parsedown->getTextLevelElements();
|
||||
array_walk($textLevelElements, function (&$element) {
|
||||
$element = preg_quote($element, '/');
|
||||
});
|
||||
$this->textLevelElementRegex = '\b(?:' . implode('|', $textLevelElements) . ')\b';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider data
|
||||
* @param $id
|
||||
* @param $section
|
||||
* @param $markdown
|
||||
* @param $expectedHtml
|
||||
*/
|
||||
public function testExample($id, $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 <pre> 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;
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
require 'SampleExtensions.php';
|
||||
|
||||
class ParsedownTest extends PHPUnit_Framework_TestCase
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ParsedownTest extends TestCase
|
||||
{
|
||||
final function __construct($name = null, array $data = array(), $dataName = '')
|
||||
{
|
||||
@ -28,7 +31,7 @@ class ParsedownTest extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
protected function initParsedown()
|
||||
{
|
||||
$Parsedown = new Parsedown();
|
||||
$Parsedown = new TestParsedown();
|
||||
|
||||
return $Parsedown;
|
||||
}
|
||||
@ -47,11 +50,47 @@ class ParsedownTest extends PHPUnit_Framework_TestCase
|
||||
$expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup);
|
||||
$expectedMarkup = str_replace("\r", "\n", $expectedMarkup);
|
||||
|
||||
$this->Parsedown->setSafeMode(substr($test, 0, 3) === 'xss');
|
||||
|
||||
$actualMarkup = $this->Parsedown->text($markdown);
|
||||
|
||||
$this->assertEquals($expectedMarkup, $actualMarkup);
|
||||
}
|
||||
|
||||
function testRawHtml()
|
||||
{
|
||||
$markdown = "```php\nfoobar\n```";
|
||||
$expectedMarkup = '<pre><code class="language-php"><p>foobar</p></code></pre>';
|
||||
$expectedSafeMarkup = '<pre><code class="language-php"><p>foobar</p></code></pre>';
|
||||
|
||||
$unsafeExtension = new UnsafeExtension;
|
||||
$actualMarkup = $unsafeExtension->text($markdown);
|
||||
|
||||
$this->assertEquals($expectedMarkup, $actualMarkup);
|
||||
|
||||
$unsafeExtension->setSafeMode(true);
|
||||
$actualSafeMarkup = $unsafeExtension->text($markdown);
|
||||
|
||||
$this->assertEquals($expectedSafeMarkup, $actualSafeMarkup);
|
||||
}
|
||||
|
||||
function testTrustDelegatedRawHtml()
|
||||
{
|
||||
$markdown = "```php\nfoobar\n```";
|
||||
$expectedMarkup = '<pre><code class="language-php"><p>foobar</p></code></pre>';
|
||||
$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();
|
||||
@ -133,15 +172,14 @@ color: red;
|
||||
<p>comment</p>
|
||||
<p><!-- html comment --></p>
|
||||
EXPECTED_HTML;
|
||||
$parsedownWithNoMarkup = new Parsedown();
|
||||
|
||||
$parsedownWithNoMarkup = new TestParsedown();
|
||||
$parsedownWithNoMarkup->setMarkupEscaped(true);
|
||||
$this->assertEquals($expectedHtml, $parsedownWithNoMarkup->text($markdownWithHtml));
|
||||
}
|
||||
|
||||
public function testLateStaticBinding()
|
||||
{
|
||||
include __DIR__ . '/TestParsedown.php';
|
||||
|
||||
$parsedown = Parsedown::instance();
|
||||
$this->assertInstanceOf('Parsedown', $parsedown);
|
||||
|
||||
|
40
test/SampleExtensions.php
Normal file
40
test/SampleExtensions.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
class UnsafeExtension 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'] = "<p>$text</p>";
|
||||
|
||||
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 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'] = "<p>$text</p>";
|
||||
$Block['element']['text']['allowRawHtmlInSafeMode'] = true;
|
||||
|
||||
return $Block;
|
||||
}
|
||||
}
|
@ -2,4 +2,8 @@
|
||||
|
||||
class TestParsedown extends Parsedown
|
||||
{
|
||||
public function getTextLevelElements()
|
||||
{
|
||||
return $this->textLevelElements;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
include 'Parsedown.php';
|
||||
|
||||
if ( ! class_exists('\PHPUnit_Framework_TestCase')) {
|
||||
class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase');
|
||||
}
|
@ -1 +1,2 @@
|
||||
<p>my email is <a href="mailto:me@example.com">me@example.com</a></p>
|
||||
<p>my email is <a href="mailto:me@example.com">me@example.com</a></p>
|
||||
<p>html tags shouldn't start an email autolink <strong>first.last@example.com</strong></p>
|
@ -1 +1,3 @@
|
||||
my email is <me@example.com>
|
||||
my email is <me@example.com>
|
||||
|
||||
html tags shouldn't start an email autolink <strong>first.last@example.com</strong>
|
@ -3,4 +3,9 @@
|
||||
$message = 'fenced code block';
|
||||
echo $message;</code></pre>
|
||||
<pre><code>tilde</code></pre>
|
||||
<pre><code class="language-php">echo 'language identifier';</code></pre>
|
||||
<pre><code class="language-php">echo 'language identifier';</code></pre>
|
||||
<pre><code class="language-c#">echo 'language identifier with non words';</code></pre>
|
||||
<pre><code class="language-html+php"><?php
|
||||
echo "Hello World";
|
||||
?>
|
||||
<a href="http://auraphp.com" >Aura Project</a></code></pre>
|
@ -11,4 +11,15 @@ tilde
|
||||
|
||||
```php
|
||||
echo 'language identifier';
|
||||
```
|
||||
|
||||
```c#
|
||||
echo 'language identifier with non words';
|
||||
```
|
||||
|
||||
```html+php
|
||||
<?php
|
||||
echo "Hello World";
|
||||
?>
|
||||
<a href="http://auraphp.com" >Aura Project</a>
|
||||
```
|
10
test/data/multiline_lists.html
Normal file
10
test/data/multiline_lists.html
Normal file
@ -0,0 +1,10 @@
|
||||
<ol>
|
||||
<li>
|
||||
<p>One
|
||||
First body copy</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Two
|
||||
Last body copy</p>
|
||||
</li>
|
||||
</ol>
|
5
test/data/multiline_lists.md
Normal file
5
test/data/multiline_lists.md
Normal file
@ -0,0 +1,5 @@
|
||||
1. One
|
||||
First body copy
|
||||
|
||||
2. Two
|
||||
Last body copy
|
@ -8,5 +8,7 @@
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
<li>li</li>
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
</ul>
|
@ -2,6 +2,10 @@
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
<li>li</li>
|
||||
<li>li</li>
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
</ul>
|
@ -2,7 +2,9 @@
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
<li>li</li>
|
||||
<li>
|
||||
<p>li</p>
|
||||
</li>
|
||||
</ul>
|
||||
<hr />
|
||||
<ul>
|
||||
|
6
test/data/xss_attribute_encoding.html
Normal file
6
test/data/xss_attribute_encoding.html
Normal file
@ -0,0 +1,6 @@
|
||||
<p><a href="https://www.example.com"">xss</a></p>
|
||||
<p><img src="https://www.example.com"" alt="xss" /></p>
|
||||
<p><a href="https://www.example.com'">xss</a></p>
|
||||
<p><img src="https://www.example.com'" alt="xss" /></p>
|
||||
<p><img src="https://www.example.com" alt="xss"" /></p>
|
||||
<p><img src="https://www.example.com" alt="xss'" /></p>
|
11
test/data/xss_attribute_encoding.md
Normal file
11
test/data/xss_attribute_encoding.md
Normal file
@ -0,0 +1,11 @@
|
||||
[xss](https://www.example.com")
|
||||
|
||||

|
||||
|
||||
[xss](https://www.example.com')
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
16
test/data/xss_bad_url.html
Normal file
16
test/data/xss_bad_url.html
Normal file
@ -0,0 +1,16 @@
|
||||
<p><a href="javascript%3Aalert(1)">xss</a></p>
|
||||
<p><a href="javascript%3Aalert(1)">xss</a></p>
|
||||
<p><a href="javascript%3A//alert(1)">xss</a></p>
|
||||
<p><a href="javascript&colon;alert(1)">xss</a></p>
|
||||
<p><img src="javascript%3Aalert(1)" alt="xss" /></p>
|
||||
<p><img src="javascript%3Aalert(1)" alt="xss" /></p>
|
||||
<p><img src="javascript%3A//alert(1)" alt="xss" /></p>
|
||||
<p><img src="javascript&colon;alert(1)" alt="xss" /></p>
|
||||
<p><a href="data%3Atext/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">xss</a></p>
|
||||
<p><a href="data%3Atext/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">xss</a></p>
|
||||
<p><a href="data%3A//text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">xss</a></p>
|
||||
<p><a href="data&colon;text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">xss</a></p>
|
||||
<p><img src="data%3Atext/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==" alt="xss" /></p>
|
||||
<p><img src="data%3Atext/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==" alt="xss" /></p>
|
||||
<p><img src="data%3A//text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==" alt="xss" /></p>
|
||||
<p><img src="data&colon;text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==" alt="xss" /></p>
|
31
test/data/xss_bad_url.md
Normal file
31
test/data/xss_bad_url.md
Normal file
@ -0,0 +1,31 @@
|
||||
[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==)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
7
test/data/xss_text_encoding.html
Normal file
7
test/data/xss_text_encoding.html
Normal file
@ -0,0 +1,7 @@
|
||||
<p><script>alert(1)</script></p>
|
||||
<p><script></p>
|
||||
<p>alert(1)</p>
|
||||
<p></script></p>
|
||||
<p><script>
|
||||
alert(1)
|
||||
</script></p>
|
12
test/data/xss_text_encoding.md
Normal file
12
test/data/xss_text_encoding.md
Normal file
@ -0,0 +1,12 @@
|
||||
<script>alert(1)</script>
|
||||
|
||||
<script>
|
||||
|
||||
alert(1)
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
alert(1)
|
||||
</script>
|
Reference in New Issue
Block a user