mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-25 03:28:45 +02:00
N°2523 - Align CKEditor and HtmlDomSanitizer denied and allowed lists
This commit is contained in:
@@ -7,6 +7,7 @@ use Combodo\iTop\Application\WebPage\WebPage;
|
|||||||
use Combodo\iTop\Renderer\BlockRenderer;
|
use Combodo\iTop\Renderer\BlockRenderer;
|
||||||
use Combodo\iTop\Renderer\RenderingOutput;
|
use Combodo\iTop\Renderer\RenderingOutput;
|
||||||
use Dict;
|
use Dict;
|
||||||
|
use DOMSanitizer;
|
||||||
use Exception;
|
use Exception;
|
||||||
use ExceptionLog;
|
use ExceptionLog;
|
||||||
use UserRights;
|
use UserRights;
|
||||||
@@ -36,11 +37,12 @@ class CKEditorHelper
|
|||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
static public function GetCkeditorConfiguration(bool $bWithMentions, ?string $sInitialValue, array $aOverloadConfiguration = []) : array
|
public static function GetCkeditorConfiguration(bool $bWithMentions, ?string $sInitialValue, array $aOverloadConfiguration = []) : array
|
||||||
{
|
{
|
||||||
// Extract language from user preferences
|
// Extract language from user preferences
|
||||||
$sLanguageCountry = trim(UserRights::GetUserLanguage());
|
$sLanguageCountry = trim(UserRights::GetUserLanguage());
|
||||||
$sLanguage = strtolower(explode(' ', $sLanguageCountry)[0]);
|
$sLanguage = strtolower(explode(' ', $sLanguageCountry)[0]);
|
||||||
|
$aSanitizerConfiguration = self::GetDOMSanitizerForCKEditor();
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
$aConfiguration = array(
|
$aConfiguration = array(
|
||||||
@@ -52,6 +54,7 @@ class CKEditorHelper
|
|||||||
'objectShortcut' => [
|
'objectShortcut' => [
|
||||||
'buttonLabel' => Dict::S('UI:ObjectShortcutInsert')
|
'buttonLabel' => Dict::S('UI:ObjectShortcutInsert')
|
||||||
],
|
],
|
||||||
|
'htmlSupport' => $aSanitizerConfiguration,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mentions
|
// Mentions
|
||||||
@@ -75,7 +78,7 @@ class CKEditorHelper
|
|||||||
* @return array|array[]
|
* @return array|array[]
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
static private function GetMentionConfiguration() : array
|
private static function GetMentionConfiguration() : array
|
||||||
{
|
{
|
||||||
// initialize feeds
|
// initialize feeds
|
||||||
$aMentionConfiguration = ['feeds' => []];
|
$aMentionConfiguration = ['feeds' => []];
|
||||||
@@ -277,4 +280,75 @@ HTML;
|
|||||||
|
|
||||||
return $aJSRelPaths;
|
return $aJSRelPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DOMSanitizer|null $oSanitizer
|
||||||
|
*
|
||||||
|
* @return array|array[]
|
||||||
|
* @throws \ConfigException
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
public static function GetDOMSanitizerForCKEditor(DOMSanitizer $oSanitizer = null) : array
|
||||||
|
{
|
||||||
|
if($oSanitizer === null) {
|
||||||
|
/* @var $oSanitizer DOMSanitizer */
|
||||||
|
$sSanitizerClass = utils::GetConfig()->Get('html_sanitizer');
|
||||||
|
$oSanitizer = new $sSanitizerClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
$aWhitelist = [
|
||||||
|
'allow' => [],
|
||||||
|
'disallow' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
// Build the allow list
|
||||||
|
foreach ($oSanitizer->GetTagsWhiteList() as $sTag => $aAttributes) {
|
||||||
|
$aAllowedItem = [
|
||||||
|
'name' => $sTag,
|
||||||
|
'attributes' => [],
|
||||||
|
'classes' => false,
|
||||||
|
'styles' => false
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($aAttributes as $aAttr) {
|
||||||
|
if ($aAttr === 'style') {
|
||||||
|
$aAllowedItem['styles'] = array_fill_keys($oSanitizer->GetStylesWhiteList(), true);
|
||||||
|
} elseif ($aAttr === 'class') {
|
||||||
|
$aAllowedItem['classes'] = true;
|
||||||
|
} elseif (isset($oSanitizer->GetAttrsWhiteList()[$aAttr])) {
|
||||||
|
$aAllowedItem['attributes'][$aAttr] = [
|
||||||
|
'pattern' => $oSanitizer->GetAttrsWhiteList()[$aAttr]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$aAllowedItem['attributes'][$aAttr] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($aAllowedItem['attributes'])) {
|
||||||
|
$aAllowedItem['attributes'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aWhitelist['allow'][] = $aAllowedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the disallow list
|
||||||
|
foreach ($oSanitizer->GetTagsBlackList() as $sTag) {
|
||||||
|
$aDisallowedItem = [
|
||||||
|
'name' => $sTag,
|
||||||
|
'attributes' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($oSanitizer->GetAttrsBlackList() as $aAttr) {
|
||||||
|
$aDisallowedItem['attributes'][$aAttr] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($aDisallowedItem['attributes'])) {
|
||||||
|
$aDisallowedItem['attributes'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aWhitelist['disallow'][] = $aDisallowedItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aWhitelist;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -32,4 +32,318 @@ class CKEditorHelperTest extends ItopTestCase
|
|||||||
'GetJSFilesRelPathsForCKEditor' => ['GetJSFilesRelPathsForCKEditor'],
|
'GetJSFilesRelPathsForCKEditor' => ['GetJSFilesRelPathsForCKEditor'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider DOMSanitizerForCKEditorProvider
|
||||||
|
*
|
||||||
|
* @param $aSanitizerConfiguration
|
||||||
|
* @param $aExpectedCKEditorConfiguration
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \ConfigException
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
public function testDOMSanitizerForCKEditor($aSanitizerConfiguration, $aExpectedCKEditorConfiguration)
|
||||||
|
{
|
||||||
|
$oSanitizer = new TestDOMSanitizer();
|
||||||
|
$oSanitizer->SetTagsWhiteList($aSanitizerConfiguration['tagsWhiteList']);
|
||||||
|
$oSanitizer->SetAttrsWhiteList($aSanitizerConfiguration['attrsWhiteList']);
|
||||||
|
$oSanitizer->SetStylesWhiteList($aSanitizerConfiguration['stylesWhiteList']);
|
||||||
|
$oSanitizer->SetTagsBlackList($aSanitizerConfiguration['tagsBlackList']);
|
||||||
|
$oSanitizer->SetAttrsBlackList($aSanitizerConfiguration['attrsBlackList']);
|
||||||
|
|
||||||
|
$aCKEditorConfiguration = CKEditorHelper::GetDOMSanitizerForCKEditor($oSanitizer);
|
||||||
|
$this->assertEquals($aExpectedCKEditorConfiguration, $aCKEditorConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function DOMSanitizerForCKEditorProvider(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Allow list small dataset' => [
|
||||||
|
[
|
||||||
|
'tagsWhiteList' => [
|
||||||
|
'html' => array(),
|
||||||
|
'p' => array('style', 'class'),
|
||||||
|
'a' => array('href', 'name'),
|
||||||
|
],
|
||||||
|
'attrsWhiteList' => [
|
||||||
|
'href' => '/^(https:)/i'
|
||||||
|
],
|
||||||
|
'stylesWhiteList' => [
|
||||||
|
'color',
|
||||||
|
'font-size'
|
||||||
|
],
|
||||||
|
'tagsBlackList' => [],
|
||||||
|
'attrsBlackList' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'allow' => [
|
||||||
|
[
|
||||||
|
'name' => 'html',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => false,
|
||||||
|
'styles' => false
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'p',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font-size' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'a',
|
||||||
|
'attributes' => [
|
||||||
|
'href' => [
|
||||||
|
'pattern' => '/^(https:)/i'
|
||||||
|
],
|
||||||
|
'name' => true
|
||||||
|
],
|
||||||
|
'classes' => false,
|
||||||
|
'styles' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'disallow' => []
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'Allow list medium dataset' => [
|
||||||
|
[
|
||||||
|
'tagsWhiteList' => [
|
||||||
|
'h1' => array('style', 'class'),
|
||||||
|
'h2' => array('style', 'class'),
|
||||||
|
'h3' => array('style', 'class'),
|
||||||
|
'h4' => array('style', 'class'),
|
||||||
|
'table' => array('style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing', 'style'),
|
||||||
|
'tr' => array('style', 'class', 'align', 'valign', 'bgcolor', 'style'),
|
||||||
|
'ul' => array(),
|
||||||
|
'ol' => array(),
|
||||||
|
],
|
||||||
|
'attrsWhiteList' => [
|
||||||
|
'href' => '/^(https:)/i',
|
||||||
|
'src' => '/^(https:)/i',
|
||||||
|
'width' => '/^([0-9]+(px|em|%)?)$/i',
|
||||||
|
'height' => '/^([0-9]+(px|em|%)?)$/i',
|
||||||
|
'align' => '/^(left|right|center|justify)$/i',
|
||||||
|
'valign' => '/^(top|middle|bottom)$/i',
|
||||||
|
'bgcolor' => '/^#[0-9a-f]{6}$/i',
|
||||||
|
],
|
||||||
|
'stylesWhiteList' => [
|
||||||
|
'color',
|
||||||
|
'float',
|
||||||
|
'font',
|
||||||
|
'font-family',
|
||||||
|
'font-size',
|
||||||
|
'font-style',
|
||||||
|
'height',
|
||||||
|
'margin',
|
||||||
|
'padding',
|
||||||
|
'text-align',
|
||||||
|
'vertical-align',
|
||||||
|
'width',
|
||||||
|
'white-space',
|
||||||
|
],
|
||||||
|
'tagsBlackList' => [],
|
||||||
|
'attrsBlackList' => [],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'allow' => [
|
||||||
|
[
|
||||||
|
'name' => 'h1',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'h2',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'h3',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'h4',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'table',
|
||||||
|
'attributes' => [
|
||||||
|
'width' => [
|
||||||
|
'pattern' => '/^([0-9]+(px|em|%)?)$/i'
|
||||||
|
],
|
||||||
|
'summary' => true,
|
||||||
|
'align' => [
|
||||||
|
'pattern' => '/^(left|right|center|justify)$/i'
|
||||||
|
],
|
||||||
|
'border' => true,
|
||||||
|
'cellpadding' => true,
|
||||||
|
'cellspacing' => true,
|
||||||
|
],
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'tr',
|
||||||
|
'attributes' => [
|
||||||
|
'align' => [
|
||||||
|
'pattern' => '/^(left|right|center|justify)$/i'
|
||||||
|
],
|
||||||
|
'valign' => [
|
||||||
|
'pattern' => '/^(top|middle|bottom)$/i'
|
||||||
|
],
|
||||||
|
'bgcolor' => [
|
||||||
|
'pattern' => '/^#[0-9a-f]{6}$/i'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'classes' => true,
|
||||||
|
'styles' => [
|
||||||
|
'color' => true,
|
||||||
|
'font' => true,
|
||||||
|
'font-family' => true,
|
||||||
|
'font-size' => true,
|
||||||
|
'font-style' => true,
|
||||||
|
'height' => true,
|
||||||
|
'margin' => true,
|
||||||
|
'padding' => true,
|
||||||
|
'text-align' => true,
|
||||||
|
'vertical-align' => true,
|
||||||
|
'width' => true,
|
||||||
|
'white-space' => true,
|
||||||
|
'float' => true
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'ul',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => false,
|
||||||
|
'styles' => false
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'ol',
|
||||||
|
'attributes' => false,
|
||||||
|
'classes' => false,
|
||||||
|
'styles' => false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'disallow' => []
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'Disallow list small dataset' => [
|
||||||
|
[
|
||||||
|
'tagsWhiteList' => [],
|
||||||
|
'attrsWhiteList' => [],
|
||||||
|
'stylesWhiteList' => [],
|
||||||
|
'tagsBlackList' => [
|
||||||
|
'html',
|
||||||
|
'p',
|
||||||
|
'a',
|
||||||
|
],
|
||||||
|
'attrsBlackList' => [
|
||||||
|
'href',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'allow' => [],
|
||||||
|
'disallow' => [
|
||||||
|
[
|
||||||
|
'name' => 'html',
|
||||||
|
'attributes' => [
|
||||||
|
'href' => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'p',
|
||||||
|
'attributes' => [
|
||||||
|
'href' => true
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => 'a',
|
||||||
|
'attributes' => [
|
||||||
|
'href' => true
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\Test\UnitTest\Application\Helper;
|
||||||
|
|
||||||
|
|
||||||
|
use DOMSanitizer;
|
||||||
|
|
||||||
|
class TestDOMSanitizer extends DOMSanitizer
|
||||||
|
{
|
||||||
|
protected static array $aTagsWhiteList = [];
|
||||||
|
private static $aStylesWhiteList = [];
|
||||||
|
private static $aAttrsWhiteList = [];
|
||||||
|
|
||||||
|
protected static array $aTagsBlackList = [];
|
||||||
|
private static $aAttrsBlackList = [];
|
||||||
|
|
||||||
|
public function GetTagsWhiteList()
|
||||||
|
{
|
||||||
|
return static::$aTagsWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetTagsBlackList()
|
||||||
|
{
|
||||||
|
return static::$aTagsBlackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAttrsWhiteList()
|
||||||
|
{
|
||||||
|
return static::$aAttrsWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAttrsBlackList()
|
||||||
|
{
|
||||||
|
return static::$aAttrsBlackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetStylesWhiteList()
|
||||||
|
{
|
||||||
|
return static::$aStylesWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetTagsWhiteList(array $aTagsWhiteList)
|
||||||
|
{
|
||||||
|
static::$aTagsWhiteList = $aTagsWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetAttrsWhiteList(array $aAttrsWhiteList)
|
||||||
|
{
|
||||||
|
static::$aAttrsWhiteList = $aAttrsWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetStylesWhiteList(array $aStylesWhiteList)
|
||||||
|
{
|
||||||
|
static::$aStylesWhiteList = $aStylesWhiteList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetTagsBlackList(array $aTagsBlackList)
|
||||||
|
{
|
||||||
|
static::$aTagsBlackList = $aTagsBlackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetAttrsBlackList(array $aAttrsBlackList)
|
||||||
|
{
|
||||||
|
static::$aAttrsBlackList = $aAttrsBlackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function LoadDoc($sHTML)
|
||||||
|
{
|
||||||
|
// TODO: Implement LoadDoc() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function PrintDoc()
|
||||||
|
{
|
||||||
|
// TODO: Implement PrintDoc() method.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user