Skip to content
Snippets Groups Projects
Commit 4882b691 authored by Oliver Hader's avatar Oliver Hader Committed by Benni Mack
Browse files

[FEATURE] Introduce explicit f:sanitize.html view-helper

New `<f:sanitize.html build="default">` view-helper is introduced
which directly invokes processing of `typo3/html-sanitize` package. An
optional view-helper argument `build` allows using a defined preset,
or a fully qualified class name of a builder instance as alternative,
which has to implement `\TYPO3\HtmlSanitizer\Builder\BuilderInterface`.

In contrast to `<f:format.html>`, this does NOT invoke `lib.parseFunc`,
and does NOT rely on TypoScript configuration being loaded and parsed.

Resolves: #94825
Releases: master, 11.3, 10.4, 9.5
Change-Id: Id0720120fea7d5d517a8c61d10bdbb6b03658adf
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70457


Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarWouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: default avatarBenni Mack <benni@typo3.org>
parent 17443f7a
Branches
Tags
No related merge requests found
.. include:: ../../Includes.txt
======================================================
Feature: #94825 - New f:sanitize.html Fluid ViewHelper
======================================================
See :issue:`94825`
Description
===========
A new Fluid ViewHelper :html:`<f:sanitize.html>` is available
for use in any Fluid Template.
Unlike :html:`<f:format.html>` the new ViewHelper does not fully rewrite
the contents of the ViewHelper, but only cleans the HTML for
incorrect / possibly bad code.
The htmlSanitize keeps all HTML code as is, but cleans up invalid
and malicious code based on the third-party package `typo3/html-sanitize`.
An optional view-helper argument `build` allows using a defined preset, or a
fully qualified class name of a builder instance as alternative, which has
to implement :php:`\TYPO3\HtmlSanitizer\Builder\BuilderInterface`.
If not given, the configuration falls back to the best-practice
sanitization preset for TYPO3's base RTE configuration (called "default").
Impact
======
The "default" preset of :html:`<f:sanitize.html>` allows
to explicitly sanitize user-submitted markup - for instance provided in
rich-text input fields using the TYPO3 backend user interface. The
default preset only supports common HTML tags and attributes that usually are
expected to be safe - for instance :html:`<iframe>`, :html:`<form>`, :html:`<nav>` or
similar elements are not supported (currently) and not supposed to be defined
by users or editors, but rather by the actual HTML Template which shouldn't
be fully wrapped in :html:`<f:sanitize.html>`.
When to use the different ViewHelpers:
* :html:`<f:format.html>`
Use this for wrapping fields produced by RTE fields, which parses
HTML and adds attributes, replaces TYPO3-internal links to pages or files,
based on :ts:`lib.parseFunc_RTE`. For this reason, it is recommended to use
this ViewHelper mainly in Frontend rendering.
This ViewHelper calls TYPO3's "parseFunc", which means that `htmlSanitize` is
activated by default in TYPO3 installations.
Summarized: :html:`<f:format.html>` does HTML sanitization plus rebuilding
the HTML output based on the configuration from `lib.parseFunc`.
* :html:`<f:sanitize.html>`
This ViewHelper takes the HTML as is, and removes malicious HTML code. This is
useful for HTML returned from external sources where the HTML-based content is
untrusted. It can be used in Frontend and Backend environments.
* :html:`<f:format.raw>`
This ViewHelper just outputs the content as is, including all HTML. Use this
ViewHelper only if the content can be fully trusted.
All of the ViewHelpers above do not escape any of the contents.
.. index:: Fluid, ext:fluid
......@@ -22,7 +22,12 @@ use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class DefaultSanitizerBuilderTest extends FunctionalTestCase
{
public function isSanitizedDataProvider(): array
/**
* @var bool Speed up this test case, it needs no database
*/
protected $initializeDatabase = false;
public static function isSanitizedDataProvider(): array
{
return [
'#010' => [
......
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\ViewHelpers\Sanitize;
use TYPO3\CMS\Core\Html\SanitizerBuilderFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\HtmlSanitizer\Builder\BuilderInterface;
use TYPO3\HtmlSanitizer\Sanitizer;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Passes a given content through `typo3/html-sanitizer` to mitigate potential
* cross-site scripting occurrences. Given `default` build corresponds to class
* `TYPO3\CMS\Core\Html\DefaultSanitizerBuilder` declaring allowed HTML tags,
* attributes and their values.
*
* Examples
* ========
*
* Default parameters
* ------------------
*
* ::
*
* <f:sanitize.html>
* <img src="/img.png" class="image" onmouseover="alert(document.location)">
* </f:sanitize.html>
*
* Output::
*
* <img src="/img.png" class="image">
*
* Inline notation
* ---------------
*
* ::
*
* {richTextFieldContent -> f:sanitize.html(build: 'default')}
*/
class HtmlViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeChildren = false;
/**
* @var bool
*/
protected $escapeOutput = false;
/**
* @throws Exception
*/
public function initializeArguments()
{
$this->registerArgument(
'build',
'string',
'preset name or class-like name of sanitization builder',
false,
'default'
);
}
/**
* @param array{build: string|class-string} $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string the parsed string.
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$value = $renderChildrenClosure();
$build = $arguments['build'] ?? 'default';
return static::createSanitizer($build)->sanitize($value);
}
protected static function createSanitizer(string $build): Sanitizer
{
if (class_exists($build) && is_a($build, BuilderInterface::class, true)) {
$builder = GeneralUtility::makeInstance($build);
} else {
$factory = GeneralUtility::makeInstance(SanitizerBuilderFactory::class);
$builder = $factory->build($build);
}
return $builder->build();
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Sanitize;
use TYPO3\CMS\Core\Tests\Functional\Html\DefaultSanitizerBuilderTest;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
class HtmlViewHelperTest extends FunctionalTestCase
{
/**
* @var bool Speed up this test case, it needs no database
*/
protected $initializeDatabase = false;
public static function isSanitizedDataProvider(): array
{
// @todo splitter for functional tests cannot deal with external classes
return DefaultSanitizerBuilderTest::isSanitizedDataProvider();
}
/**
* @param string $payload
* @param string $expectation
* @test
* @dataProvider isSanitizedDataProvider
*/
public function isSanitizedUsingNodeInstruction(string $payload, string $expectation): void
{
$view = new StandaloneView();
$view->setTemplateSource(sprintf('<f:sanitize.html>%s</f:sanitize.html>', $payload));
self::assertSame($expectation, $view->render());
}
/**
* @param string $payload
* @param string $expectation
* @test
* @dataProvider isSanitizedDataProvider
*/
public function isSanitizedUsingInlineInstruction(string $payload, string $expectation): void
{
$view = new StandaloneView();
$view->assign('payload', $payload);
$view->setTemplateSource('{payload -> f:sanitize.html()}');
self::assertSame($expectation, $view->render());
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment