CVE-2023-30619 (PHP, published 2023-05-04T14:15Z)
Description: value: Tuleap Open ALM is a Libre and Open Source tool for end to end traceability of application and system developments. The title of an artifact is not properly escaped in the tooltip. A malicious user with the capability to create an artifact or to edit a field title could force victim to execute uncontrolled code. This issue has been patched in version 14.7.99.143.

Severity: nan (CVSS v3 base score: 5.4)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/Enalean/tuleap
Files changed:
plugins/tracker/include/Tracker/Artifact/Artifact.php
plugins/tracker/include/Tracker/Semantic/Tooltip/TooltipFetcher.php
plugins/tracker/templates/tooltip/artifact-tooltip-title.mustache
plugins/tracker/tests/unit/Tracker/Semantic/Tooltip/TooltipFetcherTest.php
Commit message: Fix request #31586: XSS in the tooltip via an artifact title

Next step for the current story #26777 is to display the xref on top of
the title so in order to prepare the field we use mustache to escape the
title instead of DOMPurifier.

Part of story #26777: have artifact tooltips on roadmap

Change-Id: I534ead8a88361b364f5ee81556251dc3dc4c0bf6


Vulnerable code:
    public function __construct(OtherSemanticTooltipEntryFetcher ...$other_semantic_tooltip_entry_fetchers)
    {
            TooltipJSON::fromHtmlTitleAndHtmlBody((string) $artifact->getTitle(), $html)
                ->withAccentColor($artifact->getTracker()->getColor()->getName())
            (new TooltipFetcher())
            (new TooltipFetcher())
        $tooltip = (new TooltipFetcher())->fetchArtifactTooltip($artifact, $tooltip, $user);
        self::assertEquals('The title', $tooltip->unwrapOr('')->title_as_html);
        self::assertEquals('The title', $tooltip->unwrapOr('')->title_as_html);
        $tooltip = (new TooltipFetcher())->fetchArtifactTooltip($artifact, $tooltip, $user);
        self::assertEquals('', $tooltip->unwrapOr('')->title_as_html);

Fixed code:
            TemplateRendererFactory::build(),
    public function __construct(
        private readonly \TemplateRendererFactory $renderer_factory,
        OtherSemanticTooltipEntryFetcher ...$other_semantic_tooltip_entry_fetchers,
    ) {
            TooltipJSON::fromHtmlTitleAndHtmlBody(
                $this->renderer_factory
                    ->getRenderer(__DIR__ . '/../../../../templates/tooltip/')
                    ->renderToString('artifact-tooltip-title', ['title' => $artifact->getTitle()]),
                $html
            )->withAccentColor($artifact->getTracker()->getColor()->getName())
{{ title }}
use TemplateRendererFactory;
use Tuleap\Templating\TemplateCache;
        $template_cache = $this->createMock(TemplateCache::class);
        $template_cache->method('getPath')->willReturn(null);
        $template_factory = new TemplateRendererFactory($template_cache);

            (new TooltipFetcher($template_factory))
        $template_cache = $this->createMock(TemplateCache::class);
        $template_cache->method('getPath')->willReturn(null);
        $template_factory = new TemplateRendererFactory($template_cache);

            (new TooltipFetcher($template_factory))
        $template_cache = $this->createMock(TemplateCache::class);
        $template_cache->method('getPath')->willReturn(null);
        $template_factory = new TemplateRendererFactory($template_cache);

        $tooltip = (new TooltipFetcher($template_factory))->fetchArtifactTooltip($artifact, $tooltip, $user);
        self::assertStringContainsString('The title', $tooltip->unwrapOr('')->title_as_html);
        $template_cache = $this->createMock(TemplateCache::class);
        $template_cache->method('getPath')->willReturn(null);
        $template_factory = new TemplateRendererFactory($template_cache);

            $template_factory,
        self::assertStringContainsString('The title', $tooltip->unwrapOr('')->title_as_html);
        $template_cache = $this->createMock(TemplateCache::class);
        $template_cache->method('getPath')->willReturn(null);
        $template_factory = new TemplateRendererFactory($template_cache);

        $tooltip = (new TooltipFetcher($template_factory))->fetchArtifactTooltip($artifact, $tooltip, $user);
        self::assertEquals('', trim($tooltip->unwrapOr('')->title_as_html));