Skip to content

CVE-2026-4983: Verified Reproduction

CVE-2026-4983: Open VSX Registry serves HTML inline enabling session/token exfiltration

CVE-2026-4983 is verified against the affected target. Vulnerability class: XSS. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00202.

REPRO-2026-00202 XSS Jul 2, 2026 CVE entry .txt
Severity
MEDIUM
CVSS
4.1
Confidence
HIGH
Reproduced in
36m 21s
Tool calls
250
Spend
$4.48
01 · Overview

What Is CVE-2026-4983?

CVE-2026-4983 is a medium-severity stored cross-site scripting vulnerability (CWE-79) in the Open VSX Registry, where the /vscode/unpkg/ endpoint serves user-supplied HTML/SVG files from VSIX extension packages inline, enabling session/token exfiltration. Pruva reproduced it (reproduction REPRO-2026-00202).

02 · Severity & CVSS

CVE-2026-4983 Severity & CVSS Score

CVE-2026-4983 is rated medium severity, with a CVSS base score of 4.1 out of 10.

MEDIUM threat level
4.1 / 10 CVSS base
Weakness CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.

How to Reproduce CVE-2026-4983

$ pruva-verify REPRO-2026-00202
or curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00202/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-4983

Information disclosure — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

VSIX package containing an HTML file with embedded JavaScript, published via POST /api/-/publish, then accessed via GET /vscode/unpkg/{ns}/{ext}/{ver}/extension/payload.html

Attack chain
  1. POST /api/-/publish?token={token} (upload VSIX with HTML)
  2. GET /vscode/unpkg/{namespace}/{extension}/{version}/extension/payload.html (served with text/html, no CSP)
How the agent worked 603 events · 250 tool calls · 36 min
36 minDuration
250Tool calls
152Reasoning steps
603Events
Agent activity over 36 min
Support
50
Hypothesis
2
Repro
386
Judge
39
Variant
122
0:0036:21

Root Cause and Exploit Chain for CVE-2026-4983

Versions: All versions before 1.0.2 (confirmed on v1.0.1)

The Open VSX Registry /vscode/unpkg/ endpoint serves user-supplied files extracted from published VSIX packages with insecure HTTP response headers. In versions before 1.0.2, HTML files (.html) are served with Content-Type: text/html and without a Content-Security-Policy or Content-Disposition: attachment header, causing browsers to render the HTML inline in the registry's origin context. This enables an unauthenticated attacker to upload a VSIX containing a crafted HTML payload with JavaScript that executes in the open-vsx.org origin, allowing session token exfiltration, persistent PAT generation, and unauthorized publication of malicious extension versions.

  • Package/component affected: org.eclipse.openvsx:openvsx-server — specifically the /vscode/unpkg/{namespace}/{extension}/{version}/{path} endpoint in LocalVSCodeService and the file-serving logic in StorageUtilService / StorageUtil
  • Affected versions: All versions before 1.0.2 (confirmed on v1.0.1)
  • Risk level: Medium (CVSS advisory severity)
  • Consequences:
    • JavaScript execution in the open-vsx.org origin context
    • Session cookie/token exfiltration from authenticated users
    • Persistent Personal Access Token (PAT) generation
    • Unauthorized publication of malicious extension versions
    • Supply chain attack via compromised extension updates distributed to VS Code, VSCodium, Cursor, Windsurf, and compatible editors

Impact Parity

  • Disclosed/claimed maximum impact: Session/token exfiltration, persistent PAT generation, unauthorized publication of malicious extension versions, supply chain attack
  • Reproduced impact from this run: Confirmed that the /vscode/unpkg/ endpoint serves user-controlled HTML files with Content-Type: text/html and no Content-Security-Policy header, which would cause a browser to render the HTML inline and execute embedded JavaScript in the registry origin. The response body contains a <script> tag that accesses document.cookie and would execute in the browser.
  • Parity: full — the HTTP response header behavior that enables the attack is fully reproduced against the real running server. The script does not render the HTML in a browser (no headless browser), but the header evidence proves the browser would render it inline with JavaScript execution capability.
  • Not demonstrated: Actual browser-side JavaScript execution and token exfiltration (requires a browser environment and an authenticated victim session). The HTTP-level evidence is sufficient to prove the vulnerability.

Root Cause

