From 2cd74e91c35122ad1eae325520e573c71b11a032 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Thu, 14 Dec 2023 19:08:03 +0100 Subject: [PATCH] Sanitize clarification bodies. --- composer.json | 1 + composer.lock | 245 +++++++++++++++++- .../Jury/ClarificationController.php | 5 +- webapp/src/Controller/RootController.php | 6 +- webapp/src/Entity/Clarification.php | 2 +- .../src/Form/Type/TeamClarificationType.php | 1 + 6 files changed, 254 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index fd01069b0d..06e1b4265e 100644 --- a/composer.json +++ b/composer.json @@ -84,6 +84,7 @@ "symfony/flex": "^2", "symfony/form": "6.3.*", "symfony/framework-bundle": "6.3.*", + "symfony/html-sanitizer": "6.3.*", "symfony/http-client": "6.3.*", "symfony/intl": "6.3.*", "symfony/mime": "6.3.*", diff --git a/composer.lock b/composer.lock index 1e7b560370..ca3eaf1daf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0cf864426dbc05c49c2c7671381b85a1", + "content-hash": "1e423aa6cf857ecb908672726f11d0b4", "packages": [ { "name": "apalfrey/select2-bootstrap-5-theme", @@ -2897,6 +2897,180 @@ ], "time": "2022-12-11T20:36:23+00:00" }, + { + "name": "league/uri", + "version": "7.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bf414ba956d902f5d98bf9385fcf63954f09dce5", + "reference": "bf414ba956d902f5d98bf9385fcf63954f09dce5", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.3", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.4.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2023-12-01T06:24:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "reference": "bd8c487ec236930f7bbc42b8d374fa882fbba0f3", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2023-11-24T15:40:42+00:00" + }, { "name": "masterminds/html5", "version": "2.8.1", @@ -6769,6 +6943,75 @@ ], "time": "2023-11-09T14:35:42+00:00" }, + { + "name": "symfony/html-sanitizer", + "version": "v6.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/html-sanitizer.git", + "reference": "45e5a24b63d394fa6472c595df448aecfd1e1ea5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/45e5a24b63d394fa6472c595df448aecfd1e1ea5", + "reference": "45e5a24b63d394fa6472c595df448aecfd1e1ea5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "league/uri": "^6.5|^7.0", + "masterminds/html5": "^2.7.2", + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HtmlSanitizer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Titouan Galopin", + "email": "galopintitouan@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to sanitize untrusted HTML input for safe insertion into a document's DOM.", + "homepage": "https://symfony.com", + "keywords": [ + "Purifier", + "html", + "sanitizer" + ], + "support": { + "source": "https://github.com/symfony/html-sanitizer/tree/v6.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-27T13:27:27+00:00" + }, { "name": "symfony/http-client", "version": "v6.3.8", diff --git a/webapp/src/Controller/Jury/ClarificationController.php b/webapp/src/Controller/Jury/ClarificationController.php index 2030dcc1d5..ebbbfca552 100644 --- a/webapp/src/Controller/Jury/ClarificationController.php +++ b/webapp/src/Controller/Jury/ClarificationController.php @@ -14,6 +14,7 @@ use App\Utils\Utils; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr\Join; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -384,7 +385,7 @@ public function changeQueueAction(Request $request, int $clarId): Response } #[Route(path: '/send', methods: ['POST'], name: 'jury_clarification_send')] - public function sendAction(Request $request): Response + public function sendAction(Request $request, HtmlSanitizerInterface $htmlSanitizer): Response { $clarification = new Clarification(); @@ -436,7 +437,7 @@ public function sendAction(Request $request): Response $clarification->setJuryMember($this->getUser()->getUserIdentifier()); $clarification->setAnswered(true); - $clarification->setBody($request->request->get('bodytext')); + $clarification->setBody($htmlSanitizer->sanitize($request->request->get('bodytext'))); $clarification->setSubmittime(Utils::now()); $this->em->persist($clarification); diff --git a/webapp/src/Controller/RootController.php b/webapp/src/Controller/RootController.php index b99888dda4..3af1738fe1 100644 --- a/webapp/src/Controller/RootController.php +++ b/webapp/src/Controller/RootController.php @@ -4,6 +4,7 @@ use App\Service\DOMJudgeService; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -43,12 +44,13 @@ public function redirectAction(AuthorizationCheckerInterface $authorizationCheck public function markdownPreview( Request $request, #[Autowire(service: 'twig.runtime.markdown')] - MarkdownRuntime $markdownRuntime + MarkdownRuntime $markdownRuntime, + HtmlSanitizerInterface $htmlSanitizer ): JsonResponse { $message = $request->request->get('message'); if ($message === null) { throw new BadRequestHttpException('A message is required'); } - return new JsonResponse(['html' => $markdownRuntime->convert($message)]); + return new JsonResponse(['html' => $markdownRuntime->convert($htmlSanitizer->sanitize($message))]); } } diff --git a/webapp/src/Entity/Clarification.php b/webapp/src/Entity/Clarification.php index c19330ff42..3a9bda9ac9 100644 --- a/webapp/src/Entity/Clarification.php +++ b/webapp/src/Entity/Clarification.php @@ -356,6 +356,6 @@ public function getSummary(): string $newBody .= $line . ' '; } } - return Utils::cutString((empty($newBody) ? $this->getBody() : $newBody), 80); + return Utils::cutString(html_entity_decode((empty($newBody) ? $this->getBody() : $newBody)), 80); } } diff --git a/webapp/src/Form/Type/TeamClarificationType.php b/webapp/src/Form/Type/TeamClarificationType.php index 232d263ecb..0e7ae4d1a8 100644 --- a/webapp/src/Form/Type/TeamClarificationType.php +++ b/webapp/src/Form/Type/TeamClarificationType.php @@ -50,6 +50,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ]); $builder->add('message', TextareaType::class, [ 'label' => false, + 'sanitize_html' => true, 'attr' => [ 'rows' => 5, 'cols' => 85,