From 3a907983e327ebb2ec6b0dd67c18b35b9b729233 Mon Sep 17 00:00:00 2001 From: numew Date: Fri, 3 May 2024 11:17:35 +0200 Subject: [PATCH 1/4] snitize html #2499 --- composer.json | 1 + composer.lock | 379 ++++++++++++++---- config/packages/html_sanitizer.yaml | 16 + src/Controller/SignalementController.php | 2 +- src/Entity/Suivi.php | 8 +- .../Esabora/DossierMessageSISHFactory.php | 2 +- templates/back/notifications/index.html.twig | 2 +- .../back/signalement/view/suivis.html.twig | 2 +- .../view/visites/visite-item.html.twig | 2 +- templates/pdf/signalement.html.twig | 2 +- 10 files changed, 340 insertions(+), 76 deletions(-) create mode 100644 config/packages/html_sanitizer.yaml diff --git a/composer.json b/composer.json index fecb919d5..26f9e468d 100755 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "symfony/flex": "^2", "symfony/form": "6.4.*", "symfony/framework-bundle": "6.4.*", + "symfony/html-sanitizer": "6.4.*", "symfony/http-client": "6.4.*", "symfony/intl": "6.4.*", "symfony/lock": "6.4.*", diff --git a/composer.lock b/composer.lock index d55807042..787cf53a0 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": "e3bd92411a2b067d0d71ed00cb5ea6e4", + "content-hash": "2aea8c9209f174807119a6cfe7836277", "packages": [ { "name": "aws/aws-crt-php", @@ -3188,6 +3188,180 @@ ], "time": "2024-01-28T23:22:08+00:00" }, + { + "name": "league/uri", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "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.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", + "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "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.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-03-23T07:42:40+00:00" + }, { "name": "lorenzo/pinky", "version": "1.1.0", @@ -3241,6 +3415,73 @@ }, "time": "2023-07-31T13:36:50+00:00" }, + { + "name": "masterminds/html5", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + }, + "time": "2024-03-31T07:05:07+00:00" + }, { "name": "monolog/monolog", "version": "3.6.0", @@ -6706,6 +6947,75 @@ ], "time": "2024-03-23T16:06:09+00:00" }, + { + "name": "symfony/html-sanitizer", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/html-sanitizer.git", + "reference": "0ff5d77e3160c15db3dbc3515a4f0143e3e4a218" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/0ff5d77e3160c15db3dbc3515a4f0143e3e4a218", + "reference": "0ff5d77e3160c15db3dbc3515a4f0143e3e4a218", + "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.4.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": "2024-04-18T09:22:46+00:00" + }, { "name": "symfony/http-client", "version": "v6.4.6", @@ -11475,73 +11785,6 @@ }, "time": "2024-01-02T13:46:09+00:00" }, - { - "name": "masterminds/html5", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" - }, - "time": "2024-03-31T07:05:07+00:00" - }, { "name": "myclabs/deep-copy", "version": "1.11.1", diff --git a/config/packages/html_sanitizer.yaml b/config/packages/html_sanitizer.yaml new file mode 100644 index 000000000..47fa5223b --- /dev/null +++ b/config/packages/html_sanitizer.yaml @@ -0,0 +1,16 @@ +framework: + html_sanitizer: + sanitizers: + app.message_sanitizer: + allow_relative_links: true + allow_elements: + a: ['class', 'target', 'href', 'title', 'rel'] + ul: '' + ol: '' + li: '' + strong: '' + em: '' + br: '' + span: '' + div: '' + block_elements: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'] diff --git a/src/Controller/SignalementController.php b/src/Controller/SignalementController.php index 075146c29..a040e97f8 100755 --- a/src/Controller/SignalementController.php +++ b/src/Controller/SignalementController.php @@ -557,7 +557,7 @@ public function postUserResponse( ); $description = htmlspecialchars( - nl2br($request->get('signalement_front_response')['content']), + $request->get('signalement_front_response')['content'], \ENT_QUOTES, 'UTF-8' ); diff --git a/src/Entity/Suivi.php b/src/Entity/Suivi.php index 99fdecec5..2e8841a96 100755 --- a/src/Entity/Suivi.php +++ b/src/Entity/Suivi.php @@ -99,13 +99,17 @@ public function setCreatedBy(?User $createdBy): self return $this; } - public function getDescription(): ?string + public function getDescription($transformHtml = true): ?string { if (null !== $this->deletedAt) { return self::DESCRIPTION_DELETED.' '.$this->deletedAt->format('d/m/Y'); } - return $this->description; + if (!$transformHtml) { + return $this->description; + } + + return str_replace('<br />', '
', nl2br($this->description)); } public function setDescription(string $description): self diff --git a/src/Factory/Interconnection/Esabora/DossierMessageSISHFactory.php b/src/Factory/Interconnection/Esabora/DossierMessageSISHFactory.php index df471749c..82900a458 100644 --- a/src/Factory/Interconnection/Esabora/DossierMessageSISHFactory.php +++ b/src/Factory/Interconnection/Esabora/DossierMessageSISHFactory.php @@ -51,7 +51,7 @@ public function createInstance(Affectation $affectation): DossierMessageSISH $firstSuivi = $this->suiviRepository->findFirstSuiviBy($signalement, Suivi::TYPE_PARTNER); $cleanedSuiviDescription = null !== $firstSuivi && null !== $firstSuivi->getDescription() - ? HtmlCleaner::clean($firstSuivi->getDescription()) + ? HtmlCleaner::clean($firstSuivi->getDescription(false)) : null; $formatDate = AbstractEsaboraService::FORMAT_DATE; diff --git a/templates/back/notifications/index.html.twig b/templates/back/notifications/index.html.twig index 29875d85a..6e85c7110 100755 --- a/templates/back/notifications/index.html.twig +++ b/templates/back/notifications/index.html.twig @@ -61,7 +61,7 @@ |replace({'&t=___TOKEN___':'/'~notification.signalement.uuid}) |replace({'?t=___TOKEN___':'/'~notification.signalement.uuid}) |replace({'?folder=_up':'/'~notification.signalement.uuid~'?variant=resize'}) - |raw }} + |sanitize_html('app.message_sanitizer') }} {{ notification.suivi.createdBy ? notification.suivi.createdBy.nomComplet : notification.signalement.nomOccupant|upper~' '~notification.signalement.prenomOccupant|capitalize }} diff --git a/templates/back/signalement/view/suivis.html.twig b/templates/back/signalement/view/suivis.html.twig index fa0eaeeb6..87c4d4402 100755 --- a/templates/back/signalement/view/suivis.html.twig +++ b/templates/back/signalement/view/suivis.html.twig @@ -67,7 +67,7 @@ |replace({'&t=___TOKEN___':'/'~signalement.uuid}) |replace({'?t=___TOKEN___':'/'~signalement.uuid}) |replace({'?folder=_up':'/'~signalement.uuid~'?variant=resize'}) - |raw + |sanitize_html('app.message_sanitizer') }}
diff --git a/templates/back/signalement/view/visites/visite-item.html.twig b/templates/back/signalement/view/visites/visite-item.html.twig index 10b6bde33..7b60b0e59 100755 --- a/templates/back/signalement/view/visites/visite-item.html.twig +++ b/templates/back/signalement/view/visites/visite-item.html.twig @@ -68,7 +68,7 @@ {% if signalement.interventions is empty or intervention.details is empty %} Non renseigné {% else %} - {{ intervention.details|raw }} + {{ intervention.details|raw|nl2br|sanitize_html('app.message_sanitizer') }} {% endif %}

