Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opt in raw html #745

Merged
merged 3 commits into from Dec 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -14,6 +14,7 @@ matrix:
- php: 7.1
- php: 7.2
- php: 7.3
- php: 7.4
- php: nightly
fast_finish: true
allow_failures:
Expand Down
27 changes: 23 additions & 4 deletions Parsedown.php
Expand Up @@ -17,7 +17,7 @@ class Parsedown
{
# ~

const version = '1.7.3';
const version = '1.7.4-dev';

# ~

Expand Down Expand Up @@ -1489,22 +1489,41 @@ protected function element(array $Element)
}
}

$permitRawHtml = false;

if (isset($Element['text']))
{
$text = $Element['text'];
}
// very strongly consider an alternative if you're writing an
// extension
elseif (isset($Element['rawHtml']))
{
$text = $Element['rawHtml'];
$allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
$permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
}

if (isset($text))
{
$markup .= '>';

if (!isset($Element['nonNestables']))
if (!isset($Element['nonNestables']))
{
$Element['nonNestables'] = array();
}

if (isset($Element['handler']))
{
$markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
$markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
}
elseif (!$permitRawHtml)
{
$markup .= self::escape($text, true);
}
else
{
$markup .= self::escape($Element['text'], true);
$markup .= $text;
}

$markup .= '</'.$Element['name'].'>';
Expand Down
36 changes: 36 additions & 0 deletions test/ParsedownTest.php
@@ -1,5 +1,7 @@
<?php

require 'SampleExtensions.php';

use PHPUnit\Framework\TestCase;

class ParsedownTest extends TestCase
Expand Down Expand Up @@ -55,6 +57,40 @@ function test_($test, $dir)
$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">&lt;p&gt;foobar&lt;/p&gt;</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();
Expand Down
39 changes: 39 additions & 0 deletions test/SampleExtensions.php
@@ -0,0 +1,39 @@
<?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;
}
}