1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-08-02 02:26:57 +03:00
Files
bookstack/app/Util/HtmlDescriptionFilter.php
Dan Brown b80992ca59 Comments: Switched to lexical editor
Required a lot of changes to provide at least a decent attempt at proper
editor teardown control.
Also updates HtmlDescriptionFilter and testing to address issue with bad
child iteration which could lead to missed items.
Renamed editor version from comments to basic as it'll also be used for
item descriptions.
2025-06-25 14:16:01 +01:00

80 lines
2.1 KiB
PHP

<?php
namespace BookStack\Util;
use DOMAttr;
use DOMElement;
use DOMNode;
/**
* Filter to ensure HTML input for description content remains simple and
* to a limited allow-list of elements and attributes.
* More for consistency and to prevent nuisance rather than for security
* (which would be done via a separate content filter and CSP).
*/
class HtmlDescriptionFilter
{
/**
* @var array<string, string[]>
*/
protected static array $allowedAttrsByElements = [
'p' => [],
'a' => ['href', 'title', 'target'],
'ol' => [],
'ul' => [],
'li' => [],
'strong' => [],
'span' => [],
'em' => [],
'br' => [],
];
public static function filterFromString(string $html): string
{
if (empty(trim($html))) {
return '';
}
$doc = new HtmlDocument($html);
$topLevel = [...$doc->getBodyChildren()];
foreach ($topLevel as $child) {
/** @var DOMNode $child */
if ($child instanceof DOMElement) {
static::filterElement($child);
} else {
$child->parentNode->removeChild($child);
}
}
return $doc->getBodyInnerHtml();
}
protected static function filterElement(DOMElement $element): void
{
$elType = strtolower($element->tagName);
$allowedAttrs = static::$allowedAttrsByElements[$elType] ?? null;
if (is_null($allowedAttrs)) {
$element->remove();
return;
}
$attrs = $element->attributes;
for ($i = $attrs->length - 1; $i >= 0; $i--) {
/** @var DOMAttr $attr */
$attr = $attrs->item($i);
$name = strtolower($attr->name);
if (!in_array($name, $allowedAttrs)) {
$element->removeAttribute($attr->name);
}
}
$childNodes = [...$element->childNodes];
foreach ($childNodes as $child) {
if ($child instanceof DOMElement) {
static::filterElement($child);
}
}
}
}