Opencast: Stored XSS in Paella player via WebVTT/DFXP caption cue text
CVE-2026-77615 (GitHub advisory GHSA-m6c8-jcw2-5r25) is a high-severity vulnerability with a CVSS 3.1 base score of 8.7, classed as Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79), in org.opencastproject:opencast-engage-paella-player-7 (Maven) and paella-core (npm). Fixed versions: org.opencastproject:opencast-engage-paella-player-7 19.7 or 20.2; paella-core 1.50.6.
Published 17 Sept 2026Updated 18 Sept 2026
- CVE ID
- CVE-2026-77615
- GHSA ID
- GHSA-m6c8-jcw2-5r25
- EPSS
- 0.39%higher than 33% of CVEs
- Weakness (CWE)
- CWE-79Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
- KEV status
- Not in KEV
- Published
- 17 Sept 2026
- Updated
- 18 Sept 2026
- Ecosystems
- Maven
- npm
- Assigned by (CNA)
- GitHub_M
Summary
Source: OSV (GitHub advisory)
The Opencast Paella player renders caption cue text into innerHTML without escaping. The captions canvas clears _captionsContainer.innerHTML and then appends each active cue with _captionsContainer.innerHTML += cue, so HTML inside a WebVTT or DFXP cue becomes live DOM and executes in the Opencast origin.
The caption track is read from any media package element with a captions/* flavor and is served, with the player manifest, to anonymous viewers through /search/episode.json. The caption plugins that consume it are enabled in the default player configuration, the "Subtitles" upload that produces a captions/source track is active by default, and no caption processing step escapes the cue text.
A user who can upload a subtitle to an event and publish it stores the payload in the published caption file. Any viewer who opens the event and turns captions on runs the script.
Result: a non-admin content author stores JavaScript in a subtitle cue that executes in the browser session of every viewer who enables captions on that event, including anonymous viewers and authenticated staff.
Affected
opencast/opencast, engage-paella-player module. Supported release lines 19.x and 20.x are affected (and 18.x). Live-confirmed on 18.8 (Paella 7, paella-core 1.50.2) and 20.0 (Paella 8, paella-core 1.50.4); 19.5 ships the code-identical caption path (paella-core 1.50.4, same EpisodeConversor and default plugin config as 20.0). The captions canvas uses the same innerHTML += cue sink across these versions.
Default configuration: the WebVTT and DFXP caption plugins are enabled: true in etc/ui-config/mh_default_org/paella7/config.json, the "Subtitles" upload option (captions/source, .vtt) is active in etc/listproviders/event.upload.asset.options.properties, and the fast workflow publishes captions/* to the engage player.
Condition: an event with a caption track published to the engage player. No non-default flag required.
Root cause
The captions canvas appends each cue to _captionsContainer.innerHTML in the bundled paella-core (served at /paellaN/ui/paella-player.js / the 20.x core chunk), so markup in a cue becomes live DOM. The caption entry is built from any media package element whose flavor matches captions/* at modules/engage-paella-player-7/src/js/EpisodeConversor.js:392, and /search/episode.json serves the manifest and the caption file to anonymous clients. The WebVTT and DFXP plugins that consume it are enabled by default at etc/ui-config/mh_default_org/paella7/config.json:571 and :574. The cue text is not HTML-escaped before assignment to innerHTML, and partial-process-uploaded-captions only cuts and tags the file, it never sanitizes it. Opencast sets neither a Content-Security-Policy nor an X-Content-Type-Options header, so the injected script runs without restriction.
Reproduction
Default config, default caption plugins enabled, a non-admin user with ROLE_API_EVENTS_CREATE, ROLE_API_EVENTS_TRACK_EDIT, and ROLE_UI_TASKS_CREATE (no ROLE_ADMIN).
- As the non-admin user, create an event, then upload a WebVTT subtitle as
captions/sourcewhose cue text is an XSS payload, and publish the event.
WEBVTT
00:00:00.000 --> 00:00:30.000
<img src=x onerror=document.title=window.__xss=document.domain>
- The anonymous search manifest then exposes the caption and serves the cue raw.
GET /search/episode.json?id=<event>
"type":"captions/source", "url":".../static/.../x.vtt"
GET .../static/.../x.vtt -> cue text returned verbatim
- Open the event in the player as an anonymous viewer, open the captions menu, and select the track; the cue is written to
innerHTMLand theonerrorhandler runs.
Live-verified: on Opencast 18.8 (build 8705223) in Chrome, a non-admin author published the subtitle and an anonymous viewer enabled captions, rendering the cue as a live <img> node and setting window.__xss and document.title to document.domain. On Opencast 20.0 (build d919405, Paella 8), the served core bundle contains the identical innerHTML += cue sink and the player loads the cue raw; the sink executes JavaScript in the engage origin when fed the player's own published caption file.
Impact
- JavaScript execution in the Opencast origin in the session of any viewer who enables captions on the event.
- Anonymous viewers and authenticated staff are equally affected; an instructor or admin viewer exposes that session context to the script.
- Session and CSRF-token theft, actions performed as the victim against the Opencast REST API.
- Stored by a non-admin content author, triggered by viewing with captions on, no attacker authentication at view time.
Credit
Jan Kahmen, turingpoint (jan@turingpoint.de)
Technical details
Source: GitHub Advisory Database
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:NHigh8.7Base score from GitHub Advisory Database
- Attack Vector AV
- Network
- Attack Complexity AC
- Low
- Privileges Required PR
- Low
- User Interaction UI
- Required
- Scope S
- Changed
- Confidentiality C
- High
- Integrity I
- High
- Availability A
- None
Affected packages
Source: OSV (GitHub advisory)
- org.opencastproject:opencast-engage-paella-player-7Maven
pkg:maven/org.opencastproject/opencast-engage-paella-player-7- Affected versions
- < 19.7>= 20.0, < 20.2
- Fixed in
- 19.720.2
Show 61 listed versions
- 12.0
- 12.1
- 12.11
- 12.12
- 12.13
- 12.2
- 12.3
- 12.5
- 12.6
- 12.7
- 12.8
- 12.9
- paella-corenpm
pkg:npm/paella-core- Affected versions
- < 1.50.6
- Fixed in
- 1.50.6
| Package | Affected versions | Fixed in |
|---|---|---|
org.opencastproject:opencast-engage-paella-player-7 Maven pkg:maven/org.opencastproject/opencast-engage-paella-player-7 |
Show 61 listed versions
|
|
paella-core npm pkg:npm/paella-core |
|
|
Fix
Source: OSV (GitHub advisory)
Fix available
- org.opencastproject:opencast-engage-paella-player-7: upgrade to
19.7or20.2, depending on your release line - paella-core: upgrade to
1.50.6or later
Patch commits
Upgrade paella 8
+589 −4788 across 4 files
- modules/engage-paella-player-8/package-lock.json+541 −4751
- modules/engage-paella-player-8/package.json+10 −10
- modules/engage-paella-player-8/src/applyQueryParams.ts+37 −26
- modules/engage-paella-player-8/src/watch.ts+1 −1
Fix XSS bug in captions and HTML DOM generation
+34043 −26014 across 7 files
- dist/paella-core.js+31642 −23950
- dist/paella-core.js.map+1 −1
- package-lock.json+2319 −2046
- package.json+1 −1
- src/js/captions/CaptionsCanvas.js+9 −6
- src/js/captions/DFXPParser.js+1 −1
- src/js/core/dom.js+70 −9
Fix security issue in WevVTT captions
+195 −264 across 5 files
- package-lock.json+179 −227
- package.json+2 −1
- src/index.html+0 −2
- src/js/Paella.js+0 −1
- src/js/captions/WebVTTParser.js+14 −33
Vulnerability intelligence
Root cause analysis
Vulnerable functions
References
Fixes 5
- github.com/opencast/opencast/pull/7736
- github.com/opencast/opencast/commit/701682c635f668228c3e8fb7b4564b3294788e40
- github.com/polimediaupv/paella-core/commit/94a36490808ac5a1f60a0745d71ec9253f6d2…
- github.com/polimediaupv/paella-core/commit/9b2f14ec4cf55efaf4c045c77a5ed8f5ec559…
- github.com/polimediaupv/paella-player/commit/6fe4af7306044198c8e91e2e7f4128428b8…
Advisories 4
Package 1
Credits
- kah-jareporter
Questions about CVE-2026-77615
- What is the GHSA ID for CVE-2026-77615?
- GitHub tracks CVE-2026-77615 as GHSA-m6c8-jcw2-5r25.
- What is the CVSS score of CVE-2026-77615?
- CVE-2026-77615 has a CVSS 3.1 base score of 8.7 (High), vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N.
- Which versions are affected by CVE-2026-77615?
- org.opencastproject:opencast-engage-paella-player-7 (Maven): < 19.7; >= 20.0, < 20.2. paella-core (npm): < 1.50.6.
- How do I fix CVE-2026-77615?
- Upgrade each package to its fixed version: org.opencastproject:opencast-engage-paella-player-7 19.7 or 20.2; paella-core 1.50.6. The fix is in commits 701682c, 94a3649 and 9b2f14e and pull request #7736.
- What is the Package URL (PURL) for CVE-2026-77615?
- The affected packages' PURLs are pkg:maven/org.opencastproject/opencast-engage-paella-player-7 and pkg:npm/paella-core.
- Is CVE-2026-77615 exploited in the wild?
- CISA's Known Exploited Vulnerabilities catalog doesn't list CVE-2026-77615. That means no confirmed exploitation has been catalogued, not that it is safe to leave unpatched. EPSS estimates a 0.39% chance of exploitation in the next 30 days, higher than 33% of CVEs.
Data sources
Upstream records
- OSV (GitHub advisory)LoadedRaw JSON
- GitHub Advisory DatabaseLoadedRaw JSON
- OSV (CVE record)LoadedRaw JSON
- CVE.orgLoaded
- CISA KEV catalogLoaded
Fetched 5 days ago (9 Oct 2026, 02:42 UTC). Records refresh automatically after 24 hours.
Where each part of this page came from
- Title and description
- OSV (GitHub advisory)
- Severity score
- GitHub Advisory Database
- Weakness (CWE)
- GitHub Advisory Database
- Affected packages
- OSV (GitHub advisory)
- Fix
- OSV (GitHub advisory)
- References
- Computed by CVEminer
- KEV status
- CISA KEV catalog
- EPSS
- GitHub Advisory Database
OSV and GitHub Advisory Database content is licensed CC BY 4.0. Values marked as computed or derived were calculated by CVEminer from the upstream data.