diff --git a/src/Generator/HtmlGenerator.php b/src/Generator/HtmlGenerator.php
index 92acd10..e460a4c 100644
--- a/src/Generator/HtmlGenerator.php
+++ b/src/Generator/HtmlGenerator.php
@@ -46,7 +46,12 @@ public function generate(MotdItemCollection $collection): string
if ($motdItem->getColor()) {
if (str_contains($motdItem->getColor(), '#')) {
- $tags['span'][] = sprintf('color: %s;', $motdItem->getColor());
+ // Only allow valid hex color codes (without alpha channel), such as #FFF and #000000.
+ if(!preg_match('/^#(([0-9A-Fa-f]{2}){3}|[0-9A-Fa-f]{3})$/i', $motdItem->getColor())) {
+ continue;
+ }
+
+ $tags['span'][] = sprintf('color: %s;', $this->escape($motdItem->getColor()));
} else {
$color = $this->colorCollection->get($motdItem->getColor());
if (!$color) {
@@ -77,7 +82,7 @@ public function generate(MotdItemCollection $collection): string
foreach ($tags as $tag => $styles) {
$value = sprintf('<%s style="%s">%s%s>', $tag, implode(' ', $styles), $value, $tag);
}
- $value = sprintf($value, $motdItem->getText());
+ $value = sprintf($value, $this->escape($motdItem->getText()));
$result .= $value;
}
@@ -88,4 +93,9 @@ public function setFormatNewLine(string $format): void
{
$this->formatNewLine = $format;
}
+
+ private function escape(string $text): string
+ {
+ return htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
+ }
}
diff --git a/tests/Generator/HtmlGeneratorTest.php b/tests/Generator/HtmlGeneratorTest.php
index 98e1d2d..c5e6647 100644
--- a/tests/Generator/HtmlGeneratorTest.php
+++ b/tests/Generator/HtmlGeneratorTest.php
@@ -275,4 +275,62 @@ public function testGenerateWithEmptyCollection()
$this->assertEquals('', $result);
}
-}
\ No newline at end of file
+
+ public function testFilterInvalidHexColor()
+ {
+ $collection = new MotdItemCollection();
+
+ $item1 = new MotdItem();
+ $item1->setText('Hello');
+ $item1->setColor('#FF555');
+ $collection->add($item1);
+
+ $item2 = new MotdItem();
+ $item2->setText("\n");
+ $collection->add($item2);
+
+ $item3 = new MotdItem();
+ $item3->setText('Beautiful');
+ $item3->setColor('#800080');
+ $collection->add($item3);
+
+ $item4 = new MotdItem();
+ $item4->setText("\n");
+ $collection->add($item4);
+
+ $item5 = new MotdItem();
+ $item5->setText('World');
+ $item5->setColor('#42');
+ $collection->add($item5);
+
+ $item6 = new MotdItem();
+ $item6->setText('!');
+ $item6->setColor('#42a');
+ $collection->add($item6);
+
+ $generator = new HtmlGenerator();
+ $result = $generator->generate($collection);
+
+ $this->assertEquals('
Beautiful
!', $result);
+ }
+
+ public function testEscapeInput()
+ {
+ $collection = new MotdItemCollection();
+
+ $item1 = new MotdItem();
+ $item1->setText('Hover me');
+ $item1->setColor('#000000" onmouseover="javascript:alert(\'XSS when mouse pointer enters the span element\')"');
+ $collection->add($item1);
+
+ $item2 = new MotdItem();
+ $item2->setText('');
+ $item2->setColor('#800080');
+ $collection->add($item2);
+
+ $generator = new HtmlGenerator();
+ $result = $generator->generate($collection);
+
+ $this->assertEquals('<script>alert("XSS on page load")</script>', $result);
+ }
+}