Secure MCP SSE server against unauthenticated network access - #18
Open
jortizga wants to merge 1 commit into
Open
Secure MCP SSE server against unauthenticated network access#18jortizga wants to merge 1 commit into
jortizga wants to merge 1 commit into
Conversation
The MCP SSE server bound to all interfaces with no authentication, origin check, or CORS policy, allowing any local process, LAN host, or malicious webpage (via DNS rebinding) to enumerate tools and query the EDK2 workspace. - Bind the HTTP server to 127.0.0.1 instead of the unspecified address. - Reject requests whose Host/Origin headers are not loopback, which defeats DNS rebinding. - Require an 'Authorization: Bearer <token>' header on /sse and /messages, using a 32-byte random token persisted in VS Code SecretStorage and compared with timingSafeEqual. - Offer a 'Copy Access Token' action when the server starts. - Auto configuration now writes .vscode/mcp.json with the loopback URL and an Authorization header sourced from a password input, so the token is not stored on disk.
There was a problem hiding this comment.
Pull request overview
This PR hardens the extension’s MCP SSE server to prevent unauthenticated or cross-origin/DNS-rebinding access to the indexed EDK2 workspace by enforcing loopback-only access and adding request validation + bearer-token authentication.
Changes:
- Bind the MCP SSE HTTP server to
127.0.0.1and validateHost/Originheaders before handling requests. - Require
Authorization: Bearer <token>for/sseand/messages, with the token generated and persisted via VS CodeSecretStorage. - Update Settings UI auto-configuration to write a loopback URL and prompt-based Authorization header, plus update 2.0.0 release notes.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/mcp/mcpServer.ts | Adds loopback binding, Host/Origin validation, and bearer-token auth backed by SecretStorage. |
| src/settings/settingsPanel.ts | Updates MCP auto-config to use 127.0.0.1 and configure an Authorization header via an input prompt; copies token to clipboard. |
| src/newVersionPage/2.0.0.md | Documents the new loopback-only + header-validation + bearer-token requirements. |
Comments suppressed due to low confidence (1)
src/settings/settingsPanel.ts:299
autoConfigureMcp()no longer short-circuits when.vscode/mcp.jsonis already configured correctly. This will unnecessarily rewrite the JSON (potentially causing spurious diffs/reformatting) and will also overwrite any user customizations underservers.edk2codeeven when already valid.
if (fs.existsSync(mcpConfigPath)) {
try {
const existing = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf-8'));
// Add or update the edk2code entry
if (!existing.servers) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+102
to
+106
| const secrets = gExtensionContext?.secrets; | ||
| if (!secrets) { | ||
| // No storage available: fall back to an ephemeral token. | ||
| return serverToken ?? crypto.randomBytes(32).toString('hex'); | ||
| } |
Comment on lines
6
to
+10
| import { Disposable, Webview, WebviewPanel, window, Uri, ViewColumn } from "vscode"; | ||
| import { gExtensionContext } from '../extension'; | ||
| import { ConfigAgent, WorkspaceConfig, WorkspaceConfigErrors } from '../configuration'; | ||
| import { askReloadFiles } from '../ui/messages'; | ||
| import { isMcpServerRunning } from '../mcp/mcpServer'; | ||
| import { getOrCreateMcpToken, isMcpServerRunning } from '../mcp/mcpServer'; |
gapalomi
approved these changes
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The MCP SSE server (
src/mcp/mcpServer.ts) bound to all network interfaces with no authentication, no origin check and no CORS policy. Any local process, LAN host, or malicious webpage (via DNS rebinding) could connect, enumerate tools and query the indexed EDK2 workspace, exposing full firmware source paths, library/INF/DSC mappings and module layout.Changes
httpServer.listen(port, '127.0.0.1', ...)instead of omitting the host (which binds to0.0.0.0/::). Log and notification messages now reflect the real bind address.Hostmust resolve tolocalhost,127.0.0.1or::1(port and IPv6 brackets stripped), and when anOriginheader is present its hostname must also be loopback. Otherwise the request is rejected with403. This is what defeats DNS rebinding, since the browser keeps sending the attacker-controlled host name./sseand/messagesrequireAuthorization: Bearer <token>. The token is 32 random bytes, persisted in VS CodeSecretStorage(so clients only need to be configured once) and compared withcrypto.timingSafeEqual. Missing or invalid tokens get401withWWW-Authenticate: Bearer./healthremains unauthenticated but is still restricted to loopback requests.Supporting changes
.vscode/mcp.jsonusing the loopback URL and anAuthorizationheader sourced from apromptString/passwordinput, so the token is never written to disk; the token is copied to the clipboard for the user to paste when VS Code prompts.Testing
127.0.0.1:<port>only, unauthenticatedcurlto/sseshould return401, and a request with a spoofedHostheader should return403.