Just by clicking a link, it’s possible for an attacker to steal a GitHub token that can read and write to your repos, including private ones . Table of Contents Background VSCode Webview Security Model The Bug PoC and Protecting Yourself What VSCode Did Well Why Full Disclosure Timeline Background Did you know GitHub has this really cool feature called github.dev ? On any repository you have access to, if you can change the url from github.com to github.dev or you click this little menu item: You’ll be launched into a little light-weight version of VSCode that runs entirely in your browser (I guess that’s one advantage of having your app written with electron). This browser instance of VSCode is pretty powerful, you can view all the files in the repo (even if it’s a private one), you can send out pull requests and even make commits. This functionality is achieved by github.com POSTing over an OAuth token to github.dev that allows it to interact with GitHub on your behalf. The token is not scoped to the particular repo you interacted with , meaning it has full access to every other repo that you have access to. The presence of this token and the fact that this web-app is running almost the entire brunt of VSCode’s million line Typescript codebase makes it a great target for anyone looking into VSCode bugs. That sort of bug is what we’ll explore here and show how an attacker can use it to exfiltrate your GitHub token. VSCode Webview Security Model Being an electron app on the desktop, executing arbitrary Javascript inside of VSCode would be tantamount to full remote code execution. This is why VSCode implements some sandboxing approaches, the one we’ll focus on here is VSCode’s webviews . Webviews use an <iframe> with a different origin to the main VSCode window to ensure that any JavaScript executed inside of them is fully isolated. These webviews are used for features such as Markdown previews or editing Jupyter notebooks: The output of the cell is rendered into an <iframe> from the origin vscode-webview://... , as opposed to the main electron window which has the origin vscode-file://... . This means that even if the Jupyter notebook uses the built-in features of displaying HTML or using Javascript for interactive widgets , the actual core VScode application is protected from it. One cannot use Electron’s integration with Node.js APIs inside this iframe or call into VSCode’s APIs from this frame. Great, that gives us the ability to render content, but just static content is boring. How do we implement features like having the Markdown preview show you which source line you currently have highlighted or updating the preview live as we edit it? The same cross-origin policy that gives us security also prevents our main editor window from interacting with the DOM in the vscode-webview://... frame. After all, you wouldn’t want someone who used an <iframe src="google.com"> to be able to interact with the google page to steal your cookies or change that website’s behavior. > document.getElementsByTagName('iframe')[0].contentWindow.findElementById('foo') Uncaught SecurityError: Failed to read a named property 'findElementById' from 'Window': Blocked a frame with origin "vscode-file://vscode-app" from accessing a cross-origin frame. The only way to allow this behavior is to have the two web pages in the different origins cooperate with each other using the Window.postMessage() API . This method allows sending JavaScript objects across the different windows. So in that example of showing which rendered Markdown line corresponds to what editor line, the main editor window posts a little message like this: { type : " onDidChangeTextEditorSelection " , line : 31 } and then the corresponding code running inside of the webview has a listener for this message that adds the highlight : window . addEventListener ( ' message ' , async event => { const data = event . data as ToWebviewMessage . Type ; switch ( data . type ) { ... case ' onDidChangeTextEditorSelection ' : marker . onDidChangeTextEditorSelection ( data . line , documentVersion ); return ; Note : VSCode in the browser uses a similar sandboxing model. VSCode developer Matt Bierner has a great blogpost about the challenges of porting it over from Electron worth checking out . The Bug So our security boundary for webviews roughly looks like this: but in terms of UI, our webview sits right here in the window. People expect basic things like clicking links, drag and or pressing Ctrl + F to work inside of them: Hence, VSCode implements a bunch of basic functionality through the message passing mechanism to enable these features. Speaking of keyboard shortcuts, the astute reader who has dealt with <iframe> s may have already picked up on the issue. As with most things cross-origin, the browser offers a good amount of isolation between the two frames. If you had a page on hackerman.com and you iframed google.com/login , you would not want the hackerman page to be able to...
A vulnerability in VSCode's webview sandbox allowed an attacker to steal a user's full-scope GitHub OAuth token via a malicious link, potentially granting read/write access to all of the user's repositories. The attack vector exploited a flaw in the origin isolation between the main VSCode window and webview iframes, enabling token exfiltration. The article does not provide specific affected or fixed version numbers, a CVSS score, or a recommended workaround.