1
0
mirror of https://github.com/erusev/parsedown.git synced 2023-08-10 21:13:06 +03:00
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
This commit is contained in:
Aidan Woods 2017-05-01 03:24:40 +01:00
parent b3d45c4bb9
commit 6bb66db00f
No known key found for this signature in database
GPG Key ID: 9A6A8EFAA512BBB9

View File

@ -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.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
{
$text = $matches[2];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$text = preg_replace("/[ ]*\n/", ' ', $text);
return array(
@ -1108,7 +1105,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
{
$url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
$url = $matches[1];
if ( ! isset($matches[2]))
{
@ -1270,32 +1267,6 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
if ( $this->safeLinksEnabled )
{
$matched = false;
foreach ( $this->safeLinksWhitelist as $scheme )
{
if ( stripos($Element['attributes']['href'], $scheme) === 0 )
{
$matched = true;
break;
}
}
if ( ! $matched )
{
return;
}
}
$Element['attributes']['href'] = htmlspecialchars($Element['attributes']['href'], ENT_QUOTES, 'UTF-8');
$Element['text'] = htmlspecialchars($Element['text'], ENT_QUOTES, 'UTF-8');
if ( $Element['attributes']['title'] !== null )
{
$Element['attributes']['title'] = htmlspecialchars($Element['attributes']['title'], ENT_QUOTES, 'UTF-8');
}
return array(
'extent' => $extent,
'element' => $Element,
@ -1384,7 +1355,7 @@ class Parsedown
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
$url = htmlspecialchars($matches[0][0], ENT_QUOTES, 'UTF-8');
$url = $matches[0][0];
$Inline = array(
'extent' => strlen($matches[0][0]),
@ -1406,7 +1377,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
{
$url = htmlspecialchars($matches[1], ENT_QUOTES, 'UTF-8');
$url = $matches[1];
return array(
'extent' => strlen($matches[0]),
@ -1444,6 +1415,8 @@ class Parsedown
protected function element(array $Element)
{
$Element = $this->sanitiseElement($Element);
$markup = '<'.$Element['name'];
if (isset($Element['attributes']))
@ -1455,7 +1428,7 @@ class Parsedown
continue;
}
$markup .= ' '.$name.'="'.$value.'"';
$markup .= ' '.$name.'="'.self::escape($value).'"';
}
}
@ -1469,7 +1442,7 @@ class Parsedown
}
else
{
$markup .= $Element['text'];
$markup .= self::escape($Element['text'], true);
}
$markup .= '</'.$Element['name'].'>';
@ -1528,10 +1501,55 @@ class Parsedown
return $markup;
}
protected function sanitiseElement(array $Element)
{
$safeUrlNameToAtt = array(
'a' => 'href',
'img' => 'src',
);
if (isset($safeUrlNameToAtt[$Element['name']]))
{
$Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
}
return $Element;
}
protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
{
if ($this->safeLinksEnabled)
{
$safe = false;
foreach ($this->safeLinksWhitelist as $scheme)
{
if (stripos($Element['attributes'][$attribute], $scheme) === 0)
{
$safe = true;
break;
}
}
if ( ! $safe)
{
unset($Element['attributes'][$attribute]);
}
}
return $Element;
}
#
# Static Methods
#
protected static function escape($text, $allowQuotes = false)
{
return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
}
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))