VYPR
High severityNVD Advisory· Published Dec 1, 2021· Updated Aug 3, 2024

Cross-site Scripting (XSS) - Stored in kevinpapst/kimai2

CVE-2021-3985

Description

Kimai 2 before 1.16.3 allows stored cross-site scripting via markdown fields when safe mode is disabled.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Kimai 2 before 1.16.3 allows stored cross-site scripting via markdown fields when safe mode is disabled.

Vulnerability

Kimai 2 versions prior to 1.16.3 contain a stored cross-site scripting (XSS) vulnerability in the markdown rendering functionality. The MarkdownExtension class, used in comment, timesheet, and other content fields, allowed the markdown parser to operate without safe mode when the $safe parameter was set to false [1]. This occurred in methods commentContent, timesheetContent, and markdownToHtml [2]. If an attacker could submit markdown input (e.g., via timesheet descriptions or comments), the application would output unsanitized HTML, enabling XSS payloads.

Exploitation

An attacker with write access to Kimai's markdown-enabled fields (such as timesheet descriptions or comments) can craft a malicious markdown string, for example `XSS) [2]. When this content is rendered for another user, the markdown parser converts it to an anchor tag with a javascript:` URL. Because safe mode was not forced, the browser executes the script, leading to XSS. No special network position is required beyond being an authenticated user who can create or edit content.

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of another user's browser session. This can lead to theft of session cookies, impersonation, data exfiltration, or actions performed on behalf of the victim. The impact is limited to the scope of the affected application, but may include disclosure of timesheet data, customer information, or administrative actions if the victim has elevated privileges.

Mitigation

The vulnerability is fixed in Kimai 2 version 1.16.3, released December 1, 2021 [4]. The fix forces safe mode and escapes markup in the toHtml method, deprecating the unsafe parameter [2]. Users should upgrade to 1.16.3 or later. No other workarounds are documented. The CVE is not listed on CISA's Known Exploited Vulnerabilities catalog.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
kevinpapst/kimai2Packagist
< 1.16.31.16.3

Affected products

2

Patches

1
76e09447c85e

make sure that markdown uses safe mode (#2961)

https://github.com/kevinpapst/kimai2Kevin PapstNov 19, 2021via ghsa
4 files changed · +39 4
  • src/Twig/Runtime/MarkdownExtension.php+3 3 modified
    @@ -61,7 +61,7 @@ public function commentContent(?string $content, bool $fullLength = false): stri
             }
     
             if ($this->isMarkdownEnabled()) {
    -            $content = $this->markdown->toHtml($content, false);
    +            $content = $this->markdown->toHtml($content);
             } elseif ($fullLength) {
                 $content = '<p>' . nl2br($content) . '</p>';
             }
    @@ -112,7 +112,7 @@ public function timesheetContent(?string $content): string
             }
     
             if ($this->isMarkdownEnabled()) {
    -            return $this->markdown->toHtml($content, false);
    +            return $this->markdown->toHtml($content);
             }
     
             return nl2br($content);
    @@ -126,6 +126,6 @@ public function timesheetContent(?string $content): string
          */
         public function markdownToHtml(string $content): string
         {
    -        return $this->markdown->toHtml($content, false);
    +        return $this->markdown->toHtml($content);
         }
     }
    
  • src/Utils/Markdown.php+6 1 modified
    @@ -33,7 +33,12 @@ public function __construct()
          */
         public function toHtml(string $text, bool $safe = true): string
         {
    -        $this->parser->setSafeMode($safe);
    +        if ($safe !== true) {
    +            @trigger_error('Only safe mode is supported in Markdown since 1.16.3 to prevent XSS attacks. Parameter $safe will be removed with 2.0', E_USER_DEPRECATED);
    +        }
    +
    +        $this->parser->setSafeMode(true);
    +        $this->parser->setMarkupEscaped(true);
     
             return $this->parser->text($text);
         }
    
  • tests/Twig/Runtime/MarkdownExtensionTest.php+12 0 modified
    @@ -27,6 +27,10 @@ public function testMarkdownToHtml()
             $sut = new MarkdownExtension(new Markdown(), $config);
             $this->assertEquals('<p><em>test</em></p>', $sut->markdownToHtml('*test*'));
             $this->assertEquals('<p># foobar</p>', $sut->markdownToHtml('# foobar'));
    +        $this->assertEquals(
    +            '<p><a href="javascript%3Aalert(`XSS`)">XSS</a></p>',
    +            $sut->markdownToHtml('[XSS](javascript:alert(`XSS`))')
    +        );
         }
     
         public function testTimesheetContent()
    @@ -47,6 +51,10 @@ public function testTimesheetContent()
                 "<ul>\n<li>test</li>\n<li>foo</li>\n</ul>\n<p>foo <strong>bar</strong></p>",
                 $sut->timesheetContent("- test\n- foo\n\nfoo __bar__")
             );
    +        $this->assertEquals(
    +            '<p><a href="javascript%3Aalert(`XSS`)">XSS</a></p>',
    +            $sut->timesheetContent('[XSS](javascript:alert(`XSS`))')
    +        );
         }
     
         public function testCommentContent()
    @@ -76,6 +84,10 @@ public function testCommentContent()
                 "<ul>\n<li>test</li>\n<li>foo</li>\n</ul>\n<p>foo <strong>bar</strong></p>",
                 $sut->commentContent("- test\n- foo\n\nfoo __bar__")
             );
    +        $this->assertEquals(
    +            '<p><a href="javascript%3Aalert(`XSS`)">XSS</a></p>',
    +            $sut->commentContent('[XSS](javascript:alert(`XSS`))')
    +        );
         }
     
         public function testCommentOneLiner()
    
  • tests/Utils/MarkdownTest.php+18 0 modified
    @@ -81,6 +81,24 @@ public function testDuplicateIds()
     ## test
     ### test
     # test
    +EOT;
    +        $this->assertEquals($html, $sut->toHtml($markdown));
    +    }
    +
    +    public function testLinksAreSanitized()
    +    {
    +        $sut = new Markdown();
    +
    +        $html = <<<'EOT'
    +<p><a href="javascript%3Aalert(`XSS`)">XSS</a><br />
    +<a href="javascript%3Aalert(&quot;XSS&quot;)">XSS</a><br />
    +<a href="javascript%3Aalert(&#039;XSS&#039;)">XSS</a></p>
    +EOT;
    +
    +        $markdown = <<<EOT
    +[XSS](javascript:alert(`XSS`))
    +[XSS](javascript:alert("XSS"))
    +[XSS](javascript:alert('XSS'))
     EOT;
             $this->assertEquals($html, $sut->toHtml($markdown));
         }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.