diff --git a/templates/pdf/signalement.html.twig b/templates/pdf/signalement.html.twig index 09d71a402..65486eb2a 100755 --- a/templates/pdf/signalement.html.twig +++ b/templates/pdf/signalement.html.twig @@ -270,7 +270,7 @@ {% endif %} {{ suivi.createdAt|date('d/m/Y') }} - {{ suivi.description|raw }} + {{ suivi.description|sanitize_html('app.message_sanitizer') }} {% endfor %} From 6994a7fa9a91d6d1fd8b8ce906870bf30c85e1b1 Mon Sep 17 00:00:00 2001 From: numew Date: Tue, 7 May 2024 11:18:01 +0200 Subject: [PATCH 2/4] accept more permisive html #2499 --- config/packages/html_sanitizer.yaml | 10 ++++++++-- src/Controller/SignalementController.php | 4 ++-- src/Entity/Suivi.php | 4 +++- templates/back/signalement/view/suivis.html.twig | 6 ++++++ .../signalement/view/visites/visite-item.html.twig | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/config/packages/html_sanitizer.yaml b/config/packages/html_sanitizer.yaml index 47fa5223b..2f6736aaa 100644 --- a/config/packages/html_sanitizer.yaml +++ b/config/packages/html_sanitizer.yaml @@ -2,6 +2,7 @@ framework: html_sanitizer: sanitizers: app.message_sanitizer: + allow_safe_elements: true allow_relative_links: true allow_elements: a: ['class', 'target', 'href', 'title', 'rel'] @@ -10,7 +11,12 @@ framework: li: '' strong: '' em: '' - br: '' span: '' div: '' - block_elements: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'] + table: '' + thead: '' + tbody: '' + tr: '' + th: '' + td: '' + drop_elements: ['img'] diff --git a/src/Controller/SignalementController.php b/src/Controller/SignalementController.php index a040e97f8..4256e197c 100755 --- a/src/Controller/SignalementController.php +++ b/src/Controller/SignalementController.php @@ -556,11 +556,11 @@ public function postUserResponse( isPublic: true, ); - $description = htmlspecialchars( + $description = nl2br(htmlspecialchars( $request->get('signalement_front_response')['content'], \ENT_QUOTES, 'UTF-8' - ); + )); $docs = $entityManager->getRepository(File::class)->findBy(['signalement' => $signalement, 'isTemp' => true, 'uploadedBy' => $user]); if (\count($docs)) { diff --git a/src/Entity/Suivi.php b/src/Entity/Suivi.php index 2e8841a96..8e9904ee3 100755 --- a/src/Entity/Suivi.php +++ b/src/Entity/Suivi.php @@ -109,7 +109,9 @@ public function getDescription($transformHtml = true): ?string return $this->description; } - return str_replace('<br />', '
', nl2br($this->description)); + $transformed = str_replace('<br />', '
', $this->description); + + return $transformed; } public function setDescription(string $description): self diff --git a/templates/back/signalement/view/suivis.html.twig b/templates/back/signalement/view/suivis.html.twig index 87c4d4402..57331af18 100755 --- a/templates/back/signalement/view/suivis.html.twig +++ b/templates/back/signalement/view/suivis.html.twig @@ -63,6 +63,12 @@ {% else %}
{% endif %} + {#{{ dump(suivi.description + |replace({'&t=___TOKEN___':'/'~signalement.uuid}) + |replace({'?t=___TOKEN___':'/'~signalement.uuid}) + |replace({'?folder=_up':'/'~signalement.uuid~'?variant=resize'}) + |sanitize_html('app.message_sanitizer')) + }}#} {{ suivi.description |replace({'&t=___TOKEN___':'/'~signalement.uuid}) |replace({'?t=___TOKEN___':'/'~signalement.uuid}) diff --git a/templates/back/signalement/view/visites/visite-item.html.twig b/templates/back/signalement/view/visites/visite-item.html.twig index 7b60b0e59..be7e03a22 100755 --- a/templates/back/signalement/view/visites/visite-item.html.twig +++ b/templates/back/signalement/view/visites/visite-item.html.twig @@ -68,7 +68,7 @@ {% if signalement.interventions is empty or intervention.details is empty %} Non renseigné {% else %} - {{ intervention.details|raw|nl2br|sanitize_html('app.message_sanitizer') }} + {{ intervention.details|sanitize_html('app.message_sanitizer') }} {% endif %}

From 335acbe59fe4a1cd0e899ef4d22465ce2a015411 Mon Sep 17 00:00:00 2001 From: numew Date: Tue, 7 May 2024 12:03:54 +0200 Subject: [PATCH 3/4] improve max_input_length config #2499 --- config/packages/html_sanitizer.yaml | 1 + src/Entity/Suivi.php | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/packages/html_sanitizer.yaml b/config/packages/html_sanitizer.yaml index 2f6736aaa..1e4343aa1 100644 --- a/config/packages/html_sanitizer.yaml +++ b/config/packages/html_sanitizer.yaml @@ -20,3 +20,4 @@ framework: th: '' td: '' drop_elements: ['img'] + max_input_length: 20000000 diff --git a/src/Entity/Suivi.php b/src/Entity/Suivi.php index 8e9904ee3..ff93341ec 100755 --- a/src/Entity/Suivi.php +++ b/src/Entity/Suivi.php @@ -109,9 +109,7 @@ public function getDescription($transformHtml = true): ?string return $this->description; } - $transformed = str_replace('<br />', '
', $this->description); - - return $transformed; + return str_replace('<br />', '
', $this->description); } public function setDescription(string $description): self From 9f9aef1b6375e432966e50ee0c6f0b9061799b95 Mon Sep 17 00:00:00 2001 From: numew Date: Tue, 7 May 2024 15:42:46 +0200 Subject: [PATCH 4/4] set on new suivi usager #2499 --- .../front/_partials/_suivi_signalement_tab_suivi.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/front/_partials/_suivi_signalement_tab_suivi.html.twig b/templates/front/_partials/_suivi_signalement_tab_suivi.html.twig index 02de52758..d8f96420f 100755 --- a/templates/front/_partials/_suivi_signalement_tab_suivi.html.twig +++ b/templates/front/_partials/_suivi_signalement_tab_suivi.html.twig @@ -14,7 +14,7 @@
- {{ suivi.description|replace({'___TOKEN___':csrf_token('suivi_signalement_ext_file_view')})|raw }} + {{ suivi.description|replace({'___TOKEN___':csrf_token('suivi_signalement_ext_file_view')})|sanitize_html('app.message_sanitizer') }}
{% endfor %}