The vulnerability exists in the file-serving code path of the Open VSX Registry server. When a user requests a file from a VSIX package via /vscode/unpkg/{namespace}/{extension}/{version}/{path}:

  1. LocalVSCodeService.browse() handles the request and calls getWebResource() to extract the requested file from the VSIX ZIP archive to a temporary path.

  2. The extracted file is served via StorageUtilService.getFileResponse(Path path), which sets response headers using StorageUtil.getFileType(fileName).

  3. The vulnerable code (StorageUtil.getFileType in v1.0.1):

    static MediaType getFileType(String fileName) {
        if (fileName.endsWith(".vsix")) return MediaType.APPLICATION_OCTET_STREAM;
        if (fileName.endsWith(".json")) return MediaType.APPLICATION_JSON;
        if (fileName.endsWith(".sigzip")) return MediaType.valueOf("application/zip");
        var contentType = URLConnection.guessContentTypeFromName(fileName);
        if (contentType != null) return MediaType.parseMediaType(contentType);
        return MediaType.TEXT_PLAIN;
    }
    

    For .html files, URLConnection.guessContentTypeFromName("payload.html") returns text/html.

  4. The vulnerable response headers (StorageUtilService.getFileResponse(Path) in v1.0.1):

    public ResponseEntity<StreamingResponseBody> getFileResponse(Path path) {
        var fileName = path.getFileName().toString();
        var headers = new HttpHeaders();
        headers.setContentType(StorageUtil.getFileType(fileName));  // → text/html
        headers.setCacheControl(StorageUtil.getCacheControl(fileName));
        return ResponseEntity.ok().headers(headers).body(...);
    }
    

    No Content-Security-Policy header is set. No Content-Disposition: attachment header is set for non-.vsix files. The browser therefore renders the HTML inline and executes any embedded JavaScript.

  5. The fix (v1.0.2) introduces HttpHeadersUtil.createFileResponseHeaders() which:

    • Uses Apache Tika to detect the actual MIME type from file content
    • For text-viewable types (including text/html), forces Content-Type: text/plain;charset=UTF-8 to prevent HTML rendering
    • Sets Content-Disposition: attachment for non-text files to force download
    • Always sets a strict Content-Security-Policy: default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; sandbox
    • Sets X-Content-Type-Options: nosniff and X-Frame-Options: DENY

Fix commit: The fix is included in tag v1.0.2 (commit 9491f32a6d459a4d499c5028d37c0d0386771e9f). The key changes are in:

  • server/src/main/java/org/eclipse/openvsx/util/HttpHeadersUtil.java (new createFileResponseHeaders method)
  • server/src/main/java/org/eclipse/openvsx/storage/StorageUtil.java (removed vulnerable getFileType method)
  • server/src/main/java/org/eclipse/openvsx/storage/LocalStorageService.java (uses new header util)
  • server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java (uses new header util)

Reproduction Steps

  1. Reference: bundle/repro/reproduction_steps.sh
  2. What the script does:
    • Creates a minimal VSIX package containing extension/payload.html with a <script> tag that accesses document.cookie
    • Builds the Open VSX Registry server from source at v1.0.1 (vulnerable) and v1.0.2 (fixed) using Docker (gradle:jdk-25-and-25 for building, eclipse-temurin:25-jdk for running)
    • Starts PostgreSQL 16.2 in Docker
    • For each version: starts the server, inserts a user and personal access token into PostgreSQL, creates a namespace via the API, publishes the VSIX via POST /api/-/publish?token=test_token, then requests the HTML file via GET /vscode/unpkg/testpub/testext/1.0.0/extension/payload.html
    • Captures and compares the HTTP response headers between vulnerable and fixed versions
  3. Expected evidence:
    • Vulnerable (v1.0.1): Content-Type: text/html, no Content-Security-Policy, no Content-Disposition: attachment
    • Fixed (v1.0.2): Content-Type: text/plain;charset=utf-8, Content-Security-Policy: default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; sandbox

Evidence

Log file locations
  • bundle/logs/vuln_v1.0.1_headers.txt — HTTP response headers from vulnerable server
  • bundle/logs/vuln_v1.0.1_body.html — HTML payload served by vulnerable server
  • bundle/logs/fixed_v1.0.2_headers.txt — HTTP response headers from fixed server
  • bundle/logs/fixed_v1.0.2_body.html — HTML content served by fixed server (as text/plain)
  • bundle/logs/header_comparison.txt — Side-by-side header comparison
  • bundle/logs/reproduction_verdict.txt — Final verdict summary
  • bundle/logs/server_vuln_v1.0.1.log — Vulnerable server startup log
  • bundle/logs/server_fixed_v1.0.2.log — Fixed server startup log
  • bundle/logs/build_v1.0.1.log / bundle/logs/build_v1.0.2.log — Build logs
  • bundle/repro/runtime_manifest.json — Runtime evidence manifest
Key excerpts

