NEW Update lib parsedownto 1.7.4
This commit is contained in:
parent
3999f87fd2
commit
bf89d61686
@ -29,7 +29,7 @@ GeoIP2 0.2.0 Apache License 2.0 Yes
|
|||||||
Mobiledetect 2.8.41 MIT License Yes Detect mobile devices browsers
|
Mobiledetect 2.8.41 MIT License Yes Detect mobile devices browsers
|
||||||
NuSoap 0.9.5 LGPL 2.1+ Yes Library to develop SOAP Web services (not into rpm and deb package)
|
NuSoap 0.9.5 LGPL 2.1+ Yes Library to develop SOAP Web services (not into rpm and deb package)
|
||||||
PEAR Mail_MIME 1.8.9 BSD Yes NuSoap dependency
|
PEAR Mail_MIME 1.8.9 BSD Yes NuSoap dependency
|
||||||
ParseDown 1.6 MIT License Yes Markdown parser
|
ParseDown 1.7.4 MIT License Yes Markdown parser
|
||||||
PCLZip 2.8.4 LGPL-3+ Yes Library to zip/unzip files
|
PCLZip 2.8.4 LGPL-3+ Yes Library to zip/unzip files
|
||||||
PHPDebugBar 1.18.2 MIT License Yes Used only by the module "debugbar" for developers
|
PHPDebugBar 1.18.2 MIT License Yes Used only by the module "debugbar" for developers
|
||||||
PHP-Imap 2.7.2 MIT License Yes Library to use IMAP with OAuth
|
PHP-Imap 2.7.2 MIT License Yes Library to use IMAP with OAuth
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
/* Copyright (C) 2008-2013 Laurent Destailleur <eldy@users.sourceforge.net>
|
/* Copyright (C) 2008-2023 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \file htdocs/core/lib/parsemd.lib.php
|
* \file htdocs/core/lib/parsemd.lib.php
|
||||||
* \brief This file contains functions dedicated to MD parsind.
|
* \brief This file contains functions dedicated to MD parsing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,8 +40,9 @@ function dolMd2Html($content, $parser = 'parsedown', $replaceimagepath = null)
|
|||||||
}
|
}
|
||||||
if ($parser == 'parsedown') {
|
if ($parser == 'parsedown') {
|
||||||
include_once DOL_DOCUMENT_ROOT.'/includes/parsedown/Parsedown.php';
|
include_once DOL_DOCUMENT_ROOT.'/includes/parsedown/Parsedown.php';
|
||||||
$Parsedown = new Parsedown();
|
$parsedown = new Parsedown();
|
||||||
$content = $Parsedown->text($content);
|
$parsedown->setSafeMode(true); // This will escape HTML link <a href=""> into html entities but markdown links are ok
|
||||||
|
$content = $parsedown->text($content);
|
||||||
} else {
|
} else {
|
||||||
$content = nl2br($content);
|
$content = nl2br($content);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -683,6 +683,11 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
|
|||||||
if ((float) DOL_VERSION >= 6.0) {
|
if ((float) DOL_VERSION >= 6.0) {
|
||||||
@include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
|
@include_once DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
|
||||||
|
|
||||||
|
// Replace a HTML string with a Markdown syntax
|
||||||
|
$content = preg_replace('/<a href="([^"]+)">([^<]+)<\/a>/', '[\2](\1)', $content);
|
||||||
|
//$content = preg_replace('/<a href="([^"]+)" target="([^"]+)">([^<]+)<\/a>/', '[\3](\1){:target="\2"}', $content);
|
||||||
|
$content = preg_replace('/<a href="([^"]+)" target="([^"]+)">([^<]+)<\/a>/', '[\3](\1)', $content);
|
||||||
|
|
||||||
$content = dolMd2Html(
|
$content = dolMd2Html(
|
||||||
$content,
|
$content,
|
||||||
'parsedown',
|
'parsedown',
|
||||||
@ -692,6 +697,8 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
|
|||||||
'images/' => dol_buildpath(strtolower($this->name).'/images/', 1),
|
'images/' => dol_buildpath(strtolower($this->name).'/images/', 1),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$content = preg_replace('/<a href="/', '<a target="_blank" rel="noopener noreferrer" href="', $content);
|
||||||
} else {
|
} else {
|
||||||
$content = nl2br($content);
|
$content = nl2br($content);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
language: php
|
|
||||||
|
|
||||||
php:
|
|
||||||
- 7.1
|
|
||||||
- 7.0
|
|
||||||
- 5.6
|
|
||||||
- 5.5
|
|
||||||
- 5.4
|
|
||||||
- 5.3
|
|
||||||
- hhvm
|
|
||||||
- hhvm-nightly
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
allow_failures:
|
|
||||||
- php: hhvm-nightly
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 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
|
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
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
# ~
|
# ~
|
||||||
|
|
||||||
const version = '1.6.0';
|
const version = '1.7.4';
|
||||||
|
|
||||||
# ~
|
# ~
|
||||||
|
|
||||||
@ -75,6 +75,32 @@ class Parsedown
|
|||||||
|
|
||||||
protected $urlsLinked = true;
|
protected $urlsLinked = true;
|
||||||
|
|
||||||
|
function setSafeMode($safeMode)
|
||||||
|
{
|
||||||
|
$this->safeMode = (bool) $safeMode;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $safeMode;
|
||||||
|
|
||||||
|
protected $safeLinksWhitelist = array(
|
||||||
|
'http://',
|
||||||
|
'https://',
|
||||||
|
'ftp://',
|
||||||
|
'ftps://',
|
||||||
|
'mailto:',
|
||||||
|
'data:image/png;base64,',
|
||||||
|
'data:image/gif;base64,',
|
||||||
|
'data:image/jpeg;base64,',
|
||||||
|
'irc:',
|
||||||
|
'ircs:',
|
||||||
|
'git:',
|
||||||
|
'ssh:',
|
||||||
|
'news:',
|
||||||
|
'steam:',
|
||||||
|
);
|
||||||
|
|
||||||
#
|
#
|
||||||
# Lines
|
# Lines
|
||||||
#
|
#
|
||||||
@ -141,11 +167,7 @@ class Parsedown
|
|||||||
|
|
||||||
foreach ($parts as $part)
|
foreach ($parts as $part)
|
||||||
{
|
{
|
||||||
// @CHANGE LDR Fix when mb_strlen is not available
|
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
||||||
//$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
|
||||||
if (function_exists('mb_strlen')) $len = mb_strlen($line, 'utf-8');
|
|
||||||
else $len = strlen($line);
|
|
||||||
$shortage = 4 - $len % 4;
|
|
||||||
|
|
||||||
$line .= str_repeat(' ', $shortage);
|
$line .= str_repeat(' ', $shortage);
|
||||||
$line .= $part;
|
$line .= $part;
|
||||||
@ -346,8 +368,6 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
$text = $Block['element']['text']['text'];
|
$text = $Block['element']['text']['text'];
|
||||||
|
|
||||||
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
|
||||||
|
|
||||||
$Block['element']['text']['text'] = $text;
|
$Block['element']['text']['text'] = $text;
|
||||||
|
|
||||||
return $Block;
|
return $Block;
|
||||||
@ -358,7 +378,7 @@ class Parsedown
|
|||||||
|
|
||||||
protected function blockComment($Line)
|
protected function blockComment($Line)
|
||||||
{
|
{
|
||||||
if ($this->markupEscaped)
|
if ($this->markupEscaped or $this->safeMode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -400,7 +420,7 @@ class Parsedown
|
|||||||
|
|
||||||
protected function blockFencedCode($Line)
|
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(
|
$Element = array(
|
||||||
'name' => 'code',
|
'name' => 'code',
|
||||||
@ -409,7 +429,21 @@ class Parsedown
|
|||||||
|
|
||||||
if (isset($matches[1]))
|
if (isset($matches[1]))
|
||||||
{
|
{
|
||||||
$class = 'language-'.$matches[1];
|
/**
|
||||||
|
* https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
|
||||||
|
* Every HTML element may have a class attribute specified.
|
||||||
|
* The attribute, if specified, must have a value that is a set
|
||||||
|
* of space-separated tokens representing the various classes
|
||||||
|
* that the element belongs to.
|
||||||
|
* [...]
|
||||||
|
* The space characters, for the purposes of this specification,
|
||||||
|
* are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
|
||||||
|
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
|
||||||
|
* U+000D CARRIAGE RETURN (CR).
|
||||||
|
*/
|
||||||
|
$language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r"));
|
||||||
|
|
||||||
|
$class = 'language-'.$language;
|
||||||
|
|
||||||
$Element['attributes'] = array(
|
$Element['attributes'] = array(
|
||||||
'class' => $class,
|
'class' => $class,
|
||||||
@ -461,8 +495,6 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
$text = $Block['element']['text']['text'];
|
$text = $Block['element']['text']['text'];
|
||||||
|
|
||||||
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
|
||||||
|
|
||||||
$Block['element']['text']['text'] = $text;
|
$Block['element']['text']['text'] = $text;
|
||||||
|
|
||||||
return $Block;
|
return $Block;
|
||||||
@ -551,6 +583,8 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
$Block['li']['text'] []= '';
|
$Block['li']['text'] []= '';
|
||||||
|
|
||||||
|
$Block['loose'] = true;
|
||||||
|
|
||||||
unset($Block['interrupted']);
|
unset($Block['interrupted']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,6 +633,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
|
# Quote
|
||||||
|
|
||||||
@ -682,12 +732,12 @@ class Parsedown
|
|||||||
|
|
||||||
protected function blockMarkup($Line)
|
protected function blockMarkup($Line)
|
||||||
{
|
{
|
||||||
if ($this->markupEscaped)
|
if ($this->markupEscaped or $this->safeMode)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
||||||
{
|
{
|
||||||
$element = strtolower($matches[1]);
|
$element = strtolower($matches[1]);
|
||||||
|
|
||||||
@ -1001,7 +1051,7 @@ class Parsedown
|
|||||||
# ~
|
# ~
|
||||||
#
|
#
|
||||||
|
|
||||||
public function line($text)
|
public function line($text, $nonNestables=array())
|
||||||
{
|
{
|
||||||
$markup = '';
|
$markup = '';
|
||||||
|
|
||||||
@ -1017,6 +1067,13 @@ class Parsedown
|
|||||||
|
|
||||||
foreach ($this->InlineTypes[$marker] as $inlineType)
|
foreach ($this->InlineTypes[$marker] as $inlineType)
|
||||||
{
|
{
|
||||||
|
# check to see if the current inline type is nestable in the current context
|
||||||
|
|
||||||
|
if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
||||||
|
|
||||||
if ( ! isset($Inline))
|
if ( ! isset($Inline))
|
||||||
@ -1038,6 +1095,13 @@ class Parsedown
|
|||||||
$Inline['position'] = $markerPosition;
|
$Inline['position'] = $markerPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# cause the new element to 'inherit' our non nestables
|
||||||
|
|
||||||
|
foreach ($nonNestables as $non_nestable)
|
||||||
|
{
|
||||||
|
$Inline['element']['nonNestables'][] = $non_nestable;
|
||||||
|
}
|
||||||
|
|
||||||
# the text that comes before the inline
|
# the text that comes before the inline
|
||||||
$unmarkedText = substr($text, 0, $Inline['position']);
|
$unmarkedText = substr($text, 0, $Inline['position']);
|
||||||
|
|
||||||
@ -1078,7 +1142,6 @@ class Parsedown
|
|||||||
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
||||||
{
|
{
|
||||||
$text = $matches[2];
|
$text = $matches[2];
|
||||||
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
|
||||||
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
@ -1181,8 +1244,6 @@ class Parsedown
|
|||||||
'attributes' => array(
|
'attributes' => array(
|
||||||
'src' => $Link['element']['attributes']['href'],
|
'src' => $Link['element']['attributes']['href'],
|
||||||
'alt' => $Link['element']['text'],
|
'alt' => $Link['element']['text'],
|
||||||
// @CHANGE LDR
|
|
||||||
'class' => (!empty($Link['element']['attributes']['class']) ? $Link['element']['attributes']['class'] : '')
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1199,6 +1260,7 @@ class Parsedown
|
|||||||
$Element = array(
|
$Element = array(
|
||||||
'name' => 'a',
|
'name' => 'a',
|
||||||
'handler' => 'line',
|
'handler' => 'line',
|
||||||
|
'nonNestables' => array('Url', 'Link'),
|
||||||
'text' => null,
|
'text' => null,
|
||||||
'attributes' => array(
|
'attributes' => array(
|
||||||
'href' => null,
|
'href' => null,
|
||||||
@ -1233,13 +1295,6 @@ class Parsedown
|
|||||||
}
|
}
|
||||||
|
|
||||||
$extent += strlen($matches[0]);
|
$extent += strlen($matches[0]);
|
||||||
|
|
||||||
// @CHANGE LDR
|
|
||||||
if (preg_match('/{([^}]+)}/', $remainder, $matches2))
|
|
||||||
{
|
|
||||||
$Element['attributes']['class'] = $matches2[1];
|
|
||||||
$remainder = preg_replace('/{'.preg_quote($matches2[1],'/').'}/', '', $remainder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1266,8 +1321,6 @@ class Parsedown
|
|||||||
$Element['attributes']['title'] = $Definition['title'];
|
$Element['attributes']['title'] = $Definition['title'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'extent' => $extent,
|
'extent' => $extent,
|
||||||
'element' => $Element,
|
'element' => $Element,
|
||||||
@ -1276,12 +1329,12 @@ class Parsedown
|
|||||||
|
|
||||||
protected function inlineMarkup($Excerpt)
|
protected function inlineMarkup($Excerpt)
|
||||||
{
|
{
|
||||||
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
|
||||||
{
|
{
|
||||||
return;
|
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(
|
return array(
|
||||||
'markup' => $matches[0],
|
'markup' => $matches[0],
|
||||||
@ -1297,7 +1350,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(
|
return array(
|
||||||
'markup' => $matches[0],
|
'markup' => $matches[0],
|
||||||
@ -1356,14 +1409,16 @@ class Parsedown
|
|||||||
|
|
||||||
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
||||||
{
|
{
|
||||||
|
$url = $matches[0][0];
|
||||||
|
|
||||||
$Inline = array(
|
$Inline = array(
|
||||||
'extent' => strlen($matches[0][0]),
|
'extent' => strlen($matches[0][0]),
|
||||||
'position' => $matches[0][1],
|
'position' => $matches[0][1],
|
||||||
'element' => array(
|
'element' => array(
|
||||||
'name' => 'a',
|
'name' => 'a',
|
||||||
'text' => $matches[0][0],
|
'text' => $url,
|
||||||
'attributes' => array(
|
'attributes' => array(
|
||||||
'href' => $matches[0][0],
|
'href' => $url,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1376,7 +1431,7 @@ class Parsedown
|
|||||||
{
|
{
|
||||||
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
||||||
{
|
{
|
||||||
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
$url = $matches[1];
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'extent' => strlen($matches[0]),
|
'extent' => strlen($matches[0]),
|
||||||
@ -1414,6 +1469,11 @@ class Parsedown
|
|||||||
|
|
||||||
protected function element(array $Element)
|
protected function element(array $Element)
|
||||||
{
|
{
|
||||||
|
if ($this->safeMode)
|
||||||
|
{
|
||||||
|
$Element = $this->sanitiseElement($Element);
|
||||||
|
}
|
||||||
|
|
||||||
$markup = '<'.$Element['name'];
|
$markup = '<'.$Element['name'];
|
||||||
|
|
||||||
if (isset($Element['attributes']))
|
if (isset($Element['attributes']))
|
||||||
@ -1425,23 +1485,45 @@ class Parsedown
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$markup .= ' '.$name.'="'.$value.'"';
|
$markup .= ' '.$name.'="'.self::escape($value).'"';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$permitRawHtml = false;
|
||||||
|
|
||||||
if (isset($Element['text']))
|
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 .= '>';
|
$markup .= '>';
|
||||||
|
|
||||||
|
if (!isset($Element['nonNestables']))
|
||||||
|
{
|
||||||
|
$Element['nonNestables'] = array();
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($Element['handler']))
|
if (isset($Element['handler']))
|
||||||
{
|
{
|
||||||
// @CHANGE LDR
|
$markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
|
||||||
//$markup .= $this->{$Element['handler']}($Element['text']);
|
}
|
||||||
$markup .= preg_replace('/>{[^}]+}/', '>', $this->{$Element['handler']}($Element['text']));
|
elseif (!$permitRawHtml)
|
||||||
|
{
|
||||||
|
$markup .= self::escape($text, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$markup .= $Element['text'];
|
$markup .= $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
$markup .= '</'.$Element['name'].'>';
|
$markup .= '</'.$Element['name'].'>';
|
||||||
@ -1500,10 +1582,77 @@ class Parsedown
|
|||||||
return $markup;
|
return $markup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function sanitiseElement(array $Element)
|
||||||
|
{
|
||||||
|
static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
|
||||||
|
static $safeUrlNameToAtt = array(
|
||||||
|
'a' => 'href',
|
||||||
|
'img' => 'src',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($safeUrlNameToAtt[$Element['name']]))
|
||||||
|
{
|
||||||
|
$Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty($Element['attributes']))
|
||||||
|
{
|
||||||
|
foreach ($Element['attributes'] as $att => $val)
|
||||||
|
{
|
||||||
|
# filter out badly parsed attribute
|
||||||
|
if ( ! preg_match($goodAttribute, $att))
|
||||||
|
{
|
||||||
|
unset($Element['attributes'][$att]);
|
||||||
|
}
|
||||||
|
# dump onevent attribute
|
||||||
|
elseif (self::striAtStart($att, 'on'))
|
||||||
|
{
|
||||||
|
unset($Element['attributes'][$att]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
|
||||||
|
{
|
||||||
|
foreach ($this->safeLinksWhitelist as $scheme)
|
||||||
|
{
|
||||||
|
if (self::striAtStart($Element['attributes'][$attribute], $scheme))
|
||||||
|
{
|
||||||
|
return $Element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
|
||||||
|
|
||||||
|
return $Element;
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Static Methods
|
# Static Methods
|
||||||
#
|
#
|
||||||
|
|
||||||
|
protected static function escape($text, $allowQuotes = false)
|
||||||
|
{
|
||||||
|
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')
|
static function instance($name = 'default')
|
||||||
{
|
{
|
||||||
if (isset(self::$instances[$name]))
|
if (isset(self::$instances[$name]))
|
||||||
@ -1554,10 +1703,10 @@ class Parsedown
|
|||||||
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
||||||
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
||||||
'q', 'rt', 'ins', 'font', 'strong',
|
'q', 'rt', 'ins', 'font', 'strong',
|
||||||
's', 'tt', 'sub', 'mark',
|
's', 'tt', 'kbd', 'mark',
|
||||||
'u', 'xm', 'sup', 'nobr',
|
'u', 'xm', 'sub', 'nobr',
|
||||||
'var', 'ruby',
|
'sup', 'ruby',
|
||||||
'wbr', 'span',
|
'var', 'span',
|
||||||
'time',
|
'wbr', 'time',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
> You might also like [Caret](http://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 and PC.
|
||||||
|
|
||||||
## Parsedown
|
## Parsedown
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ Better Markdown Parser in PHP
|
|||||||
### Features
|
### Features
|
||||||
|
|
||||||
* One File
|
* One File
|
||||||
|
* No Dependencies
|
||||||
* Super Fast
|
* Super Fast
|
||||||
* Extensible
|
* Extensible
|
||||||
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
|
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
|
||||||
@ -35,6 +36,35 @@ echo $Parsedown->text('Hello _Parsedown_!'); # prints: <p>Hello <em>Parsedown</e
|
|||||||
|
|
||||||
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI).
|
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI).
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
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/).
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
### Questions
|
||||||
|
|
||||||
**How does Parsedown work?**
|
**How does Parsedown work?**
|
||||||
@ -49,7 +79,7 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
|
|||||||
|
|
||||||
**Who uses it?**
|
**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).
|
[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?**
|
**How can I help?**
|
||||||
|
|
||||||
|
|||||||
@ -13,9 +13,21 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.3.0"
|
"php": ">=5.3.0",
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^4.8.35"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {"Parsedown": ""}
|
"psr-0": {"Parsedown": ""}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-0": {
|
||||||
|
"TestParsedown": "test/",
|
||||||
|
"ParsedownTest": "test/",
|
||||||
|
"CommonMarkTest": "test/",
|
||||||
|
"CommonMarkTestWeak": "test/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<phpunit bootstrap="test/bootstrap.php" colors="true">
|
|
||||||
<testsuites>
|
|
||||||
<testsuite>
|
|
||||||
<file>test/ParsedownTest.php</file>
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
</phpunit>
|
|
||||||
Loading…
Reference in New Issue
Block a user