Merge remote-tracking branch 'origin/support/2.7' into develop

# Conflicts:
#	core/attributedef.class.inc.php
#	core/config.class.inc.php
#	core/htmlsanitizer.class.inc.php
#	sources/Renderer/RenderingOutput.php
#	test/core/sanitizer/HTMLDOMSanitizerTest.php
#	test/integration/DictionariesConsistencyTest.php
This commit is contained in:
Pierre Goiffon
2021-11-24 15:01:38 +01:00
14 changed files with 516 additions and 235 deletions

View File

@@ -8130,6 +8130,25 @@ class AttributeImage extends AttributeBlob
return "Image";
}
/**
* {@inheritDoc}
* @see AttributeBlob::MakeRealValue()
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
if (($oDoc instanceof ormDocument)
&& (false === $oDoc->IsEmpty())
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
}
// The validation of the MIME Type is done by CheckFormat below
return $oDoc;
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}

View File

@@ -1125,6 +1125,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'svg_sanitizer' => [
'type' => 'string',
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
'default' => 'SvgDOMSanitizer',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'inline_image_max_display_width' => [
'type' => 'integer',
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',

View File

@@ -97,64 +97,163 @@ class HTMLNullSanitizer extends HTMLSanitizer
{
return $sHTML;
}
}
/**
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
* Complete but quite slow
* http://htmlpurifier.org
* Common implementation for sanitizer using DOM parsing
*/
/*
class HTMLPurifierSanitizer extends HTMLSanitizer
abstract class DOMSanitizer extends HTMLSanitizer
{
protected static $oPurifier = null;
/** @var DOMDocument */
protected $oDoc;
public function __construct()
{
if (self::$oPurifier == null)
{
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
if (!file_exists($sLibPath))
{
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
}
require_once($sLibPath);
abstract public function GetTagsWhiteList();
$oPurifierConfig = HTMLPurifier_Config::createDefault();
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
$oPurifierConfig->set('URI.AllowedSchemes', array (
'http' => true,
'https' => true,
'data' => true, // This one is not present by default
));
$sPurifierCache = APPROOT.'data/HTMLPurifier';
if (!is_dir($sPurifierCache))
{
mkdir($sPurifierCache);
}
if (!is_dir($sPurifierCache))
{
throw new Exception("Could not create the cache directory '$sPurifierCache'");
}
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
}
}
abstract public function GetTagsBlackList();
abstract public function GetAttrsWhiteList();
abstract public function GetAttrsBlackList();
abstract public function GetStylesWhiteList();
public function DoSanitize($sHTML)
{
$sCleanHtml = self::$oPurifier->purify($sHTML);
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
$this->LoadDoc($sHTML);
$this->CleanNode($this->oDoc);
$sCleanHtml = $this->PrintDoc();
return $sCleanHtml;
}
abstract public function LoadDoc($sHTML);
/**
* @return string cleaned source
* @uses \DOMSanitizer::oDoc
*/
abstract public function PrintDoc();
protected function CleanNode(DOMNode $oElement)
{
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes()) {
foreach ($oElement->attributes as $oAttr) {
$sAttr = strtolower($oAttr->name);
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
$aAttrToRemove[] = $oAttr->name;
} else if ((false === empty($this->GetTagsWhiteList()))
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
$aAttrToRemove[] = $oAttr->name;
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else if ($sAttr == 'style') {
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '') {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else {
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode) {
if ($oNode instanceof DOMElement) {
$sNodeTagName = strtolower($oNode->tagName);
}
if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsBlackList()))
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
$aChildElementsToRemove[] = $oNode;
} else if (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsWhiteList()))
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
$aChildElementsToRemove[] = $oNode;
} else if ($oNode instanceof DOMComment) {
$aChildElementsToRemove[] = $oNode;
} else {
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
InlineImage::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
}
protected function IsValidAttributeContent($sAttributeName, $sValue)
{
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttributeName, $this->GetAttrsBlackList(), true))) {
return true;
}
if (array_key_exists($sAttributeName, $this->GetAttrsWhiteList())) {
return preg_match($this->GetAttrsWhiteList()[$sAttributeName], $sValue);
}
return true;
}
protected function CleanStyle($sStyle)
{
if (empty($this->GetStylesWhiteList())) {
return $sStyle;
}
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach ($aItems as $sItem) {
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), $this->GetStylesWhiteList())) {
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
}
}
*/
class HTMLDOMSanitizer extends HTMLSanitizer
class HTMLDOMSanitizer extends DOMSanitizer
{
protected $oDoc;
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
@@ -239,6 +338,31 @@ class HTMLDOMSanitizer extends HTMLSanitizer
'white-space',
);
public function GetTagsWhiteList()
{
return static::$aTagsWhiteList;
}
public function GetTagsBlackList()
{
return [];
}
public function GetAttrsWhiteList()
{
return static::$aAttrsWhiteList;
}
public function GetAttrsBlackList()
{
return [];
}
public function GetStylesWhiteList()
{
return static::$aStylesWhiteList;
}
public function __construct()
{
parent::__construct();
@@ -264,139 +388,152 @@ class HTMLDOMSanitizer extends HTMLSanitizer
}
}
public function DoSanitize($sHTML)
public function LoadDoc($sHTML)
{
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
// therefore we have to do the transformation upfront
$sHTML = preg_replace('@<o:p>(\s|&nbsp;)*</o:p>@', '<br>', $sHTML);
// Replace badly encoded non breaking space
$sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML);
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
$this->oDoc->preserveWhitespace = true;
}
$this->CleanNode($this->oDoc);
public function PrintDoc()
{
$oXPath = new DOMXPath($this->oDoc);
$sXPath = "//body";
$oNodesList = $oXPath->query($sXPath);
if ($oNodesList->length == 0)
{
if ($oNodesList->length == 0) {
// No body, save the whole document
$sCleanHtml = $this->oDoc->saveHTML();
}
else
{
} else {
// Export only the content of the body tag
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
// remove the body tag itself
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
}
return $sCleanHtml;
}
}
protected function CleanNode(DOMNode $oElement)
/**
* @since 2.6.5 2.7.6 3.0.0 N°4360
*/
class SvgDOMSanitizer extends DOMSanitizer
{
public function GetTagsWhiteList()
{
$aAttrToRemove = array();
// Gather the attributes to remove
if ($oElement->hasAttributes())
{
foreach($oElement->attributes as $oAttr)
{
$sAttr = strtolower($oAttr->name);
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
{
// Forbidden (or unknown) attribute
$aAttrToRemove[] = $oAttr->name;
}
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else if ($sAttr == 'style')
{
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '')
{
// Invalid content
$aAttrToRemove[] = $oAttr->name;
}
else
{
$oElement->setAttribute($oAttr->name, $sCleanStyle);
}
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode)
{
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
{
$aChildElementsToRemove[] = $oNode;
}
else if ($oNode instanceof DOMComment)
{
$aChildElementsToRemove[] = $oNode;
}
else
{
// Recurse
$this->CleanNode($oNode);
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
{
InlineImage::ProcessImageTag($oNode);
}
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
$oElement->removeChild($oDomElement);
}
}
return [];
}
protected function CleanStyle($sStyle)
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
*/
public function GetTagsBlackList()
{
$aAllowedStyles = array();
$aItems = explode(';', $sStyle);
{
foreach($aItems as $sItem)
{
$aElements = explode(':', trim($sItem));
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
{
$aAllowedStyles[] = trim($sItem);
}
}
}
return implode(';', $aAllowedStyles);
return [
'script',
];
}
protected function IsValidAttributeContent($sAttributeName, $sValue)
public function GetAttrsWhiteList()
{
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
{
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
}
return true;
return [];
}
/**
* @return string[]
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
*/
public function GetAttrsBlackList()
{
return [
'onbegin',
'onbegin',
'onrepeat',
'onabort',
'onerror',
'onerror',
'onscroll',
'onunload',
'oncopy',
'oncut',
'onpaste',
'oncancel',
'oncanplay',
'oncanplaythrough',
'onchange',
'onclick',
'onclose',
'oncuechange',
'ondblclick',
'ondrag',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onended',
'onerror',
'onfocus',
'oninput',
'oninvalid',
'onkeydown',
'onkeypress',
'onkeyup',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onpause',
'onplay',
'onplaying',
'onprogress',
'onratechange',
'onreset',
'onresize',
'onscroll',
'onseeked',
'onseeking',
'onselect',
'onshow',
'onstalled',
'onsubmit',
'onsuspend',
'ontimeupdate',
'ontoggle',
'onvolumechange',
'onwaiting',
'onactivate',
'onfocusin',
'onfocusout',
];
}
public function GetStylesWhiteList()
{
return [];
}
public function LoadDoc($sHTML)
{
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
}
public function PrintDoc()
{
return $this->oDoc->saveXML();
}
}

View File

@@ -138,7 +138,7 @@ final class ormTagSet extends ormSet
}
/**
* @return array of tags indexed by code
* @return array index: code, value: corresponding {@see \TagSetFieldData}
*/
public function GetTags()
{

View File

@@ -107,18 +107,36 @@ EOF
// ... in view mode
else
{
$aItems = $oOrmItemSet->GetTags();
if ($oOrmItemSet instanceof \ormTagSet) {
$aItems = $oOrmItemSet->GetTags();
$fExtractTagData = static function($oTag, &$sItemLabel, &$sItemDescription) {
$sItemLabel = $oTag->Get('label');
$sItemDescription = $oTag->Get('description');
};
} else {
$aItems = $oOrmItemSet->GetValues();
$oAttDef = MetaModel::GetAttributeDef($oOrmItemSet->GetClass(), $oOrmItemSet->GetAttCode());
$fExtractTagData = static function($sEnumSetValue, &$sItemLabel, &$sItemDescription) use ($oAttDef) {
$sItemLabel = $oAttDef->GetValueLabel($sEnumSetValue);
$sItemDescription = '';
};
}
$oOutput->AddHtml('<div class="form-control-static">')
->AddHtml('<span class="label-group">');
foreach($aItems as $sItemCode => $oItem)
foreach($aItems as $sItemCode => $value)
{
$sItemLabel = $oItem->Get('label');
$sItemDescription = $oItem->Get('description');
$fExtractTagData($value, $sItemLabel, $sItemDescription);
$sDescriptionAttr = (empty($sItemDescription))
? ''
: ' data-description="'.utils::HtmlEntities($sItemDescription).'"';
$oOutput->AddHtml('<span class="label label-default" data-code="'.$sItemCode.'" data-label="')
->AddHtml($sItemLabel, true)
->AddHtml('" data-description="')
->AddHtml($sItemDescription, true)
->AddHtml('">')
->AddHtml('"')
->AddHtml($sDescriptionAttr)
->AddHtml('>')
->AddHtml($sItemLabel, true)
->AddHtml('</span>');
}

View File

@@ -20,6 +20,8 @@
namespace Combodo\iTop\Renderer;
use utils;
/**
* Description of RenderingOutput
*
@@ -111,15 +113,15 @@ class RenderingOutput
/**
*
* @param string $sHtml
* @param bool $bEncodeHtmlEntities
* @param ?string $sHtml
* @param bool $bEscapeHtmlEntities
*
* @return \Combodo\iTop\Renderer\RenderingOutput
*/
public function AddHtml(?string $sHtml, bool $bEncodeHtmlEntities = false)
public function AddHtml(?string $sHtml, bool $bEscapeHtmlEntities = false)
{
if (!is_null($sHtml)) {
$this->sHtml .= ($bEncodeHtmlEntities) ? htmlentities($sHtml, ENT_QUOTES, 'UTF-8') : $sHtml;
$this->sHtml .= ($bEscapeHtmlEntities) ? utils::Escapehtml($sHtml) : $sHtml;
}
return $this;

View File

@@ -0,0 +1,75 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core\Sanitizer;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
abstract class AbstractDOMSanitizerTest extends ItopTestCase
{
const INPUT_DIRECTORY = 'input';
const OUTPUT_DIRECTORY = 'output';
protected function setUp()
{
parent::setUp();
require_once(APPROOT.'application/utils.inc.php');
require_once(APPROOT.'core/htmlsanitizer.class.inc.php');
require_once(APPROOT.'test/core/sanitizer/InlineImageMock.php');
}
protected function ReadTestFile($sFileToTest, $sFolderName)
{
$sCurrentPath = __DIR__;
return file_get_contents($sCurrentPath.DIRECTORY_SEPARATOR
.$sFolderName.DIRECTORY_SEPARATOR
.$sFileToTest);
}
protected function RemoveNewLines($sText)
{
$sText = str_replace("\r\n", "\n", $sText);
$sText = str_replace("\r", "\n", $sText);
$sText = str_replace("\n", '', $sText);
return $sText;
}
/**
* Generates an appropriate value for the given attribute, or use the counter if needed.
* This is necessary as most of the attributes with empty or inappropriate values (like a numeric for a href) are removed by the parser
*
* @param string $sTagAttribute
* @param int $iAttributeCounter
*
* @return string attribute value
*/
protected function GetTagAttributeValue($sTagAttribute, $iAttributeCounter)
{
$sTagAttrValue = ' '.$sTagAttribute.'="';
if (in_array($sTagAttribute, array('href', 'src'))) {
return $sTagAttrValue.'http://www.combodo.com"';
}
if ($sTagAttribute === 'style') {
return $sTagAttrValue.'color: black"';
}
return $sTagAttrValue.$iAttributeCounter.'"';
}
protected function IsClosingTag($sTag)
{
if (in_array($sTag, array('br', 'img', 'hr'))) {
return false;
}
return true;
}
}

View File

@@ -1,20 +1,19 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core\Sanitizer;
namespace Combodo\iTop\Test\UnitTest\Core;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
use HTMLDOMSanitizer;
require_once __DIR__.'/AbstractDOMSanitizerTest.php';
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class HTMLDOMSanitizerTest extends ItopTestCase
class HTMLDOMSanitizerTest extends AbstractDOMSanitizerTest
{
const INPUT_DIRECTORY = 'sanitizer/input';
const OUTPUT_DIRECTORY = 'sanitizer/output';
/**
* @dataProvider DoSanitizeProvider
*
@@ -41,34 +40,15 @@ class HTMLDOMSanitizerTest extends ItopTestCase
$this->assertEquals($sOutputHtml, $sRes);
}
private function ReadTestFile($sFileToTest, $sFolderName)
{
$sCurrentPath = __DIR__;
return file_get_contents($sCurrentPath.DIRECTORY_SEPARATOR
.$sFolderName.DIRECTORY_SEPARATOR
.$sFileToTest);
}
private function RemoveNewLines($sText)
{
$sText = str_replace("\r\n", "\n", $sText);
$sText = str_replace("\r", "\n", $sText);
$sText = str_replace("\n", '', $sText);
return $sText;
}
public function DoSanitizeProvider()
{
return array(
array(
'utf-8_wrong_character_email_truncated.txt',
'scripts.html',
),
);
}
/**
* @dataProvider WhiteListProvider
*
@@ -146,13 +126,11 @@ class HTMLDOMSanitizerTest extends ItopTestCase
$aTestCaseArray = array();
$sInputText = $this->ReadTestFile('whitelist_test.html', self::INPUT_DIRECTORY);
foreach ($aTagsWhiteList as $sTag => $aTagAttributes)
{
foreach ($aTagsWhiteList as $sTag => $aTagAttributes) {
$sTestCaseText = $sInputText;
$sStartTag = "<$sTag";
$iAttrCounter = 0;
foreach ($aTagAttributes as $sTagAttribute)
{
foreach ($aTagAttributes as $sTagAttribute) {
$sStartTag .= $this->GetTagAttributeValue($sTagAttribute, $iAttrCounter);
$iAttrCounter++;
}
@@ -168,41 +146,6 @@ class HTMLDOMSanitizerTest extends ItopTestCase
return $aTestCaseArray;
}
/**
* Generates an appropriate value for the given attribute, or use the counter if needed.
* This is necessary as most of the attributes with empty or inappropriate values (like a numeric for a href) are removed by the parser
*
* @param string $sTagAttribute
* @param int $iAttributeCounter
*
* @return string attribute value
*/
private function GetTagAttributeValue($sTagAttribute, $iAttributeCounter)
{
$sTagAttrValue = ' '.$sTagAttribute.'="';
if (in_array($sTagAttribute, array('href', 'src')))
{
return $sTagAttrValue.'http://www.combodo.com"';
}
if ($sTagAttribute === 'style')
{
return $sTagAttrValue.'color: black"';
}
return $sTagAttrValue.$iAttributeCounter.'"';
}
private function IsClosingTag($sTag)
{
if (in_array($sTag, array('br', 'img', 'hr')))
{
return false;
}
return true;
}
/**
* @dataProvider RemoveBlackListedTagContentProvider
*/

View File

@@ -0,0 +1,53 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Core\Sanitizer;
use SvgDOMSanitizer;
require_once __DIR__.'/AbstractDOMSanitizerTest.php';
/**
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
* @backupGlobals disabled
*/
class SvgDOMSanitizerTest extends AbstractDOMSanitizerTest
{
/**
* @dataProvider DoSanitizeProvider
*
* @param string $sFileToTest filename
*/
public function testDoSanitize($sFileToTest)
{
$sInputHtml = $this->ReadTestFile($sFileToTest, self::INPUT_DIRECTORY);
$sOutputHtml = $this->ReadTestFile($sFileToTest, self::OUTPUT_DIRECTORY);
$sOutputHtml = $this->RemoveNewLines($sOutputHtml);
$oSanitizer = new SvgDOMSanitizer();
$sRes = $oSanitizer->DoSanitize($sInputHtml);
// Removing newlines as the parser gives different results depending on the PHP version
// Didn't manage to get it right :
// - no php.ini difference
// - playing with the parser preserveWhitespace/formatOutput parser options didn't help
// So we're removing new lines on both sides :/
$sOutputHtml = $this->RemoveNewLines($sOutputHtml);
$sRes = $this->RemoveNewLines($sRes);
$this->debug($sRes);
$this->assertEquals($sOutputHtml, $sRes);
}
public function DoSanitizeProvider()
{
return array(
array(
'scripts.svg',
),
);
}
}

View File

@@ -0,0 +1,7 @@
<h1>Test with lots of JS scripts to filter !</h1>
<p><img src="http://toto.invalid/" onerror="alert('hello world !');"></p>
<script>
alert("hello world !");
</script>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" onload="alert('hello world !');">
<rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)"/>
<script type="text/javascript">
alert("XSS");
</script>
</svg>

After

Width:  |  Height:  |  Size: 418 B

View File

@@ -0,0 +1,3 @@
<h1>Test with lots of JS scripts to filter !</h1>
<p><img src="http://toto.invalid/"></p>

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full"><rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)"/></svg>

After

Width:  |  Height:  |  Size: 305 B

View File

@@ -34,7 +34,10 @@ class DictionariesConsistencyTest extends ItopTestCase
'da' => array('DA DA', 'Danish', 'Dansk'),
'de' => array('DE DE', 'German', 'Deutsch'),
'en' => array('EN US', 'English', 'English'),
'es_cr' => array('ES CR', 'Spanish', 'Español, Castellano'),
'es_cr' => array('ES CR', 'Spanish', array(
'Español, Castellaño', // old value
'Español, Castellano', // new value since N°3635
)),
'fr' => array('FR FR', 'French', 'Français'),
'hu' => array('HU HU', 'Hungarian', 'Magyar'),
'it' => array('IT IT', 'Italian', 'Italiano'),
@@ -57,7 +60,7 @@ class DictionariesConsistencyTest extends ItopTestCase
static::fail("Unknown prefix '$sLangPrefix' for dictionary file '$sDictFile'");
}
[$sExpectedLanguageCode, $sExpectedEnglishLanguageDesc, $sExpectedLocalizedLanguageDesc] = $aPrefixToLanguageData[$sLangPrefix];
[$sExpectedLanguageCode, $sExpectedEnglishLanguageDesc, $aExpectedLocalizedLanguageDesc] = $aPrefixToLanguageData[$sLangPrefix];
$sDictPHP = file_get_contents($sDictFile);
$iCount = preg_match_all("@Dict::Add\('(.*)'\s*,\s*'(.*)'\s*,\s*'(.*)'@", $sDictPHP, $aMatches);
@@ -76,8 +79,12 @@ class DictionariesConsistencyTest extends ItopTestCase
static::assertSame($sExpectedEnglishLanguageDesc, $sEnglishLanguageDesc,
"Unexpected language description (english) for Dict::Add in dictionary $sDictFile");
}
foreach ($aMatches[3] as $sLocalizedLanguageDesc) {
static::assertSame($sExpectedLocalizedLanguageDesc, $sLocalizedLanguageDesc,
foreach ($aMatches[3] as $sLocalizedLanguageDesc)
{
if (false === is_array($aExpectedLocalizedLanguageDesc)) {
$aExpectedLocalizedLanguageDesc = array($aExpectedLocalizedLanguageDesc);
}
static::assertContains($sLocalizedLanguageDesc,$aExpectedLocalizedLanguageDesc,
"Unexpected language description for Dict::Add in dictionary $sDictFile");
}
}