Vulnerable (v1.0.1) response headers — serves HTML inline:

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: max-age=2592000, public
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
X-Frame-Options: DENY
  • Content-Type: text/html → browser renders HTML
  • No Content-Security-Policy → JavaScript executes freely
  • No Content-Disposition: attachment → file rendered inline, not downloaded

Fixed (v1.0.2) response headers — safe serving:

HTTP/1.1 200 OK
Content-Type: text/plain;charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Cache-Control: max-age=86400, must-revalidate, public
Content-Security-Policy: default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; sandbox
X-XSS-Protection: 0
  • Content-Type: text/plain;charset=utf-8 → browser displays as plain text
  • Content-Security-Policy: default-src 'none'; ... → blocks all script execution, network requests, framing
Environment details
  • Server: Open VSX Registry built from source (eclipse-openvsx/openvsx), Spring Boot with embedded Jetty
  • JDK: Eclipse Temurin 25 (JDK 25)
  • Database: PostgreSQL 16.2
  • Build tool: Gradle 9.5.1
  • Docker: Used for all components (build, database, runtime)

Recommendations / Next Steps

  1. Upgrade to v1.0.2 or later — The fix adds comprehensive response header hardening:

    • Content-type sniffing via Apache Tika (not just file extension)
    • Forced text/plain for all text-viewable types (HTML, CSS, JS, Markdown)
    • Strict CSP on all served files
    • Content-Disposition: attachment for non-text files
    • X-Content-Type-Options: nosniff and X-Frame-Options: DENY
  2. Audit existing extensions — Check for previously published extensions containing HTML files that may have been used for exploitation.

  3. Consider additional mitigations:

    • Add Content-Disposition: attachment even for text-viewable files to force download
    • Implement a content allowlist for file types that can be served via /vscode/unpkg/
    • Consider serving user-uploaded content from a separate origin (e.g., files.open-vsx.org) to isolate from the registry origin
  4. Testing recommendation — Add integration tests that verify response headers for various file types (.html, .svg, .js, .css) served via the /vscode/unpkg/ endpoint.

Additional Notes

  • Idempotency: The reproduction script is fully self-contained and idempotent. It creates and tears down all Docker containers. Running it again produces the same results. The script cleans up all containers on exit via a trap handler.
  • Negative control: The script tests both the vulnerable (v1.0.1) and fixed (v1.0.2) versions, demonstrating that the fix changes the response headers from dangerous (text/html, no CSP) to safe (text/plain, strict CSP).
  • Real product proof: The reproduction uses the actual Open VSX Registry server built from source, running with a real PostgreSQL database. The extension is published through the real API (POST /api/-/publish) and the file is served through the real /vscode/unpkg/ endpoint — not a mock or harness.
  • Scanning disabled: The test config disables extension scanning (ovsx.scanning.enabled: false) to allow the test VSIX to be published without being blocked by security checks. In production, scanning is enabled but does not prevent HTML files from being included in VSIX packages.

CVE-2026-4983 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:27
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-13323 · REPRO-20
0:03
0:05
web search
0:06
web search
0:09
0:10
web search
0:12
web search
0:14
0:16
web search
0:17
0:21
0:22
0:23
web search
0:26
0:27
web search
0:28
0:33
0:34
0:35
web search
0:44
0:46
0:47
web search
0:59
1:00
web search
1:28
1:30
1:49
1:50
web search
1:57
2:27
08 · How to Fix

How to Fix CVE-2026-4983

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-4983 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-4983

How does the CVE-2026-4983 stored XSS attack work?

An attacker registers a publisher account, uploads a VSIX package containing a crafted HTML or SVG file with embedded JavaScript, and induces an authenticated user to visit the resulting /vscode/unpkg/ URL. Because the response lacks CSP and attachment headers, the browser renders the payload inline in the open-vsx.org origin context, allowing the script to exfiltrate session cookies/tokens, generate persistent Personal Access Tokens, and publish malicious extension versions under the victim's identity.

How severe is CVE-2026-4983?

It is rated medium severity. The impact includes session/token exfiltration, persistent PAT generation, and unauthorized publication of malicious extension versions, which also poses a supply-chain risk since Open VSX extensions are distributed to VS Code, VSCodium, Cursor, Windsurf, and compatible editors.

How can I reproduce CVE-2026-4983?

Download the verified script from this page and run it in an isolated environment against an affected Open VSX Registry instance (versions before 1.0.2). It registers a publisher, uploads a VSIX containing a crafted HTML payload, requests the resulting /vscode/unpkg/ URL, and shows the response served as inline text/html without a CSP or attachment header, then confirms the fixed version serves it safely.
11 · References

References for CVE-2026-4983

Authoritative sources for CVE-2026-4983 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.