Skip to main content
Vulnerability Database/CVE-2026-94210

CVE-2026-94210: Hyve5 Leantime XSS Vulnerability

CVE-2026-94210 is a cross-site scripting flaw in Hyve5 Leantime affecting versions up to 3.9.8, allowing remote attackers to inject malicious scripts. This article covers technical details, affected versions, and mitigation steps.

Published:

CVE-2026-94210 Overview

CVE-2026-94210 is a stored cross-site scripting (XSS) vulnerability in Hyve5 Leantime through version 3.9.8. The flaw affects the getAllGrouped function within app/Domain/Tickets/Services/Tickets.php, part of the Kanban Board component. Attackers can inject persistent script payloads into swimlane header labels, which downstream consumers render as raw HTML. The vulnerability is authenticated but requires only low-privileged access and can be triggered remotely. Public exploit details and a research writeup are available, describing privilege escalation to the project Owner role via stored XSS in Kanban swimlane headers [CWE-79].

Critical Impact

Authenticated low-privilege users can inject stored JavaScript into Kanban swimlane headers, executing in the browser sessions of higher-privileged users and enabling account takeover up to Owner level.

Affected Products

  • Hyve5 Leantime versions up to and including 3.9.8
  • Component: Kanban Board (app/Domain/Tickets/Services/Tickets.php)
  • Function: getAllGrouped

Discovery Timeline

  • 2026-09-21 - CVE-2026-94210 published to the National Vulnerability Database
  • 2026-09-21 - Patch commit a30a6837b4071ac05a4f58d0e1baa2c62aa8695e merged via Leantime pull request #3767
  • 2026-09-21 - Last updated in NVD database

Technical Details for CVE-2026-94210

Vulnerability Analysis

The vulnerability resides in the getAllGrouped method in app/Domain/Tickets/Services/Tickets.php. This function constructs group labels used to render Kanban swimlane headers, table headers, and list accordion headers. Consumers render the label field as raw HTML because some grouping modes intentionally embed markup such as type icons or assignee avatars. Because the function did not escape user-controlled text before assembling the label, an attacker with write access to grouped fields (for example status names, milestones, or user-provided values) could persist HTML and JavaScript that later executes in any user's browser when the Kanban board loads. Impact is limited to confidentiality of session context and integrity of client-side actions, but exploitation supports privilege escalation to the project Owner role by hijacking an Owner's authenticated session.

Root Cause

The root cause is missing output encoding at a rendering boundary. Every branch of the switch ($searchCriteria['groupBy']) statement placed user-controlled strings directly into $label without invoking htmlspecialchars. Because consumers of the field are documented to render label raw, the escaping responsibility belonged inside getAllGrouped, and that responsibility was not enforced [CWE-79].

Attack Vector

An authenticated user with permission to create or edit grouped entities (status labels, milestone names, or similar) injects an HTML payload containing JavaScript into a value used by getAllGrouped. When any user views the Kanban board grouped by that field, the payload executes in their browser context. If the victim is a project Owner, the attacker can perform Owner-level actions through the victim's session.

php
// Patch excerpt from Tickets.php - escape user-controlled group labels at the source
if (isset($ticketGroups[$groupedFieldValue])) {
    $ticketGroups[$groupedFieldValue]['items'][] = $ticket;
} else {
    // 'label' is rendered RAW by every consumer (kanban swimlane header,
    // table/list accordion headers) because some groups embed markup - a type
    // icon, the assignee avatar. So this switch is the escaping boundary:
    // every branch must htmlspecialchars any user-controlled text it embeds.
    switch ($searchCriteria['groupBy']) {
        case 'status':
            $status = $this->getStatusLabels();

            if (isset($status[$groupedFieldValue])) {
                $label = htmlspecialchars((string) $status[$groupedFieldValue]['name'], ENT_QUOTES, 'UTF-8');
                $class = $status[$groupedFieldValue]['class'];
            } else {
                $label = 'New';
            }
}

Source: GitHub Commit a30a6837

Detection Methods for CVE-2026-94210

Indicators of Compromise

  • Stored values in Leantime status names, milestone names, or other Kanban group fields containing HTML tags such as <script>, <img onerror=, or <svg onload=.
  • Outbound browser requests from authenticated Leantime user sessions to unknown external hosts after viewing a Kanban board.
  • Unexpected privilege changes, project role modifications, or Owner-level actions performed shortly after a Kanban view event.

Detection Strategies

  • Query the Leantime database for group-label fields (status, milestone, custom fields) matching regex patterns for HTML or script tags.
  • Inspect web server access logs for POST or PUT requests to ticket, status, and milestone endpoints containing encoded angle brackets or javascript: payloads.
  • Correlate authentication events with anomalous administrative actions performed by Owner accounts immediately after a Kanban page load.

Monitoring Recommendations

  • Enable Content Security Policy (CSP) reporting on the Leantime deployment to capture inline script violations.
  • Log and alert on modifications to status labels, group names, and other fields consumed by getAllGrouped.
  • Monitor Leantime application logs for role changes and privilege grants to detect potential post-exploitation activity.

How to Mitigate CVE-2026-94210

Immediate Actions Required

  • Upgrade Leantime to the version containing patch commit a30a6837b4071ac05a4f58d0e1baa2c62aa8695e from pull request #3767.
  • Audit existing status names, milestone titles, and grouped field values for HTML or script content, and sanitize or remove any malicious entries.
  • Rotate session tokens and review Owner-level accounts for unauthorized activity since the vulnerability window.

Patch Information

The fix is delivered in commit a30a6837b4071ac05a4f58d0e1baa2c62aa8695e, merged through Leantime Pull Request #3767. The patch applies htmlspecialchars with ENT_QUOTES and UTF-8 encoding to every user-controlled label branch inside getAllGrouped, moving the escaping responsibility to the source function that consumers treat as trusted markup.

Workarounds

  • Restrict permissions for creating or editing status labels, milestones, and custom grouping fields to trusted administrators only.
  • Deploy a strict Content Security Policy that disallows inline scripts (script-src 'self') to reduce the impact of stored payloads.
  • Place Leantime behind a web application firewall with rules blocking HTML tags in ticket, status, and milestone parameters.
bash
# Example nginx CSP header to restrict inline script execution
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.