[增添]添加了datasource的setting数据库以及默认值
This commit is contained in:
12
vendor/symfony/html-sanitizer/CHANGELOG.md
vendored
Normal file
12
vendor/symfony/html-sanitizer/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Add support for sanitizing unlimited length of HTML document
|
||||
|
||||
6.1
|
||||
---
|
||||
|
||||
* Add the component as experimental
|
||||
127
vendor/symfony/html-sanitizer/HtmlSanitizer.php
vendored
Normal file
127
vendor/symfony/html-sanitizer/HtmlSanitizer.php
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\Parser\MastermindsParser;
|
||||
use Symfony\Component\HtmlSanitizer\Parser\ParserInterface;
|
||||
use Symfony\Component\HtmlSanitizer\Reference\W3CReference;
|
||||
use Symfony\Component\HtmlSanitizer\TextSanitizer\StringSanitizer;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\DomVisitor;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class HtmlSanitizer implements HtmlSanitizerInterface
|
||||
{
|
||||
private HtmlSanitizerConfig $config;
|
||||
private ParserInterface $parser;
|
||||
|
||||
/**
|
||||
* @var array<string, DomVisitor>
|
||||
*/
|
||||
private array $domVisitors = [];
|
||||
|
||||
public function __construct(HtmlSanitizerConfig $config, ?ParserInterface $parser = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->parser = $parser ?? new MastermindsParser();
|
||||
}
|
||||
|
||||
public function sanitize(string $input): string
|
||||
{
|
||||
return $this->sanitizeWithContext(W3CReference::CONTEXT_BODY, $input);
|
||||
}
|
||||
|
||||
public function sanitizeFor(string $element, string $input): string
|
||||
{
|
||||
return $this->sanitizeWithContext(
|
||||
W3CReference::CONTEXTS_MAP[StringSanitizer::htmlLower($element)] ?? W3CReference::CONTEXT_BODY,
|
||||
$input
|
||||
);
|
||||
}
|
||||
|
||||
private function sanitizeWithContext(string $context, string $input): string
|
||||
{
|
||||
// Text context: early return with HTML encoding
|
||||
if (W3CReference::CONTEXT_TEXT === $context) {
|
||||
return StringSanitizer::encodeHtmlEntities($input);
|
||||
}
|
||||
|
||||
// Other context: build a DOM visitor
|
||||
$this->domVisitors[$context] ??= $this->createDomVisitorForContext($context);
|
||||
|
||||
// Prevent DOS attack induced by extremely long HTML strings
|
||||
if (-1 !== $this->config->getMaxInputLength() && \strlen($input) > $this->config->getMaxInputLength()) {
|
||||
$input = substr($input, 0, $this->config->getMaxInputLength());
|
||||
}
|
||||
|
||||
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
|
||||
// site scripting issues on Internet Explorer 6. Idea from Drupal (filter_xss).
|
||||
if (!$this->isValidUtf8($input)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Remove NULL character
|
||||
$input = str_replace(\chr(0), '', $input);
|
||||
|
||||
// Parse as HTML
|
||||
if (!$parsed = $this->parser->parse($input)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Visit the DOM tree and render the sanitized nodes
|
||||
return $this->domVisitors[$context]->visit($parsed)?->render() ?? '';
|
||||
}
|
||||
|
||||
private function isValidUtf8(string $html): bool
|
||||
{
|
||||
// preg_match() fails silently on strings containing invalid UTF-8.
|
||||
return '' === $html || preg_match('//u', $html);
|
||||
}
|
||||
|
||||
private function createDomVisitorForContext(string $context): DomVisitor
|
||||
{
|
||||
$elementsConfig = [];
|
||||
|
||||
// Head: only a few elements are allowed
|
||||
if (W3CReference::CONTEXT_HEAD === $context) {
|
||||
foreach ($this->config->getAllowedElements() as $allowedElement => $allowedAttributes) {
|
||||
if (\array_key_exists($allowedElement, W3CReference::HEAD_ELEMENTS)) {
|
||||
$elementsConfig[$allowedElement] = $allowedAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->config->getBlockedElements() as $blockedElement => $v) {
|
||||
if (\array_key_exists($blockedElement, W3CReference::HEAD_ELEMENTS)) {
|
||||
$elementsConfig[$blockedElement] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return new DomVisitor($this->config, $elementsConfig);
|
||||
}
|
||||
|
||||
// Body: allow any configured element that isn't in <head>
|
||||
foreach ($this->config->getAllowedElements() as $allowedElement => $allowedAttributes) {
|
||||
if (!\array_key_exists($allowedElement, W3CReference::HEAD_ELEMENTS)) {
|
||||
$elementsConfig[$allowedElement] = $allowedAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->config->getBlockedElements() as $blockedElement => $v) {
|
||||
if (!\array_key_exists($blockedElement, W3CReference::HEAD_ELEMENTS)) {
|
||||
$elementsConfig[$blockedElement] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return new DomVisitor($this->config, $elementsConfig);
|
||||
}
|
||||
}
|
||||
507
vendor/symfony/html-sanitizer/HtmlSanitizerConfig.php
vendored
Normal file
507
vendor/symfony/html-sanitizer/HtmlSanitizerConfig.php
vendored
Normal file
@@ -0,0 +1,507 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\HtmlSanitizer\Reference\W3CReference;
|
||||
use Symfony\Component\HtmlSanitizer\Visitor\AttributeSanitizer\AttributeSanitizerInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class HtmlSanitizerConfig
|
||||
{
|
||||
/**
|
||||
* Elements that should be removed but their children should be retained.
|
||||
*
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private array $blockedElements = [];
|
||||
|
||||
/**
|
||||
* Elements that should be retained, with their allowed attributes.
|
||||
*
|
||||
* @var array<string, array<string, true>>
|
||||
*/
|
||||
private array $allowedElements = [];
|
||||
|
||||
/**
|
||||
* Attributes that should always be added to certain elements.
|
||||
*
|
||||
* @var array<string, array<string, string>>
|
||||
*/
|
||||
private array $forcedAttributes = [];
|
||||
|
||||
/**
|
||||
* Links schemes that should be retained, other being dropped.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
private array $allowedLinkSchemes = ['http', 'https', 'mailto', 'tel'];
|
||||
|
||||
/**
|
||||
* Links hosts that should be retained (by default, all hosts are allowed).
|
||||
*
|
||||
* @var list<string>|null
|
||||
*/
|
||||
private ?array $allowedLinkHosts = null;
|
||||
|
||||
/**
|
||||
* Should the sanitizer allow relative links (by default, they are dropped).
|
||||
*/
|
||||
private bool $allowRelativeLinks = false;
|
||||
|
||||
/**
|
||||
* Image/Audio/Video schemes that should be retained, other being dropped.
|
||||
*
|
||||
* @var list<string>
|
||||
*/
|
||||
private array $allowedMediaSchemes = ['http', 'https', 'data'];
|
||||
|
||||
/**
|
||||
* Image/Audio/Video hosts that should be retained (by default, all hosts are allowed).
|
||||
*
|
||||
* @var list<string>|null
|
||||
*/
|
||||
private ?array $allowedMediaHosts = null;
|
||||
|
||||
/**
|
||||
* Should the sanitizer allow relative media URL (by default, they are dropped).
|
||||
*/
|
||||
private bool $allowRelativeMedias = false;
|
||||
|
||||
/**
|
||||
* Should the URL in the sanitized document be transformed to HTTPS if they are using HTTP.
|
||||
*/
|
||||
private bool $forceHttpsUrls = false;
|
||||
|
||||
/**
|
||||
* Sanitizers that should be applied to specific attributes in addition to standard sanitization.
|
||||
*
|
||||
* @var list<AttributeSanitizerInterface>
|
||||
*/
|
||||
private array $attributeSanitizers;
|
||||
|
||||
private int $maxInputLength = 20_000;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->attributeSanitizers = [
|
||||
new Visitor\AttributeSanitizer\UrlAttributeSanitizer(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows all static elements and attributes from the W3C Sanitizer API standard.
|
||||
*
|
||||
* All scripts will be removed but the output may still contain other dangerous
|
||||
* behaviors like CSS injection (click-jacking), CSS expressions, ...
|
||||
*/
|
||||
public function allowStaticElements(): static
|
||||
{
|
||||
$elements = array_merge(
|
||||
array_keys(W3CReference::HEAD_ELEMENTS),
|
||||
array_keys(W3CReference::BODY_ELEMENTS)
|
||||
);
|
||||
|
||||
$clone = clone $this;
|
||||
foreach ($elements as $element) {
|
||||
$clone = $clone->allowElement($element, '*');
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows "safe" elements and attributes.
|
||||
*
|
||||
* All scripts will be removed, as well as other dangerous behaviors like CSS injection.
|
||||
*/
|
||||
public function allowSafeElements(): static
|
||||
{
|
||||
$attributes = [];
|
||||
foreach (W3CReference::ATTRIBUTES as $attribute => $isSafe) {
|
||||
if ($isSafe) {
|
||||
$attributes[] = $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
|
||||
foreach (W3CReference::HEAD_ELEMENTS as $element => $isSafe) {
|
||||
if ($isSafe) {
|
||||
$clone = $clone->allowElement($element, $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (W3CReference::BODY_ELEMENTS as $element => $isSafe) {
|
||||
if ($isSafe) {
|
||||
$clone = $clone->allowElement($element, $attributes);
|
||||
}
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows only a given list of schemes to be used in links href attributes.
|
||||
*
|
||||
* All other schemes will be dropped.
|
||||
*
|
||||
* @param list<string> $allowLinkSchemes
|
||||
*/
|
||||
public function allowLinkSchemes(array $allowLinkSchemes): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowedLinkSchemes = $allowLinkSchemes;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows only a given list of hosts to be used in links href attributes.
|
||||
*
|
||||
* All other hosts will be dropped. By default all hosts are allowed
|
||||
* ($allowedLinkHosts = null).
|
||||
*
|
||||
* @param list<string>|null $allowLinkHosts
|
||||
*/
|
||||
public function allowLinkHosts(?array $allowLinkHosts): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowedLinkHosts = $allowLinkHosts;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows relative URLs to be used in links href attributes.
|
||||
*/
|
||||
public function allowRelativeLinks(bool $allowRelativeLinks = true): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowRelativeLinks = $allowRelativeLinks;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows only a given list of schemes to be used in media source attributes (img, audio, video, ...).
|
||||
*
|
||||
* All other schemes will be dropped.
|
||||
*
|
||||
* @param list<string> $allowMediaSchemes
|
||||
*/
|
||||
public function allowMediaSchemes(array $allowMediaSchemes): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowedMediaSchemes = $allowMediaSchemes;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows only a given list of hosts to be used in media source attributes (img, audio, video, ...).
|
||||
*
|
||||
* All other hosts will be dropped. By default all hosts are allowed
|
||||
* ($allowMediaHosts = null).
|
||||
*
|
||||
* @param list<string>|null $allowMediaHosts
|
||||
*/
|
||||
public function allowMediaHosts(?array $allowMediaHosts): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowedMediaHosts = $allowMediaHosts;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows relative URLs to be used in media source attributes (img, audio, video, ...).
|
||||
*/
|
||||
public function allowRelativeMedias(bool $allowRelativeMedias = true): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->allowRelativeMedias = $allowRelativeMedias;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms URLs using the HTTP scheme to use the HTTPS scheme instead.
|
||||
*/
|
||||
public function forceHttpsUrls(bool $forceHttpsUrls = true): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->forceHttpsUrls = $forceHttpsUrls;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given element as allowed.
|
||||
*
|
||||
* Allowed elements are elements the sanitizer should retain from the input.
|
||||
*
|
||||
* A list of allowed attributes for this element can be passed as a second argument.
|
||||
* Passing "*" will allow all standard attributes on this element. By default, no
|
||||
* attributes are allowed on the element.
|
||||
*
|
||||
* @param list<string>|string $allowedAttributes
|
||||
*/
|
||||
public function allowElement(string $element, array|string $allowedAttributes = []): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
// Unblock the element is necessary
|
||||
unset($clone->blockedElements[$element]);
|
||||
|
||||
$clone->allowedElements[$element] = [];
|
||||
|
||||
$attrs = ('*' === $allowedAttributes) ? array_keys(W3CReference::ATTRIBUTES) : (array) $allowedAttributes;
|
||||
foreach ($attrs as $allowedAttr) {
|
||||
$clone->allowedElements[$element][$allowedAttr] = true;
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given element as blocked.
|
||||
*
|
||||
* Blocked elements are elements the sanitizer should remove from the input, but retain
|
||||
* their children.
|
||||
*/
|
||||
public function blockElement(string $element): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
|
||||
// Disallow the element is necessary
|
||||
unset($clone->allowedElements[$element]);
|
||||
|
||||
$clone->blockedElements[$element] = true;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given element as dropped.
|
||||
*
|
||||
* Dropped elements are elements the sanitizer should remove from the input, including
|
||||
* their children.
|
||||
*
|
||||
* Note: when using an empty configuration, all unknown elements are dropped
|
||||
* automatically. This method let you drop elements that were allowed earlier
|
||||
* in the configuration.
|
||||
*/
|
||||
public function dropElement(string $element): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
unset($clone->allowedElements[$element], $clone->blockedElements[$element]);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given attribute as allowed.
|
||||
*
|
||||
* Allowed attributes are attributes the sanitizer should retain from the input.
|
||||
*
|
||||
* A list of allowed elements for this attribute can be passed as a second argument.
|
||||
* Passing "*" will allow all currently allowed elements to use this attribute.
|
||||
*
|
||||
* @param list<string>|string $allowedElements
|
||||
*/
|
||||
public function allowAttribute(string $attribute, array|string $allowedElements): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$allowedElements = ('*' === $allowedElements) ? array_keys($clone->allowedElements) : (array) $allowedElements;
|
||||
|
||||
// For each configured element ...
|
||||
foreach ($clone->allowedElements as $element => $attrs) {
|
||||
if (\in_array($element, $allowedElements, true)) {
|
||||
// ... if the attribute should be allowed, add it
|
||||
$clone->allowedElements[$element][$attribute] = true;
|
||||
} else {
|
||||
// ... if the attribute should not be allowed, remove it
|
||||
unset($clone->allowedElements[$element][$attribute]);
|
||||
}
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given attribute as dropped.
|
||||
*
|
||||
* Dropped attributes are attributes the sanitizer should remove from the input.
|
||||
*
|
||||
* A list of elements on which to drop this attribute can be passed as a second argument.
|
||||
* Passing "*" will drop this attribute from all currently allowed elements.
|
||||
*
|
||||
* Note: when using an empty configuration, all unknown attributes are dropped
|
||||
* automatically. This method let you drop attributes that were allowed earlier
|
||||
* in the configuration.
|
||||
*
|
||||
* @param list<string>|string $droppedElements
|
||||
*/
|
||||
public function dropAttribute(string $attribute, array|string $droppedElements): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$droppedElements = ('*' === $droppedElements) ? array_keys($clone->allowedElements) : (array) $droppedElements;
|
||||
|
||||
foreach ($droppedElements as $element) {
|
||||
if (isset($clone->allowedElements[$element][$attribute])) {
|
||||
unset($clone->allowedElements[$element][$attribute]);
|
||||
}
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully set the value of a given attribute on a given element.
|
||||
*
|
||||
* The attribute will be created on the nodes if it didn't exist.
|
||||
*/
|
||||
public function forceAttribute(string $element, string $attribute, string $value): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->forcedAttributes[$element][$attribute] = $value;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a custom attribute sanitizer.
|
||||
*/
|
||||
public function withAttributeSanitizer(AttributeSanitizerInterface $sanitizer): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->attributeSanitizers[] = $sanitizer;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a custom attribute sanitizer.
|
||||
*/
|
||||
public function withoutAttributeSanitizer(AttributeSanitizerInterface $sanitizer): static
|
||||
{
|
||||
$clone = clone $this;
|
||||
$clone->attributeSanitizers = array_values(array_filter(
|
||||
$this->attributeSanitizers,
|
||||
static fn ($current) => $current !== $sanitizer
|
||||
));
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $maxInputLength The maximum length of the input string in bytes
|
||||
* -1 means no limit
|
||||
*/
|
||||
public function withMaxInputLength(int $maxInputLength): static
|
||||
{
|
||||
if ($maxInputLength < -1) {
|
||||
throw new \InvalidArgumentException(sprintf('The maximum input length must be greater than -1, "%d" given.', $maxInputLength));
|
||||
}
|
||||
|
||||
$clone = clone $this;
|
||||
$clone->maxInputLength = $maxInputLength;
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
public function getMaxInputLength(): int
|
||||
{
|
||||
return $this->maxInputLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, true>>
|
||||
*/
|
||||
public function getAllowedElements(): array
|
||||
{
|
||||
return $this->allowedElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, true>
|
||||
*/
|
||||
public function getBlockedElements(): array
|
||||
{
|
||||
return $this->blockedElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string>>
|
||||
*/
|
||||
public function getForcedAttributes(): array
|
||||
{
|
||||
return $this->forcedAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getAllowedLinkSchemes(): array
|
||||
{
|
||||
return $this->allowedLinkSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getAllowedLinkHosts(): ?array
|
||||
{
|
||||
return $this->allowedLinkHosts;
|
||||
}
|
||||
|
||||
public function getAllowRelativeLinks(): bool
|
||||
{
|
||||
return $this->allowRelativeLinks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getAllowedMediaSchemes(): array
|
||||
{
|
||||
return $this->allowedMediaSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>|null
|
||||
*/
|
||||
public function getAllowedMediaHosts(): ?array
|
||||
{
|
||||
return $this->allowedMediaHosts;
|
||||
}
|
||||
|
||||
public function getAllowRelativeMedias(): bool
|
||||
{
|
||||
return $this->allowRelativeMedias;
|
||||
}
|
||||
|
||||
public function getForceHttpsUrls(): bool
|
||||
{
|
||||
return $this->forceHttpsUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<AttributeSanitizerInterface>
|
||||
*/
|
||||
public function getAttributeSanitizers(): array
|
||||
{
|
||||
return $this->attributeSanitizers;
|
||||
}
|
||||
}
|
||||
42
vendor/symfony/html-sanitizer/HtmlSanitizerInterface.php
vendored
Normal file
42
vendor/symfony/html-sanitizer/HtmlSanitizerInterface.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Sanitizes an untrusted HTML input for safe insertion into a document's DOM.
|
||||
*
|
||||
* This interface is inspired by the W3C Standard Draft about a HTML Sanitizer API
|
||||
* ({@see https://wicg.github.io/sanitizer-api/}).
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
interface HtmlSanitizerInterface
|
||||
{
|
||||
/**
|
||||
* Sanitizes an untrusted HTML input for a <body> context.
|
||||
*
|
||||
* This method is NOT context sensitive: it assumes the returned HTML string
|
||||
* will be injected in a "body" context, and therefore will drop tags only
|
||||
* allowed in the "head" element. To sanitize a string for injection
|
||||
* in the "head" element, use {@see HtmlSanitizerInterface::sanitizeFor()}.
|
||||
*/
|
||||
public function sanitize(string $input): string;
|
||||
|
||||
/**
|
||||
* Sanitizes an untrusted HTML input for a given context.
|
||||
*
|
||||
* This method is context sensitive: by providing a parent element name
|
||||
* (body, head, title, ...), the sanitizer will adapt its rules to only
|
||||
* allow elements that are valid inside the given parent element.
|
||||
*/
|
||||
public function sanitizeFor(string $element, string $input): string;
|
||||
}
|
||||
19
vendor/symfony/html-sanitizer/LICENSE
vendored
Normal file
19
vendor/symfony/html-sanitizer/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2021-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
29
vendor/symfony/html-sanitizer/Parser/MastermindsParser.php
vendored
Normal file
29
vendor/symfony/html-sanitizer/Parser/MastermindsParser.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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\Parser;
|
||||
|
||||
use Masterminds\HTML5;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
final class MastermindsParser implements ParserInterface
|
||||
{
|
||||
public function __construct(private array $defaultOptions = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function parse(string $html): ?\DOMNode
|
||||
{
|
||||
return (new HTML5($this->defaultOptions))->loadHTMLFragment($html);
|
||||
}
|
||||
}
|
||||
27
vendor/symfony/html-sanitizer/Parser/ParserInterface.php
vendored
Normal file
27
vendor/symfony/html-sanitizer/Parser/ParserInterface.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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\Parser;
|
||||
|
||||
/**
|
||||
* Transforms an untrusted HTML input string into a DOM tree.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
interface ParserInterface
|
||||
{
|
||||
/**
|
||||
* Parse a given string and returns a DOMNode tree.
|
||||
*
|
||||
* This method must return null if the string cannot be parsed as HTML.
|
||||
*/
|
||||
public function parse(string $html): ?\DOMNode;
|
||||
}
|
||||
115
vendor/symfony/html-sanitizer/README.md
vendored
Normal file
115
vendor/symfony/html-sanitizer/README.md
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
HtmlSanitizer Component
|
||||
=======================
|
||||
|
||||
The HtmlSanitizer component provides an object-oriented API to sanitize
|
||||
untrusted HTML input for safe insertion into a document's DOM.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```php
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizerConfig;
|
||||
use Symfony\Component\HtmlSanitizer\HtmlSanitizer;
|
||||
|
||||
// By default, an element not added to the allowed or blocked elements
|
||||
// will be dropped, including its children
|
||||
$config = (new HtmlSanitizerConfig())
|
||||
// Allow "safe" elements and attributes. All scripts will be removed
|
||||
// as well as other dangerous behaviors like CSS injection
|
||||
->allowSafeElements()
|
||||
|
||||
// Allow all static elements and attributes from the W3C Sanitizer API
|
||||
// standard. All scripts will be removed but the output may still contain
|
||||
// other dangerous behaviors like CSS injection (click-jacking), CSS
|
||||
// expressions, ...
|
||||
->allowStaticElements()
|
||||
|
||||
// Allow the "div" element and no attribute can be on it
|
||||
->allowElement('div')
|
||||
|
||||
// Allow the "a" element, and the "title" attribute to be on it
|
||||
->allowElement('a', ['title'])
|
||||
|
||||
// Allow the "span" element, and any attribute from the Sanitizer API is allowed
|
||||
// (see https://wicg.github.io/sanitizer-api/#default-configuration)
|
||||
->allowElement('span', '*')
|
||||
|
||||
// Block the "section" element: this element will be removed but
|
||||
// its children will be retained
|
||||
->blockElement('section')
|
||||
|
||||
// Drop the "div" element: this element will be removed, including its children
|
||||
->dropElement('div')
|
||||
|
||||
// Allow the attribute "title" on the "div" element
|
||||
->allowAttribute('title', ['div'])
|
||||
|
||||
// Allow the attribute "data-custom-attr" on all currently allowed elements
|
||||
->allowAttribute('data-custom-attr', '*')
|
||||
|
||||
// Drop the "data-custom-attr" attribute from the "div" element:
|
||||
// this attribute will be removed
|
||||
->dropAttribute('data-custom-attr', ['div'])
|
||||
|
||||
// Drop the "data-custom-attr" attribute from all elements:
|
||||
// this attribute will be removed
|
||||
->dropAttribute('data-custom-attr', '*')
|
||||
|
||||
// Forcefully set the value of all "rel" attributes on "a"
|
||||
// elements to "noopener noreferrer"
|
||||
->forceAttribute('a', 'rel', 'noopener noreferrer')
|
||||
|
||||
// Transform all HTTP schemes to HTTPS
|
||||
->forceHttpsUrls()
|
||||
|
||||
// Configure which schemes are allowed in links (others will be dropped)
|
||||
->allowLinkSchemes(['https', 'http', 'mailto'])
|
||||
|
||||
// Configure which hosts are allowed in links (by default all are allowed)
|
||||
->allowLinkHosts(['symfony.com', 'example.com'])
|
||||
|
||||
// Allow relative URL in links (by default they are dropped)
|
||||
->allowRelativeLinks()
|
||||
|
||||
// Configure which schemes are allowed in img/audio/video/iframe (others will be dropped)
|
||||
->allowMediaSchemes(['https', 'http'])
|
||||
|
||||
// Configure which hosts are allowed in img/audio/video/iframe (by default all are allowed)
|
||||
->allowMediaHosts(['symfony.com', 'example.com'])
|
||||
|
||||
// Allow relative URL in img/audio/video/iframe (by default they are dropped)
|
||||
->allowRelativeMedias()
|
||||
|
||||
// Configure a custom attribute sanitizer to apply custom sanitization logic
|
||||
// ($attributeSanitizer instance of AttributeSanitizerInterface)
|
||||
->withAttributeSanitizer($attributeSanitizer)
|
||||
|
||||
// Unregister a previously registered attribute sanitizer
|
||||
// ($attributeSanitizer instance of AttributeSanitizerInterface)
|
||||
->withoutAttributeSanitizer($attributeSanitizer)
|
||||
;
|
||||
|
||||
$sanitizer = new HtmlSanitizer($config);
|
||||
|
||||
// Sanitize a given string, using the configuration provided and in the
|
||||
// "body" context (tags only allowed in <head> will be removed)
|
||||
$sanitizer->sanitize($userInput);
|
||||
|
||||
// Sanitize the given string for a usage in a <head> tag
|
||||
$sanitizer->sanitizeFor('head', $userInput);
|
||||
|
||||
// Sanitize the given string for a usage in another tag
|
||||
$sanitizer->sanitizeFor('title', $userInput); // Will encode as HTML entities
|
||||
$sanitizer->sanitizeFor('textarea', $userInput); // Will encode as HTML entities
|
||||
$sanitizer->sanitizeFor('div', $userInput); // Will sanitize as body
|
||||
$sanitizer->sanitizeFor('section', $userInput); // Will sanitize as body
|
||||
// ...
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
400
vendor/symfony/html-sanitizer/Reference/W3CReference.php
vendored
Normal file
400
vendor/symfony/html-sanitizer/Reference/W3CReference.php
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
<?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\Reference;
|
||||
|
||||
/**
|
||||
* Stores reference data from the W3C Sanitizer API standard.
|
||||
*
|
||||
* @see https://wicg.github.io/sanitizer-api/#default-configuration
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class W3CReference
|
||||
{
|
||||
/**
|
||||
* Sanitizer supported contexts.
|
||||
*
|
||||
* A parent element name can be passed as an argument to {@see HtmlSanitizer::sanitizeFor()}.
|
||||
* When doing so, depending on the given context, different elements will be allowed.
|
||||
*/
|
||||
public const CONTEXT_HEAD = 'head';
|
||||
public const CONTEXT_BODY = 'body';
|
||||
public const CONTEXT_TEXT = 'text';
|
||||
|
||||
// Which context to apply depending on the passed parent element name
|
||||
public const CONTEXTS_MAP = [
|
||||
'head' => self::CONTEXT_HEAD,
|
||||
'textarea' => self::CONTEXT_TEXT,
|
||||
'title' => self::CONTEXT_TEXT,
|
||||
];
|
||||
|
||||
/**
|
||||
* Elements allowed by the Sanitizer standard in <head> as keys, including whether
|
||||
* they are safe or not as values (safe meaning no global display/audio/video impact).
|
||||
*/
|
||||
public const HEAD_ELEMENTS = [
|
||||
'head' => true,
|
||||
'link' => true,
|
||||
'meta' => true,
|
||||
'style' => false,
|
||||
'title' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Elements allowed by the Sanitizer standard in <body> as keys, including whether
|
||||
* they are safe or not as values (safe meaning no global display/audio/video impact).
|
||||
*/
|
||||
public const BODY_ELEMENTS = [
|
||||
'a' => true,
|
||||
'abbr' => true,
|
||||
'acronym' => true,
|
||||
'address' => true,
|
||||
'area' => true,
|
||||
'article' => true,
|
||||
'aside' => true,
|
||||
'audio' => true,
|
||||
'b' => true,
|
||||
'basefont' => true,
|
||||
'bdi' => true,
|
||||
'bdo' => true,
|
||||
'bgsound' => false,
|
||||
'big' => true,
|
||||
'blockquote' => true,
|
||||
'body' => true,
|
||||
'br' => true,
|
||||
'button' => true,
|
||||
'canvas' => true,
|
||||
'caption' => true,
|
||||
'center' => true,
|
||||
'cite' => true,
|
||||
'code' => true,
|
||||
'col' => true,
|
||||
'colgroup' => true,
|
||||
'command' => true,
|
||||
'data' => true,
|
||||
'datalist' => true,
|
||||
'dd' => true,
|
||||
'del' => true,
|
||||
'details' => true,
|
||||
'dfn' => true,
|
||||
'dialog' => true,
|
||||
'dir' => true,
|
||||
'div' => true,
|
||||
'dl' => true,
|
||||
'dt' => true,
|
||||
'em' => true,
|
||||
'fieldset' => true,
|
||||
'figcaption' => true,
|
||||
'figure' => true,
|
||||
'font' => true,
|
||||
'footer' => true,
|
||||
'form' => false,
|
||||
'h1' => true,
|
||||
'h2' => true,
|
||||
'h3' => true,
|
||||
'h4' => true,
|
||||
'h5' => true,
|
||||
'h6' => true,
|
||||
'header' => true,
|
||||
'hgroup' => true,
|
||||
'hr' => true,
|
||||
'html' => true,
|
||||
'i' => true,
|
||||
'image' => true,
|
||||
'img' => true,
|
||||
'input' => false,
|
||||
'ins' => true,
|
||||
'kbd' => true,
|
||||
'keygen' => true,
|
||||
'label' => true,
|
||||
'layer' => true,
|
||||
'legend' => true,
|
||||
'li' => true,
|
||||
'listing' => true,
|
||||
'main' => true,
|
||||
'map' => true,
|
||||
'mark' => true,
|
||||
'marquee' => true,
|
||||
'menu' => true,
|
||||
'meter' => true,
|
||||
'nav' => true,
|
||||
'nobr' => true,
|
||||
'ol' => true,
|
||||
'optgroup' => true,
|
||||
'option' => true,
|
||||
'output' => true,
|
||||
'p' => true,
|
||||
'picture' => true,
|
||||
'plaintext' => true,
|
||||
'popup' => true,
|
||||
'portal' => true,
|
||||
'pre' => true,
|
||||
'progress' => true,
|
||||
'q' => true,
|
||||
'rb' => true,
|
||||
'rp' => true,
|
||||
'rt' => true,
|
||||
'rtc' => true,
|
||||
'ruby' => true,
|
||||
's' => true,
|
||||
'samp' => true,
|
||||
'section' => true,
|
||||
'select' => false,
|
||||
'selectmenu' => false,
|
||||
'slot' => true,
|
||||
'small' => true,
|
||||
'source' => true,
|
||||
'span' => true,
|
||||
'strike' => true,
|
||||
'strong' => true,
|
||||
'sub' => true,
|
||||
'summary' => true,
|
||||
'sup' => true,
|
||||
'table' => true,
|
||||
'tbody' => true,
|
||||
'td' => true,
|
||||
'template' => true,
|
||||
'textarea' => false,
|
||||
'tfoot' => true,
|
||||
'th' => true,
|
||||
'thead' => true,
|
||||
'time' => true,
|
||||
'tr' => true,
|
||||
'track' => true,
|
||||
'tt' => true,
|
||||
'u' => true,
|
||||
'ul' => true,
|
||||
'var' => true,
|
||||
'video' => true,
|
||||
'wbr' => true,
|
||||
'xmp' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Attributes allowed by the standard.
|
||||
*/
|
||||
public const ATTRIBUTES = [
|
||||
'abbr' => true,
|
||||
'accept' => true,
|
||||
'accept-charset' => true,
|
||||
'accesskey' => true,
|
||||
'action' => true,
|
||||
'align' => true,
|
||||
'alink' => true,
|
||||
'allow' => true,
|
||||
'allowfullscreen' => true,
|
||||
'allowpaymentrequest' => false,
|
||||
'alt' => true,
|
||||
'anchor' => true,
|
||||
'archive' => true,
|
||||
'as' => true,
|
||||
'async' => false,
|
||||
'autocapitalize' => false,
|
||||
'autocomplete' => false,
|
||||
'autocorrect' => false,
|
||||
'autofocus' => false,
|
||||
'autopictureinpicture' => false,
|
||||
'autoplay' => false,
|
||||
'axis' => true,
|
||||
'background' => false,
|
||||
'behavior' => true,
|
||||
'bgcolor' => false,
|
||||
'border' => false,
|
||||
'bordercolor' => false,
|
||||
'capture' => true,
|
||||
'cellpadding' => true,
|
||||
'cellspacing' => true,
|
||||
'challenge' => true,
|
||||
'char' => true,
|
||||
'charoff' => true,
|
||||
'charset' => true,
|
||||
'checked' => false,
|
||||
'cite' => true,
|
||||
'class' => false,
|
||||
'classid' => false,
|
||||
'clear' => true,
|
||||
'code' => true,
|
||||
'codebase' => true,
|
||||
'codetype' => true,
|
||||
'color' => false,
|
||||
'cols' => true,
|
||||
'colspan' => true,
|
||||
'compact' => true,
|
||||
'content' => true,
|
||||
'contenteditable' => false,
|
||||
'controls' => true,
|
||||
'controlslist' => true,
|
||||
'conversiondestination' => true,
|
||||
'coords' => true,
|
||||
'crossorigin' => true,
|
||||
'csp' => true,
|
||||
'data' => true,
|
||||
'datetime' => true,
|
||||
'declare' => true,
|
||||
'decoding' => true,
|
||||
'default' => true,
|
||||
'defer' => true,
|
||||
'dir' => true,
|
||||
'direction' => true,
|
||||
'dirname' => true,
|
||||
'disabled' => true,
|
||||
'disablepictureinpicture' => true,
|
||||
'disableremoteplayback' => true,
|
||||
'disallowdocumentaccess' => true,
|
||||
'download' => true,
|
||||
'draggable' => true,
|
||||
'elementtiming' => true,
|
||||
'enctype' => true,
|
||||
'end' => true,
|
||||
'enterkeyhint' => true,
|
||||
'event' => true,
|
||||
'exportparts' => true,
|
||||
'face' => true,
|
||||
'for' => true,
|
||||
'form' => false,
|
||||
'formaction' => false,
|
||||
'formenctype' => false,
|
||||
'formmethod' => false,
|
||||
'formnovalidate' => false,
|
||||
'formtarget' => false,
|
||||
'frame' => false,
|
||||
'frameborder' => false,
|
||||
'headers' => true,
|
||||
'height' => true,
|
||||
'hidden' => false,
|
||||
'high' => true,
|
||||
'href' => true,
|
||||
'hreflang' => true,
|
||||
'hreftranslate' => true,
|
||||
'hspace' => true,
|
||||
'http-equiv' => false,
|
||||
'id' => true,
|
||||
'imagesizes' => true,
|
||||
'imagesrcset' => true,
|
||||
'importance' => true,
|
||||
'impressiondata' => true,
|
||||
'impressionexpiry' => true,
|
||||
'incremental' => true,
|
||||
'inert' => true,
|
||||
'inputmode' => true,
|
||||
'integrity' => true,
|
||||
'invisible' => true,
|
||||
'is' => true,
|
||||
'ismap' => true,
|
||||
'keytype' => true,
|
||||
'kind' => true,
|
||||
'label' => true,
|
||||
'lang' => true,
|
||||
'language' => true,
|
||||
'latencyhint' => true,
|
||||
'leftmargin' => true,
|
||||
'link' => true,
|
||||
'list' => true,
|
||||
'loading' => true,
|
||||
'longdesc' => true,
|
||||
'loop' => true,
|
||||
'low' => true,
|
||||
'lowsrc' => true,
|
||||
'manifest' => true,
|
||||
'marginheight' => true,
|
||||
'marginwidth' => true,
|
||||
'max' => true,
|
||||
'maxlength' => true,
|
||||
'mayscript' => true,
|
||||
'media' => true,
|
||||
'method' => true,
|
||||
'min' => true,
|
||||
'minlength' => true,
|
||||
'multiple' => true,
|
||||
'muted' => true,
|
||||
'name' => true,
|
||||
'nohref' => true,
|
||||
'nomodule' => true,
|
||||
'nonce' => true,
|
||||
'noresize' => true,
|
||||
'noshade' => true,
|
||||
'novalidate' => true,
|
||||
'nowrap' => true,
|
||||
'object' => true,
|
||||
'open' => true,
|
||||
'optimum' => true,
|
||||
'part' => true,
|
||||
'pattern' => true,
|
||||
'ping' => false,
|
||||
'placeholder' => true,
|
||||
'playsinline' => true,
|
||||
'policy' => true,
|
||||
'poster' => true,
|
||||
'preload' => true,
|
||||
'pseudo' => true,
|
||||
'readonly' => true,
|
||||
'referrerpolicy' => true,
|
||||
'rel' => true,
|
||||
'reportingorigin' => true,
|
||||
'required' => true,
|
||||
'resources' => true,
|
||||
'rev' => true,
|
||||
'reversed' => true,
|
||||
'role' => true,
|
||||
'rows' => true,
|
||||
'rowspan' => true,
|
||||
'rules' => true,
|
||||
'sandbox' => true,
|
||||
'scheme' => true,
|
||||
'scope' => true,
|
||||
'scopes' => true,
|
||||
'scrollamount' => true,
|
||||
'scrolldelay' => true,
|
||||
'scrolling' => true,
|
||||
'select' => false,
|
||||
'selected' => false,
|
||||
'shadowroot' => true,
|
||||
'shadowrootdelegatesfocus' => true,
|
||||
'shape' => true,
|
||||
'size' => true,
|
||||
'sizes' => true,
|
||||
'slot' => true,
|
||||
'span' => true,
|
||||
'spellcheck' => true,
|
||||
'src' => true,
|
||||
'srcdoc' => true,
|
||||
'srclang' => true,
|
||||
'srcset' => true,
|
||||
'standby' => true,
|
||||
'start' => true,
|
||||
'step' => true,
|
||||
'style' => false,
|
||||
'summary' => true,
|
||||
'tabindex' => true,
|
||||
'target' => true,
|
||||
'text' => true,
|
||||
'title' => true,
|
||||
'topmargin' => true,
|
||||
'translate' => true,
|
||||
'truespeed' => true,
|
||||
'trusttoken' => true,
|
||||
'type' => true,
|
||||
'usemap' => true,
|
||||
'valign' => true,
|
||||
'value' => false,
|
||||
'valuetype' => true,
|
||||
'version' => true,
|
||||
'virtualkeyboardpolicy' => true,
|
||||
'vlink' => false,
|
||||
'vspace' => true,
|
||||
'webkitdirectory' => true,
|
||||
'width' => true,
|
||||
'wrap' => true,
|
||||
];
|
||||
}
|
||||
82
vendor/symfony/html-sanitizer/TextSanitizer/StringSanitizer.php
vendored
Normal file
82
vendor/symfony/html-sanitizer/TextSanitizer/StringSanitizer.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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\TextSanitizer;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class StringSanitizer
|
||||
{
|
||||
private const LOWERCASE = [
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'abcdefghijklmnopqrstuvwxyz',
|
||||
];
|
||||
|
||||
private const REPLACEMENTS = [
|
||||
[
|
||||
// """ is shorter than """
|
||||
'"',
|
||||
|
||||
// Fix several potential issues in how browsers interpret attributes values
|
||||
'+',
|
||||
'=',
|
||||
'@',
|
||||
'`',
|
||||
|
||||
// Some DB engines will transform UTF8 full-width characters their classical version
|
||||
// if the data is saved in a non-UTF8 field
|
||||
'<',
|
||||
'>',
|
||||
'+',
|
||||
'=',
|
||||
'@',
|
||||
'`',
|
||||
],
|
||||
[
|
||||
'"',
|
||||
|
||||
'+',
|
||||
'=',
|
||||
'@',
|
||||
'`',
|
||||
|
||||
'<',
|
||||
'>',
|
||||
'+',
|
||||
'=',
|
||||
'@',
|
||||
'`',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Applies a transformation to lowercase following W3C HTML Standard.
|
||||
*
|
||||
* @see https://w3c.github.io/html-reference/terminology.html#case-insensitive
|
||||
*/
|
||||
public static function htmlLower(string $string): string
|
||||
{
|
||||
return strtr($string, self::LOWERCASE[0], self::LOWERCASE[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the HTML entities in the given string for safe injection in a document's DOM.
|
||||
*/
|
||||
public static function encodeHtmlEntities(string $string): string
|
||||
{
|
||||
return str_replace(
|
||||
self::REPLACEMENTS[0],
|
||||
self::REPLACEMENTS[1],
|
||||
htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8')
|
||||
);
|
||||
}
|
||||
}
|
||||
136
vendor/symfony/html-sanitizer/TextSanitizer/UrlSanitizer.php
vendored
Normal file
136
vendor/symfony/html-sanitizer/TextSanitizer/UrlSanitizer.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?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\TextSanitizer;
|
||||
|
||||
use League\Uri\Exceptions\SyntaxError;
|
||||
use League\Uri\UriString;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class UrlSanitizer
|
||||
{
|
||||
/**
|
||||
* Sanitizes a given URL string.
|
||||
*
|
||||
* In addition to ensuring $input is a valid URL, this sanitizer checks that:
|
||||
* * the URL's host is allowed ;
|
||||
* * the URL's scheme is allowed ;
|
||||
* * the URL is allowed to be relative if it is ;
|
||||
*
|
||||
* It also transforms the URL to HTTPS if requested.
|
||||
*/
|
||||
public static function sanitize(?string $input, ?array $allowedSchemes = null, bool $forceHttps = false, ?array $allowedHosts = null, bool $allowRelative = false): ?string
|
||||
{
|
||||
if (!$input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$url = self::parse($input);
|
||||
|
||||
// Malformed URL
|
||||
if (!$url || !\is_array($url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No scheme and relative not allowed
|
||||
if (!$allowRelative && !$url['scheme']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Forbidden scheme
|
||||
if ($url['scheme'] && null !== $allowedSchemes && !\in_array($url['scheme'], $allowedSchemes, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the scheme used is not supposed to have a host, do not check the host
|
||||
if (!self::isHostlessScheme($url['scheme'])) {
|
||||
// No host and relative not allowed
|
||||
if (!$allowRelative && !$url['host']) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Forbidden host
|
||||
if ($url['host'] && null !== $allowedHosts && !self::isAllowedHost($url['host'], $allowedHosts)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Force HTTPS
|
||||
if ($forceHttps && 'http' === $url['scheme']) {
|
||||
$url['scheme'] = 'https';
|
||||
}
|
||||
|
||||
return UriString::build($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a given URL and returns an array of its components.
|
||||
*
|
||||
* @return null|array{
|
||||
* scheme:?string,
|
||||
* user:?string,
|
||||
* pass:?string,
|
||||
* host:?string,
|
||||
* port:?int,
|
||||
* path:string,
|
||||
* query:?string,
|
||||
* fragment:?string
|
||||
* }
|
||||
*/
|
||||
public static function parse(string $url): ?array
|
||||
{
|
||||
if (!$url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return UriString::parse($url);
|
||||
} catch (SyntaxError) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function isHostlessScheme(?string $scheme): bool
|
||||
{
|
||||
return \in_array($scheme, ['blob', 'chrome', 'data', 'file', 'geo', 'mailto', 'maps', 'tel', 'view-source'], true);
|
||||
}
|
||||
|
||||
private static function isAllowedHost(?string $host, array $allowedHosts): bool
|
||||
{
|
||||
if (null === $host) {
|
||||
return \in_array(null, $allowedHosts, true);
|
||||
}
|
||||
|
||||
$parts = array_reverse(explode('.', $host));
|
||||
|
||||
foreach ($allowedHosts as $allowedHost) {
|
||||
if (self::matchAllowedHostParts($parts, array_reverse(explode('.', $allowedHost)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function matchAllowedHostParts(array $uriParts, array $trustedParts): bool
|
||||
{
|
||||
// Check each chunk of the domain is valid
|
||||
foreach ($trustedParts as $key => $trustedPart) {
|
||||
if ($uriParts[$key] !== $trustedPart) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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'];
|
||||
}
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
177
vendor/symfony/html-sanitizer/Visitor/DomVisitor.php
vendored
Normal file
177
vendor/symfony/html-sanitizer/Visitor/DomVisitor.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
46
vendor/symfony/html-sanitizer/Visitor/Node/BlockedNode.php
vendored
Normal file
46
vendor/symfony/html-sanitizer/Visitor/Node/BlockedNode.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
123
vendor/symfony/html-sanitizer/Visitor/Node/Node.php
vendored
Normal file
123
vendor/symfony/html-sanitizer/Visitor/Node/Node.php
vendored
Normal 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) : '';
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
39
vendor/symfony/html-sanitizer/Visitor/Node/TextNode.php
vendored
Normal file
39
vendor/symfony/html-sanitizer/Visitor/Node/TextNode.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/html-sanitizer/composer.json
vendored
Normal file
31
vendor/symfony/html-sanitizer/composer.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "symfony/html-sanitizer",
|
||||
"type": "library",
|
||||
"description": "Provides an object-oriented API to sanitize untrusted HTML input for safe insertion into a document's DOM.",
|
||||
"keywords": ["html", "sanitizer", "purifier"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Titouan Galopin",
|
||||
"email": "galopintitouan@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"ext-dom": "*",
|
||||
"league/uri": "^6.5|^7.0",
|
||||
"masterminds/html5": "^2.7.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\HtmlSanitizer\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
Reference in New Issue
Block a user