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

CVE-2026-104979: Plane Project Management XSS Vulnerability

CVE-2026-104979 is a cross-site scripting flaw in Plane that allows authenticated users to inject malicious HTML and execute JavaScript to steal API tokens. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-104979 Overview

CVE-2026-104979 is a stored Cross-Site Scripting (XSS) vulnerability [CWE-79] in Plane, an open-source project management tool. Versions prior to 1.4.0 fail to sanitize HTML in the intake issue creation endpoint. The IntakeIssuePublicViewSet.create method writes description_html through Issue.objects.create(...) without invoking validate_html_content from the nh3 sanitizer. Any authenticated user, including a new account with no workspace memberships, can inject arbitrary HTML into projects that expose a published DeployBoard with intake enabled. The payload leverages a TipTap javascript: parser bypass combined with a target="_self" click handler to execute JavaScript in a viewer's session and exfiltrate a long-lived API token.

Critical Impact

Authenticated attackers can hijack project member sessions and steal long-lived API tokens through planted HTML links in intake issue descriptions.

Affected Products

  • Plane v1.3.1 and earlier versions prior to 1.4.0
  • Plane deployments with published DeployBoards that have intake enabled
  • Plane API serializers in apps/api/plane/api/serializers/issue.py and intake views in apps/api/plane/api/views/intake.py

Discovery Timeline

  • 2026-10-05 - CVE-2026-104979 published to NVD
  • 2026-10-05 - Last updated in NVD database

Technical Details for CVE-2026-104979

Vulnerability Analysis

The vulnerability stems from the intake creation flow accepting user-supplied description_html without routing it through the project's HTML sanitizer. Plane uses the nh3 library wrapped by the helper validate_html_content to strip dangerous markup elsewhere in the application. The intake path bypasses this control entirely, persisting raw HTML into the Issue model.

When a project member or viewer later opens a closed intake item, the stored markup is rendered through the TipTap editor on the client side. A parser quirk permits a javascript: URL to pass link validation, and the editor's target="_self" click handler executes the script in the viewer's authenticated origin. The attacker's payload then reads session storage and exfiltrates a long-lived API token to an attacker-controlled endpoint.

Root Cause

The root cause is missing input sanitization in the intake serializer path. The pre-patch code either accepted raw HTML or only applied lxml round-tripping, which does not strip script URIs. Both comment_html and intake description_html fields reached the database without passing through validate_html_content.

Attack Vector

An attacker creates an authenticated account, locates a public DeployBoard with intake enabled, and submits an intake issue containing a crafted TipTap-compatible link. The attack requires user interaction: a project member or viewer must click the planted link within the closed intake item. On click, JavaScript executes under the viewer's origin, enabling token theft and account takeover.

python
# Patch: apps/api/plane/api/serializers/issue.py
# Pre-patch logic parsed HTML via lxml without sanitizing script URIs.
# Post-patch routes comment_html through validate_html_content (nh3).

    def validate(self, data):
        if "comment_html" in data and data["comment_html"]:
            is_valid, error_msg, sanitized_html = validate_html_content(data["comment_html"])
            if not is_valid:
                raise serializers.ValidationError({"comment_html": "HTML content is not valid"})
            if sanitized_html is not None:
                data["comment_html"] = sanitized_html
        return data

Source: GitHub Commit 0d58adb

python
# Patch: apps/api/plane/api/views/intake.py
# Imports the sanitizer used to clean intake description_html before persistence.

from plane.utils.content_validator import validate_html_content

Source: GitHub Commit 0d58adb

Detection Methods for CVE-2026-104979

Indicators of Compromise

  • Intake issues containing anchor tags with javascript: URI schemes in description_html
  • Outbound HTTP requests from browser sessions to unknown domains carrying API token values in query strings or request bodies
  • Unexpected API activity from long-lived tokens originating from new IP addresses or user agents
  • New user accounts created shortly before intake submissions on projects with public DeployBoards

Detection Strategies

  • Query the Issue table for description_html and comment_html values matching patterns such as javascript:, data:text/html, or onerror=
  • Review web server logs for POST requests to IntakeIssuePublicViewSet endpoints followed by anonymous DeployBoard retrievals
  • Correlate TipTap client errors or Content Security Policy violations with intake view sessions
  • Audit API token usage logs for tokens that suddenly appear in network traffic patterns distinct from their owner's baseline

Monitoring Recommendations

  • Enable Content Security Policy headers with script-src restrictions on the Plane web frontend to limit inline execution
  • Alert on any Plane API tokens appearing in referrer headers, URL parameters, or external DNS queries
  • Monitor creation of intake issues by accounts with zero workspace memberships, which is the attack precondition
  • Track DeployBoard publication events and periodically audit which projects expose public intake

How to Mitigate CVE-2026-104979

Immediate Actions Required

  • Upgrade Plane to version 1.4.0 or later, which applies validate_html_content to both comment and intake HTML fields
  • Rotate all long-lived API tokens for users who may have viewed intake items on affected projects
  • Disable public DeployBoards or disable intake on DeployBoards until the upgrade is complete
  • Review and purge existing intake issues containing suspicious HTML in description_html

Patch Information

The fix is included in Plane Release v1.4.0 via Pull Request #9287. The patch imports validate_html_content from plane.utils.content_validator and applies it in both IntakeIssuePublicViewSet.create and the issue serializer's validate method. See the GitHub Security Advisory GHSA-hh2r-3hwp-mvq3 for the full advisory.

Workarounds

  • Unpublish any DeployBoards or disable the intake feature on DeployBoards exposed to untrusted users until the patch is applied
  • Instruct project members and viewers not to click links inside closed intake items on affected deployments
  • Apply a reverse-proxy Content Security Policy that blocks javascript: URL schemes and inline scripts for Plane routes
  • Short-circuit long-lived API token exposure by reducing token lifetime or scoping tokens to specific endpoints
bash
# Upgrade Plane via Docker Compose to the patched release
git fetch --tags
git checkout v1.4.0
docker compose pull
docker compose up -d

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.