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.
This isn't a hypothetical vulnerability or a "theoretical risk" scenario. Security researcher Ammar Askar published a working proof-of-concept on June 2, 2026 that demonstrates the attack against the browser-based version of VSCode. The story hit the front page of Hacker News with over 600 points, and for good reason — it affects anyone who uses GitHub and VSCode.
I use VSCode every day for writing code, editing this blog's Ghost theme, and managing my homelab scripts. So when I saw this, I stopped what I was doing and checked my own setup. Here's what I found, how this bug works in plain terms, and what you need to do to protect yourself.
What's the Attack?
VSCode has a feature called webviews — embedded iframes that render things like Markdown previews and Jupyter notebook outputs. These webviews use a different browser origin (vscode-webview://) from the main editor window (vscode-file://) so that any JavaScript running inside them can't access the core VSCode process. That's the sandbox.
The problem is that webviews need keyboard shortcuts to work. When you press Ctrl+F to search inside a preview, that keypress needs to reach the iframe. VSCode solves this by bubbling up keydown events from the webview to the main window. JavaScript running inside the webview can listen for keypresses and forward them to the host.
Here's the catch — nothing stops a script running inside that webview from pretending to be the user pressing keys. Attackers can dispatch synthetic keyboard events that VSCode treats as real user input.
The Exploit Chain
The attack works against github.dev — the browser-based version of VSCode that GitHub launches when you change github.com to github.dev in a repo URL. GitHub POSTs a full OAuth token to github.dev that has read/write access to all repos you can access, not just the one you're viewing. That token is the prize.
Here's how an attacker chains the exploit together:
Step 1: The Bait
An attacker creates a GitHub repo containing a Jupyter notebook with a malicious payload. Jupyter notebooks rendered in VSCode webviews can execute arbitrary JavaScript using a common trick — an `tag with anonerror` handler:
`html
`
This executes inside the webview sandbox, which means it can't directly access Node.js APIs or VSCode internals. But it can dispatch keyboard events.
Step 2: Keydown Event Simulation
The attacker's JavaScript waits for VSCode to finish loading, then dispatches a synthetic keyboard event:
`javascript
window.dispatchEvent(
new KeyboardEvent("keydown", {
key: "a", code: "KeyA", keyCode: 65,
ctrlKey: true, shiftKey: true
})
);
`
The keyboard shortcut Ctrl+Shift+A maps to Notifications: Accept Notification Primary Action — a default VSCode keybinding that clicks the primary button on whatever notification is currently displayed.
Step 3: Extension Installation
The malicious repo includes a .vscode/extensions.json file that recommends a workspace extension:
`json
{
"recommendations": [
"HackerMan.evil-extension"
]
}
`
When VSCode opens the repo, it pops up a notification: "This workspace recommends extensions. Install?" The attacker's JavaScript accepts that notification via the Ctrl+Shift+A shortcut.
But there's a catch — newer versions of VSCode show a publisher trust dialog before installing extensions from unknown publishers. The exploit bypasses this by using a local workspace extension placed in .vscode/extensions/ inside the repo. Local extensions skip the publisher trust check if the workspace is trusted, which github.dev workspaces are by default.
Step 4: Custom Keybinding Escalation
The local workspace extension can't execute arbitrary code directly (Content Security Policy blocks it on the web version). But it can contribute custom keybindings to VSCode. The package.json adds a keybinding that calls workbench.extensions.installExtension with skipPublisherTrust: true:
`json
"contributes": {
"keybindings": [
{
"key": "ctrl+f1",
"command": "runCommands",
"args": {
"commands": [{
"command": "workbench.extensions.installExtension",
"args": [
"HackerMan.real-malicious-extension",
{
"donotSync": true,
"context": { "skipPublisherTrust": true }
}
]
}]
}
}
]
}
`
Step 5: Token Exfiltration
With the real malicious extension installed, the attacker now has full extension-level access inside github.dev. That extension reads the stored GitHub OAuth token and exfiltrates it — along with a list of all your private repos.
The whole chain happens automatically in about 10-15 seconds after you click the link.
Does Desktop VSCode Have the Same Bug?
The one-click attack mainly targeted github.dev, because the victim only had to open a crafted browser link.
Askar noted that related webview behaviour also exists in desktop VSCode, but Microsoft later stated that this specific issue does not affect VS Code Desktop. The practical risk is different: on desktop, an attacker would need a separate route, such as convincing someone to clone a repo and open a malicious notebook or workspace locally.
So the browser-based github.dev version is the bigger concern here. A malicious link is enough to start the chain; desktop exploitation would require more user interaction.
What Has Microsoft Done?
Microsoft responded quickly once Askar disclosed the bug publicly on June 2. According to reports from the VSCode issue tracker, by June 3 Microsoft had merged at least two fixes:
- Stopgap fix: Added a confirmation dialog when opening notebooks in the web version of VSCode. This gives users a chance to leave the page before any JavaScript executes.
-
Complete fix: Blocked the
skipPublisherTrustbypass for commands called via keybindings. Also prevented keydown event bubbling from notebook webviews.
The VSCode team's defense-in-depth approach — using CSP headers, script-src 'none' policies, and DOMPurify for Markdown rendering — limited the damage to webviews that can already execute JavaScript (like Jupyter notebook outputs). If a similar bug existed in the Markdown preview, the impact would have been far worse: one-click RCE on desktop from simply viewing an extension page.
How to Protect Yourself
Even with the fixes applied, it's worth locking down your setup. Here's what I did:
1. Clear github.dev Site Data
If you've ever used github.dev, clear your browser data for the domain. This removes any stored session tokens and forces a fresh authentication flow:
In Chrome:
- Click the padlock icon in the URL bar
- Go to Cookies and site data > Manage on-device site data
- Find
github.devand delete it
In Firefox:
- Right-click the page → View Page Info → Permissions → Clear Cookies and Site Data
2. Use Fine-Grained PATs Instead of Full Tokens
GitHub now recommends fine-grained PATs where possible, but classic tokens still exist and may still be needed for some workflows. If you have old classic tokens sitting around, revoke anything you don't actively use and replace broad access with fine-grained tokens scoped to specific repositories and permissions. This is the same kind of security hygiene I've covered in other posts about securing your online accounts — limit what's exposed and rotate what can't be limited.
`bash
Check what tokens the GitHub CLI has cached
gh auth status
Revoke a token via CLI
gh auth logout
`
3. Use gh CLI Instead of Token-Based Auth
The GitHub CLI stores OAuth tokens more securely than VSCode extensions do. Switch to using gh for authenticated API calls and git operations:
`bash
Authenticate via browser (safer)
gh auth login
Configure git to use gh as credential helper
gh auth setup-git
`
4. Audit Your VSCode Extensions
I wrote about why you might want to consider alternatives to VSCode in a previous post, but regardless of which editor you use, audit your extensions:
`bash
List all installed VSCode extensions
code --list-extensions
Check for any you don't recognize or haven't used recently
`
5. Enable Two-Factor Authentication on GitHub
This will not prevent token theft. A stolen OAuth token can still be used for whatever permissions it already has.
But 2FA still helps protect your account against password-based compromise and some account-level changes. Treat it as a safety layer, not a fix for stolen tokens.
The Bigger Picture
This bug is a reminder that VSCode's security model relies heavily on its webview sandbox — and that sandbox has cracks. The attack is creative in how it chains together seemingly unrelated features: keyboard shortcuts, workspace extensions, and publisher trust.
For developers, the takeaway is to treat repository links — especially those pointing to .dev or web-based editors — with the same wariness as executable files. A link to a repo is a link to code, and in this case, that code can act on your behalf.
Askar chose full disclosure rather than responsible disclosure because of a poor experience with Microsoft's Security Response Center (MSRC) on a previous VSCode bug. He argues that public disclosure is the only tool researchers have to push for better security practices. Whether you agree with that approach or not, the result is a bug that got fixed within 24 hours of going public — which is a lot faster than many responsibly-disclosed vulnerabilities I've seen.
References
- Original disclosure by Ammar Askar
- Proof-of-concept repo
- VSCode Issue Tracker
- Why Developers Should Consider Using Cursor AI Over VSCode — my previous post looking at VSCode alternatives


Top comments (0)