refactor: Cleanup git state - commit all staged changes
Major refactoring cleanup: - Add new controller architecture (class-controller-*.php) - Add new settings-v2 UI (views/settings-v2/) - Add new CSS architecture (agentic-sidebar.css, tokens) - Add esbuild build pipeline (scripts/build.js, package.json) - Add composer dependencies (vendor/) - Add frontend src directory (assets/js/src/index.jsx) - Add documentation files - Remove old/obsolete files (class-settings.php, old CSS) This commits all pending changes from previous refactoring efforts.
This commit is contained in:
41
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/AttributeSanitizerInterface.php
vendored
Normal file
41
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/AttributeSanitizerInterface.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
|
||||
|
||||
/**
|
||||
* Implements attribute-specific sanitization logic.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
interface AttributeSanitizerInterface
|
||||
{
|
||||
/**
|
||||
* Returns the list of element names supported, or null to support all elements.
|
||||
*
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getSupportedElements(): ?array;
|
||||
|
||||
/**
|
||||
* Returns the list of attributes names supported, or null to support all attributes.
|
||||
*
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getSupportedAttributes(): ?array;
|
||||
|
||||
/**
|
||||
* Returns the sanitized value of a given attribute for the given element.
|
||||
*/
|
||||
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string;
|
||||
}
|
||||
56
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/MetaRefreshAttributeSanitizer.php
vendored
Normal file
56
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/MetaRefreshAttributeSanitizer.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\UrlSanitizer;
|
||||
|
||||
/**
|
||||
* Sanitizes the URL embedded in the content attribute of a <meta http-equiv="refresh">
|
||||
* element, since the http-equiv value is not visible from a per-attribute sanitizer.
|
||||
*
|
||||
* The content attribute carries an unrelated value for other meta types (description,
|
||||
* keywords, generator…), which is passed through unchanged.
|
||||
*/
|
||||
final class MetaRefreshAttributeSanitizer implements AttributeSanitizerInterface
|
||||
{
|
||||
public function getSupportedElements(): ?array
|
||||
{
|
||||
return ['meta'];
|
||||
}
|
||||
|
||||
public function getSupportedAttributes(): ?array
|
||||
{
|
||||
return ['content'];
|
||||
}
|
||||
|
||||
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string
|
||||
{
|
||||
if (!preg_match('/^(\s*\d+\s*[;,]\s*url\s*=\s*)(["\']?)(.+?)\2(\s*)$/i', $value, $m)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$sanitized = UrlSanitizer::sanitize(
|
||||
$m[3],
|
||||
$config->getAllowedLinkSchemes(),
|
||||
$config->getForceHttpsUrls(),
|
||||
$config->getAllowedLinkHosts(),
|
||||
$config->getAllowRelativeLinks(),
|
||||
);
|
||||
|
||||
if (null === $sanitized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $m[1].$m[2].$sanitized.$m[2].$m[4];
|
||||
}
|
||||
}
|
||||
53
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/UrlAttributeSanitizer.php
vendored
Normal file
53
vendor/symfony/html-sanitizer/Visitor/AttributeSanitizer/UrlAttributeSanitizer.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\UrlSanitizer;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class UrlAttributeSanitizer implements AttributeSanitizerInterface
|
||||
{
|
||||
public function getSupportedElements(): ?array
|
||||
{
|
||||
// Check all elements for URL attributes
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getSupportedAttributes(): ?array
|
||||
{
|
||||
return ['src', 'href', 'lowsrc', 'background', 'ping', 'action', 'formaction', 'poster', 'cite', 'data', 'codebase', 'archive', 'longdesc'];
|
||||
}
|
||||
|
||||
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string
|
||||
{
|
||||
if (\in_array($element, ['a', 'area'], true) || \in_array($attribute, ['action', 'formaction', 'cite'], true)) {
|
||||
return UrlSanitizer::sanitize(
|
||||
$value,
|
||||
$config->getAllowedLinkSchemes(),
|
||||
$config->getForceHttpsUrls(),
|
||||
$config->getAllowedLinkHosts(),
|
||||
$config->getAllowRelativeLinks(),
|
||||
);
|
||||
}
|
||||
|
||||
return UrlSanitizer::sanitize(
|
||||
$value,
|
||||
$config->getAllowedMediaSchemes(),
|
||||
$config->getForceHttpsUrls(),
|
||||
$config->getAllowedMediaHosts(),
|
||||
$config->getAllowRelativeMedias(),
|
||||
);
|
||||
}
|
||||
}
|
||||
192
vendor/symfony/html-sanitizer/Visitor/DomVisitor.php
vendored
Normal file
192
vendor/symfony/html-sanitizer/Visitor/DomVisitor.php
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerAction;
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\StringSanitizer;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer\AttributeSanitizerInterface;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Model\Cursor;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\BlockedNode;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\DocumentNode;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\Node;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\NodeInterface;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\TextNode;
|
||||
|
||||
/**
|
||||
* Iterates over the parsed DOM tree to build the sanitized tree.
|
||||
*
|
||||
* The DomVisitor iterates over the parsed DOM tree, visits its nodes and build
|
||||
* a sanitized tree with their attributes and content.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class DomVisitor
|
||||
{
|
||||
private HtmlSanitizerAction $defaultAction = HtmlSanitizerAction::Drop;
|
||||
|
||||
/**
|
||||
* Registry of attributes to forcefully set on nodes, index by element and attribute.
|
||||
*
|
||||
* @var array<string, array<string, string>>
|
||||
*/
|
||||
private array $forcedAttributes;
|
||||
|
||||
/**
|
||||
* Registry of attributes sanitizers indexed by element name and attribute name for
|
||||
* faster sanitization.
|
||||
*
|
||||
* @var array<string, array<string, list<AttributeSanitizerInterface>>>
|
||||
*/
|
||||
private array $attributeSanitizers = [];
|
||||
|
||||
/**
|
||||
* @param array<string, HtmlSanitizerAction|array<string, bool>> $elementsConfig Registry of allowed/blocked elements:
|
||||
* * If an element is present as a key and contains an array, the element should be allowed
|
||||
* and the array is the list of allowed attributes.
|
||||
* * If an element is present as a key and contains an HtmlSanitizerAction, that action applies.
|
||||
* * If an element is not present as a key, the default action applies.
|
||||
*/
|
||||
public function __construct(
|
||||
private HtmlSanitizerConfig $config,
|
||||
private array $elementsConfig,
|
||||
) {
|
||||
$this->forcedAttributes = $config->getForcedAttributes();
|
||||
|
||||
foreach ($config->getAttributeSanitizers() as $attributeSanitizer) {
|
||||
foreach ($attributeSanitizer->getSupportedElements() ?? ['*'] as $element) {
|
||||
foreach ($attributeSanitizer->getSupportedAttributes() ?? ['*'] as $attribute) {
|
||||
$this->attributeSanitizers[$element][$attribute][] = $attributeSanitizer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->defaultAction = $config->getDefaultAction();
|
||||
}
|
||||
|
||||
public function visit(\Dom\Node|\DOMNode $domNode): ?NodeInterface
|
||||
{
|
||||
$cursor = new Cursor(new DocumentNode());
|
||||
$this->visitChildren($domNode, $cursor);
|
||||
|
||||
return $cursor->node;
|
||||
}
|
||||
|
||||
private function visitNode(\Dom\Node|\DOMNode $domNode, Cursor $cursor): void
|
||||
{
|
||||
$nodeName = StringSanitizer::htmlLower($domNode->nodeName);
|
||||
|
||||
// Visit recursively if the node was not dropped
|
||||
if ($this->enterNode($nodeName, $domNode, $cursor)) {
|
||||
$this->visitChildren($domNode, $cursor);
|
||||
$cursor->node = $cursor->node->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
private function enterNode(string $domNodeName, \Dom\Node|\DOMNode $domNode, Cursor $cursor): bool
|
||||
{
|
||||
if (!\array_key_exists($domNodeName, $this->elementsConfig)) {
|
||||
$action = $this->defaultAction;
|
||||
$allowedAttributes = [];
|
||||
} else {
|
||||
if (\is_array($this->elementsConfig[$domNodeName])) {
|
||||
$action = HtmlSanitizerAction::Allow;
|
||||
$allowedAttributes = $this->elementsConfig[$domNodeName];
|
||||
} else {
|
||||
$action = $this->elementsConfig[$domNodeName];
|
||||
$allowedAttributes = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (HtmlSanitizerAction::Drop === $action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Element should be blocked, retaining its children
|
||||
if (HtmlSanitizerAction::Block === $action) {
|
||||
$node = new BlockedNode($cursor->node);
|
||||
|
||||
$cursor->node->addChild($node);
|
||||
$cursor->node = $node;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise create the node
|
||||
$node = new Node($cursor->node, $domNodeName);
|
||||
$this->setAttributes($domNodeName, $domNode, $node, $allowedAttributes);
|
||||
|
||||
// Force configured attributes
|
||||
foreach ($this->forcedAttributes[$domNodeName] ?? [] as $attribute => $value) {
|
||||
$node->setAttribute($attribute, $value, true);
|
||||
}
|
||||
|
||||
$cursor->node->addChild($node);
|
||||
$cursor->node = $node;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function visitChildren(\Dom\Node|\DOMNode $domNode, Cursor $cursor): void
|
||||
{
|
||||
/** @var \Dom\Node|\DOMNode $child */
|
||||
foreach ($domNode->childNodes ?? [] as $child) {
|
||||
if ('#text' === $child->nodeName) {
|
||||
// Add text directly for performance
|
||||
$cursor->node->addChild(new TextNode($cursor->node, $child instanceof \Dom\Node ? ($child->textContent ?? '') : $child->nodeValue));
|
||||
} elseif (!$child instanceof \Dom\Text && !$child instanceof \Dom\ProcessingInstruction && !$child instanceof \DOMText && !$child instanceof \DOMProcessingInstruction) {
|
||||
// Otherwise continue the visit recursively
|
||||
// Ignore comments for security reasons (interpreted differently by browsers)
|
||||
// Ignore processing instructions (treated as comments)
|
||||
$this->visitNode($child, $cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set attributes from a DOM node to a sanitized node.
|
||||
*/
|
||||
private function setAttributes(string $domNodeName, \Dom\Node|\DOMNode $domNode, Node $node, array $allowedAttributes = []): void
|
||||
{
|
||||
/** @var iterable<\Dom\Attr|\DOMAttr> $domAttributes */
|
||||
if (!$domAttributes = $domNode->attributes?->getIterator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($domAttributes as $attribute) {
|
||||
$name = StringSanitizer::htmlLower($attribute->name);
|
||||
|
||||
if (isset($allowedAttributes[$name])) {
|
||||
$value = $attribute->value;
|
||||
|
||||
// Sanitize the attribute value if there are attribute sanitizers for it
|
||||
$attributeSanitizers = array_merge(
|
||||
$this->attributeSanitizers[$domNodeName][$name] ?? [],
|
||||
$this->attributeSanitizers['*'][$name] ?? [],
|
||||
$this->attributeSanitizers[$domNodeName]['*'] ?? [],
|
||||
$this->attributeSanitizers['*']['*'] ?? [],
|
||||
);
|
||||
|
||||
foreach ($attributeSanitizers as $sanitizer) {
|
||||
if (null === $sanitizedValue = $sanitizer->sanitizeAttribute($domNodeName, $name, $value, $this->config)) {
|
||||
continue 2;
|
||||
}
|
||||
$value = $sanitizedValue;
|
||||
}
|
||||
|
||||
$node->setAttribute($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
vendor/symfony/html-sanitizer/Visitor/Model/Cursor.php
vendored
Normal file
26
vendor/symfony/html-sanitizer/Visitor/Model/Cursor.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Model;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\Node\NodeInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Cursor
|
||||
{
|
||||
public function __construct(public ?NodeInterface $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
45
vendor/symfony/html-sanitizer/Visitor/Node/BlockedNode.php
vendored
Normal file
45
vendor/symfony/html-sanitizer/Visitor/Node/BlockedNode.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Node;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class BlockedNode implements NodeInterface
|
||||
{
|
||||
private array $children = [];
|
||||
|
||||
public function __construct(
|
||||
private NodeInterface $parentNode,
|
||||
) {
|
||||
}
|
||||
|
||||
public function addChild(NodeInterface $node): void
|
||||
{
|
||||
$this->children[] = $node;
|
||||
}
|
||||
|
||||
public function getParent(): ?NodeInterface
|
||||
{
|
||||
return $this->parentNode;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$rendered = '';
|
||||
foreach ($this->children as $child) {
|
||||
$rendered .= $child->render();
|
||||
}
|
||||
|
||||
return $rendered;
|
||||
}
|
||||
}
|
||||
40
vendor/symfony/html-sanitizer/Visitor/Node/DocumentNode.php
vendored
Normal file
40
vendor/symfony/html-sanitizer/Visitor/Node/DocumentNode.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Node;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class DocumentNode implements NodeInterface
|
||||
{
|
||||
private array $children = [];
|
||||
|
||||
public function addChild(NodeInterface $node): void
|
||||
{
|
||||
$this->children[] = $node;
|
||||
}
|
||||
|
||||
public function getParent(): ?NodeInterface
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$rendered = '';
|
||||
foreach ($this->children as $child) {
|
||||
$rendered .= $child->render();
|
||||
}
|
||||
|
||||
return $rendered;
|
||||
}
|
||||
}
|
||||
121
vendor/symfony/html-sanitizer/Visitor/Node/Node.php
vendored
Normal file
121
vendor/symfony/html-sanitizer/Visitor/Node/Node.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Node;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\StringSanitizer;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class Node implements NodeInterface
|
||||
{
|
||||
// HTML5 elements which are self-closing
|
||||
private const VOID_ELEMENTS = [
|
||||
'area' => true,
|
||||
'base' => true,
|
||||
'br' => true,
|
||||
'col' => true,
|
||||
'embed' => true,
|
||||
'hr' => true,
|
||||
'img' => true,
|
||||
'input' => true,
|
||||
'keygen' => true,
|
||||
'link' => true,
|
||||
'meta' => true,
|
||||
'param' => true,
|
||||
'source' => true,
|
||||
'track' => true,
|
||||
'wbr' => true,
|
||||
];
|
||||
|
||||
private array $attributes = [];
|
||||
private array $children = [];
|
||||
|
||||
public function __construct(
|
||||
private NodeInterface $parent,
|
||||
private string $tagName,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getParent(): ?NodeInterface
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function getAttribute(string $name): ?string
|
||||
{
|
||||
return $this->attributes[$name] ?? null;
|
||||
}
|
||||
|
||||
public function setAttribute(string $name, ?string $value, bool $override = false): void
|
||||
{
|
||||
// Always use only the first declaration (ease sanitization)
|
||||
if ($override || !\array_key_exists($name, $this->attributes)) {
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function addChild(NodeInterface $node): void
|
||||
{
|
||||
$this->children[] = $node;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
if (isset(self::VOID_ELEMENTS[$this->tagName])) {
|
||||
return '<'.$this->tagName.$this->renderAttributes().' />';
|
||||
}
|
||||
|
||||
$rendered = '<'.$this->tagName.$this->renderAttributes().'>';
|
||||
foreach ($this->children as $child) {
|
||||
$rendered .= $child->render();
|
||||
}
|
||||
|
||||
return $rendered.'</'.$this->tagName.'>';
|
||||
}
|
||||
|
||||
private function renderAttributes(): string
|
||||
{
|
||||
$rendered = [];
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
if (null === $value) {
|
||||
// Tag should be removed as a sanitizer found suspect data inside
|
||||
continue;
|
||||
}
|
||||
|
||||
$attr = StringSanitizer::encodeHtmlEntities($name);
|
||||
|
||||
if ('' !== $value) {
|
||||
// In quirks mode, IE8 does a poor job producing innerHTML values.
|
||||
// If JavaScript does:
|
||||
// nodeA.innerHTML = nodeB.innerHTML;
|
||||
// and nodeB contains (or even if ` was encoded properly):
|
||||
// <div attr="``foo=bar">
|
||||
// then IE8 will produce:
|
||||
// <div attr=``foo=bar>
|
||||
// as the value of nodeB.innerHTML and assign it to nodeA.
|
||||
// IE8's HTML parser treats `` as a blank attribute value and foo=bar becomes a separate attribute.
|
||||
// Adding a space at the end of the attribute prevents this by forcing IE8 to put double
|
||||
// quotes around the attribute when computing nodeB.innerHTML.
|
||||
if (str_contains($value, '`')) {
|
||||
$value .= ' ';
|
||||
}
|
||||
|
||||
$attr .= '="'.StringSanitizer::encodeHtmlEntities($value).'"';
|
||||
}
|
||||
|
||||
$rendered[] = $attr;
|
||||
}
|
||||
|
||||
return $rendered ? ' '.implode(' ', $rendered) : '';
|
||||
}
|
||||
}
|
||||
37
vendor/symfony/html-sanitizer/Visitor/Node/NodeInterface.php
vendored
Normal file
37
vendor/symfony/html-sanitizer/Visitor/Node/NodeInterface.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Node;
|
||||
|
||||
/**
|
||||
* Represents the sanitized version of a DOM node in the sanitized tree.
|
||||
*
|
||||
* Once the sanitization is done, nodes are rendered into the final output string.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
interface NodeInterface
|
||||
{
|
||||
/**
|
||||
* Add a child node to this node.
|
||||
*/
|
||||
public function addChild(self $node): void;
|
||||
|
||||
/**
|
||||
* Return the parent node of this node, or null if it has no parent node.
|
||||
*/
|
||||
public function getParent(): ?self;
|
||||
|
||||
/**
|
||||
* Render this node as a string, recursively rendering its children as well.
|
||||
*/
|
||||
public function render(): string;
|
||||
}
|
||||
41
vendor/symfony/html-sanitizer/Visitor/Node/TextNode.php
vendored
Normal file
41
vendor/symfony/html-sanitizer/Visitor/Node/TextNode.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HtmlSanitizer\Visitor\Node;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\StringSanitizer;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class TextNode implements NodeInterface
|
||||
{
|
||||
public function __construct(
|
||||
private NodeInterface $parentNode,
|
||||
private string $text,
|
||||
) {
|
||||
}
|
||||
|
||||
public function addChild(NodeInterface $node): void
|
||||
{
|
||||
throw new \LogicException('Text nodes cannot have children.');
|
||||
}
|
||||
|
||||
public function getParent(): ?NodeInterface
|
||||
{
|
||||
return $this->parentNode;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
return StringSanitizer::encodeHtmlEntities($this->text);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user