Skip to content

Commit 4492e02

Browse files
committed
Use HTMLPurifier
1 parent 6a5c209 commit 4492e02

File tree

4 files changed

+26
-24
lines changed

4 files changed

+26
-24
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@
7878
"webklex/php-imap": "^6.2",
7979
"ext-imap": "*",
8080
"tatevikgr/rss-feed": "dev-main",
81-
"ext-pdo": "*"
81+
"ext-pdo": "*",
82+
"ezyang/htmlpurifier": "^4.19"
8283
},
8384
"require-dev": {
8485
"phpunit/phpunit": "^9.5",

config/services.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,20 @@ services:
5151
tags:
5252
- { name: 'doctrine.dbal.schema_filter', connection: 'default' }
5353

54-
PhpList\Core\Domain\Messaging\MessageHandler\CampaignProcessorMessageHandler:
55-
autowire: true
54+
HTMLPurifier_Config:
55+
class: HTMLPurifier_Config
56+
factory: [ 'HTMLPurifier_Config', 'createDefault' ]
57+
calls:
58+
- [ set, [ 'Cache.SerializerPath', '%kernel.cache_dir%/htmlpurifier' ] ]
59+
- [ set, [ 'HTML.ForbiddenElements', [ 'script', 'style' ] ] ]
60+
- [ set, [ 'CSS.Disable', true ] ]
61+
- [ set, [ 'URI.DisableJavaScript', true ] ]
62+
- [ set, [ 'URI.DisableDataURI', true ] ]
63+
- [ set, [ 'HTML.Doctype', 'HTML5' ] ]
64+
- [ set, [ 'HTML.Allowed', 'p,br,b,strong,i,em,u,a[href|title],ul,ol,li,blockquote,img[src|alt|title],span,div'] ]
65+
66+
HTMLPurifier:
67+
class: HTMLPurifier
5668
arguments:
57-
$maxMailSize: '%messaging.max_mail_size%'
69+
- '@HTMLPurifier_Config'
70+

config/services/messenger.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ services:
2929
autoconfigure: true
3030
tags: [ 'messenger.message_handler' ]
3131

32+
PhpList\Core\Domain\Messaging\MessageHandler\CampaignProcessorMessageHandler:
33+
autowire: true
34+
arguments:
35+
$maxMailSize: '%messaging.max_mail_size%'
36+
3237
PhpList\Core\Domain\Subscription\MessageHandler\DynamicTableMessageHandler:
3338
autowire: true
3439
autoconfigure: true

src/Domain/Messaging/Service/Manager/MessageDataManager.php

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace PhpList\Core\Domain\Messaging\Service\Manager;
66

77
use Doctrine\ORM\EntityManagerInterface;
8+
use HTMLPurifier;
89
use PhpList\Core\Domain\Messaging\Model\Message;
910
use PhpList\Core\Domain\Messaging\Model\MessageData;
1011
use PhpList\Core\Domain\Messaging\Repository\MessageDataRepository;
@@ -14,6 +15,7 @@ class MessageDataManager
1415
public function __construct(
1516
private readonly MessageDataRepository $messageDataRepository,
1617
private readonly EntityManagerInterface $entityManager,
18+
private readonly HTMLPurifier $purifier,
1719
) {
1820
}
1921

@@ -68,30 +70,11 @@ public function setMessageData(Message $campaign, string $name, mixed $value): v
6870
$entity->setData($value !== null ? (string) $value : null);
6971
}
7072

71-
/**
72-
* Remove potentially harmful JavaScript from HTML content.
73-
*
74-
* This is a conservative cleaner: removes <script> blocks, javascript: URLs,
75-
* and inline event handlers (on*) attributes.
76-
*/
77-
private function disableJavascript(string $html): string
78-
{
79-
// Remove script tags and their content
80-
$clean = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
81-
82-
// Remove on*="..." event handler attributes
83-
$clean = preg_replace('/\s+on[a-zA-Z]+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', '', $clean) ?? $clean;
84-
85-
// Neutralize javascript: and data: URIs in href/src/style
86-
$clean = preg_replace('/\b(href|src)\s*=\s*("|\')\s*(javascript:|data:)[^\2]*\2/i', '$1="#"', $clean) ?? $clean;
87-
return preg_replace('/\bstyle\s*=\s*("|\')[^\1]*\1/i', '', $clean) ?? $clean;
88-
}
89-
9073
private function normalizeValueByName(string $name, mixed $value)
9174
{
9275
return match ($name) {
9376
'subject', 'campaigntitle' => is_string($value) ? strip_tags($value) : $value,
94-
'message' => is_string($value) ? $this->disableJavascript($value) : $value,
77+
'message' => is_string($value) ? $this->purifier->purify($value) : $value,
9578
'excludelist' => is_array($value) ? array_filter($value, fn ($val) => is_numeric($val)) : $value,
9679
'footer' => is_string($value) ? preg_replace('/<!--.*?-->/', '', $value) : $value,
9780
default => $value,

0 commit comments

Comments
 (0)