CVE-2026-28737
published 2026-06-17CVE-2026-28737: Gitea: Stored XSS via glTF `extensionsRequired` in Gitea 3D File Viewer ## Summary Me again. Gitea's built-in 3D file viewer (powered by Online3DViewer) is…
high
Gitea: Stored XSS via glTF `extensionsRequired` in Gitea 3D File Viewer
## Summary
Me again.
Gitea's built-in 3D file viewer (powered by Online3DViewer) is vulnerable to stored cross-site scripting (XSS) through crafted `.gltf` files. When a glTF file declares an unsupported required extension, Online3DViewer generates an error message containing the extension name and Gitea inserts it into the DOM using `innerHTML` without sanitization. An attacker who can push a `.gltf` file to any repository can execute arbitrary JavaScript in the context of any user who views the file.
## Affected Versions
- Gitea **1.25.0** and later (3D file preview was introduced in 1.25 via the Online3DViewer integration)
- Confirmed on `gitea:1.25-nightly` (SHA `e33d1da...`), which bundles `online-3d-viewer` npm package v0.16.0
- The upstream [Online3DViewer](https://github.com/kovacsv/Online3DViewer) library is the root cause
## Severity
- Stored XSS: the payload persists in the repository and fires on every page view
- Executes under the Gitea origin with the victim's session (cookies, CSRF tokens)
- Any authenticated user viewing the file is compromised
- Enables full account takeover (token creation, settings modification, repository manipulation)
- No user interaction beyond viewing the file page is required
## Details
### Root Cause
When Online3DViewer parses a glTF file, it checks whether all `extensionsRequired` entries are supported. For unsupported extensions, it calls:
```javascript
// In the Online3DViewer bundle (online-3d-viewer.js)
// Approximate offset 1142618 in the bundled chunk
this.SetError(yp("Unsupported extension: {0}.", unsupportedExtensions.join(", ")));
```
The `SetError` method stores this message, and Gitea's rendering code inserts it into the page using `innerHTML`:
```javascript
// Gitea's error display handler
element.innerHTML = errorMessage; // unsanitized
```
The extension names from `extensionsRequired` are taken directly from the JSON file wAffected
1 ranges
| Vendor | Product | Version range | Fixed in |
|---|---|---|---|
| code.gitea.io | gitea | >= 1.25.0 < 1.26.0 | 1.26.0 |
Stop checking back — get the weekly exploitation signal.
Every Monday: what got weaponized or added to CISA KEV in the last seven days — each CVE cross-linked to its PoC, Nuclei template, and detection rule. Free, one email a week, unsubscribe in one click.
No detection rules found.
No public exploits indexed.
No writeups or analysis indexed.
2026-06-17
Published