From bb891e182e00a1248eb806b8162edada233e8cba Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 2 Oct 2026 16:19:49 +0200 Subject: [PATCH] fix(security): escape the project name in the analytic modal title LayoutHelper::analytic() prefixes the title with the raw project name, and the AJAX branch of analytic/layout.php printed that title as is, so a project name containing markup was injected into the analytic modals. Escape it with $this->text->e() like the other layouts do. It does not double-encode, so the ">" separator still renders as ">". --- app/Template/analytic/layout.php | 2 +- tests/units/Core/TemplateTest.php | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/Template/analytic/layout.php b/app/Template/analytic/layout.php index 7159094..11d5b9b 100644 --- a/app/Template/analytic/layout.php +++ b/app/Template/analytic/layout.php @@ -1,6 +1,6 @@ projectHeader->render($project, 'TaskListController', 'show') ?> diff --git a/tests/units/Core/TemplateTest.php b/tests/units/Core/TemplateTest.php index a065aa8..bdce8aa 100644 --- a/tests/units/Core/TemplateTest.php +++ b/tests/units/Core/TemplateTest.php @@ -3,6 +3,7 @@ namespace KanboardTests\units\Core; use KanboardTests\units\Base; +use Kanboard\Core\Http\Request; use Kanboard\Core\Security\Role; use Kanboard\Core\Template; @@ -82,4 +83,26 @@ class TemplateTest extends Base $html ); } + + public function testAnalyticAjaxLayoutEscapesProjectNameInTitle() + { + $this->container['request'] = new Request($this->container, array( + 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest', + )); + + $html = $this->container['helper']->layout->analytic('analytic/task_distribution', array( + 'project' => array('id' => 1, 'name' => ''), + 'metrics' => array(), + 'title' => 'Task distribution', + )); + + $this->assertStringNotContainsString( + '', + $html + ); + $this->assertStringContainsString( + '

<img src=x onerror=alert(document.domain)> > Task distribution

', + $html + ); + } }