From ef459ca4c45bd2450ecd5d67a4d73c07f836ab25 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 12 Sep 2021 16:19:17 +0100 Subject: [PATCH] Altered the parsing of custom head to prevent htmlentities on content Was causing things like emjoi within script content to be somewhat mangled. Instead we force UTF8 only parsing via XML declaration. Added test to cover. For #2923 --- app/Util/CspService.php | 2 +- app/Util/HtmlNonceApplicator.php | 4 +-- tests/Settings/CustomHeadContentTest.php | 37 ++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/Util/CspService.php b/app/Util/CspService.php index ec5021371..812e1a4be 100644 --- a/app/Util/CspService.php +++ b/app/Util/CspService.php @@ -12,7 +12,7 @@ class CspService public function __construct(string $nonce = '') { - $this->nonce = $nonce ?: Str::random(16); + $this->nonce = $nonce ?: Str::random(24); } /** diff --git a/app/Util/HtmlNonceApplicator.php b/app/Util/HtmlNonceApplicator.php index 2653b7075..07298577c 100644 --- a/app/Util/HtmlNonceApplicator.php +++ b/app/Util/HtmlNonceApplicator.php @@ -21,10 +21,10 @@ class HtmlNonceApplicator return $html; } - $html = '' . $html . ''; + $html = '' . $html . ''; libxml_use_internal_errors(true); $doc = new DOMDocument(); - $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_SCHEMA_CREATE); + $doc->loadHTML($html, LIBXML_SCHEMA_CREATE); $xPath = new DOMXPath($doc); // Apply to scripts diff --git a/tests/Settings/CustomHeadContentTest.php b/tests/Settings/CustomHeadContentTest.php index 59d5fc06c..94ef4711d 100644 --- a/tests/Settings/CustomHeadContentTest.php +++ b/tests/Settings/CustomHeadContentTest.php @@ -2,6 +2,7 @@ namespace Tests\Settings; +use BookStack\Util\CspService; use Tests\TestCase; class CustomHeadContentTest extends TestCase @@ -26,4 +27,40 @@ class CustomHeadContentTest extends TestCase $resp = $this->get('/login'); $resp->assertSee('
cat
'); } + + public function test_nonce_application_handles_edge_cases() + { + $mockCSP = $this->mock(CspService::class); + $mockCSP->shouldReceive('getNonce')->andReturn('abc123'); + + $content = trim(' + + + + + + + '); + + $expectedOutput = trim(' + + + + + + + '); + + $this->setSettings(['app-custom-head' => $content]); + $resp = $this->get('/login'); + $resp->assertSee($expectedOutput); + } }