[增添]添加了datasource的setting数据库以及默认值

This commit is contained in:
makotocc0107
2024-08-27 09:57:44 +08:00
parent d111dfaea4
commit 72eb990970
10955 changed files with 978898 additions and 0 deletions

View 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;
}

View 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'];
}
public function sanitizeAttribute(string $element, string $attribute, string $value, HtmlSanitizerConfig $config): ?string
{
if ('a' === $element) {
return UrlSanitizer::sanitize(
$value,
$config->getAllowedLinkSchemes(),
$config->getForceHttpsUrls(),
$config->getAllowedLinkHosts(),
$config->getAllowRelativeLinks(),
);
}
return UrlSanitizer::sanitize(
$value,
$config->getAllowedMediaSchemes(),
$config->getForceHttpsUrls(),
$config->getAllowedMediaHosts(),
$config->getAllowRelativeMedias(),
);
}
}

View File

@@ -0,0 +1,177 @@
<?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\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 HtmlSanitizerConfig $config;
/**
* 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 "false", the element should be blocked.
* * If an element is not present as a key, the element should be dropped.
*
* @var array<string, false|array<string, bool>>
*/
private array $elementsConfig;
/**
* 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, false|array<string, bool>> $elementsConfig
*/
public function __construct(HtmlSanitizerConfig $config, array $elementsConfig)
{
$this->config = $config;
$this->elementsConfig = $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;
}
}
}
}
public function visit(\DOMDocumentFragment $domNode): ?NodeInterface
{
$cursor = new Cursor(new DocumentNode());
$this->visitChildren($domNode, $cursor);
return $cursor->node;
}
private function visitNode(\DOMNode $domNode, Cursor $cursor): void
{
$nodeName = StringSanitizer::htmlLower($domNode->nodeName);
// Element should be dropped, including its children
if (!\array_key_exists($nodeName, $this->elementsConfig)) {
return;
}
// Otherwise, visit recursively
$this->enterNode($nodeName, $domNode, $cursor);
$this->visitChildren($domNode, $cursor);
$cursor->node = $cursor->node->getParent();
}
private function enterNode(string $domNodeName, \DOMNode $domNode, Cursor $cursor): void
{
// Element should be blocked, retaining its children
if (false === $this->elementsConfig[$domNodeName]) {
$node = new BlockedNode($cursor->node);
$cursor->node->addChild($node);
$cursor->node = $node;
return;
}
// Otherwise create the node
$node = new Node($cursor->node, $domNodeName);
$this->setAttributes($domNodeName, $domNode, $node, $this->elementsConfig[$domNodeName]);
// Force configured attributes
foreach ($this->forcedAttributes[$domNodeName] ?? [] as $attribute => $value) {
$node->setAttribute($attribute, $value);
}
$cursor->node->addChild($node);
$cursor->node = $node;
}
private function visitChildren(\DOMNode $domNode, Cursor $cursor): void
{
/** @var \DOMNode $child */
foreach ($domNode->childNodes ?? [] as $child) {
if ('#text' === $child->nodeName) {
// Add text directly for performance
$cursor->node->addChild(new TextNode($cursor->node, $child->nodeValue));
} elseif (!$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, \DOMNode $domNode, Node $node, array $allowedAttributes = []): void
{
/** @var iterable<\DOMAttr> $domAttributes */
if (!$domAttributes = $domNode->attributes ? $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]['*'] ?? [],
);
foreach ($attributeSanitizers as $sanitizer) {
$value = $sanitizer->sanitizeAttribute($domNodeName, $name, $value, $this->config);
}
$node->setAttribute($name, $value);
}
}
}
}

View 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)
{
}
}

View File

@@ -0,0 +1,46 @@
<?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 NodeInterface $parentNode;
private array $children = [];
public function __construct(NodeInterface $parentNode)
{
$this->parentNode = $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;
}
}

View 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;
}
}

View File

@@ -0,0 +1,123 @@
<?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 NodeInterface $parent;
private string $tagName;
private array $attributes = [];
private array $children = [];
public function __construct(NodeInterface $parent, string $tagName)
{
$this->parent = $parent;
$this->tagName = $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): void
{
// Always use only the first declaration (ease sanitization)
if (!\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) : '';
}
}

View 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;
}

View File

@@ -0,0 +1,39 @@
<?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);
}
}