Harden extension against local network and webview attacks - #40
Conversation
Security hardening across six advisories: Webview (SA-001, SA-002): - Add Content-Security-Policy with per-render nonce to debugger webview - Fix postMessage bridge: gate iframe commands on event.source + event.origin check - Replace postMessage(data, '*') with postMessage(data, FRAME_ORIGIN) on host responses - Restrict openExternal to http/https schemes only; refuse other URI handlers Debugger server auth (SA-003): - Generate per-session token on DebuggerServerManager construction - Inject FASTEDGE_DEBUG_TOKEN and FASTEDGE_BIND_HOST=127.0.0.1 into forked server - Add x-fastedge-token header to all extension-side /api/* fetch calls - Deliver token to iframe via URL fragment (#token=...) for frontend auth Autorun trigger file (SA-004): - Skip execution in untrusted workspaces - Require explicit user confirmation before running any trigger command - Remove commandArgs forwarding from trigger file Port file trust (SA-006): - Gate .debug-port reuse on vscode.workspace.isTrusted; always spawn fresh in untrusted workspaces MCP server config (SA-005): - Mask API key input (password: true) - Pin Docker image via mcp-server.version file injected at build time by esbuild - chmod 0600 on written mcp.json for local file URIs Cleanup: - Delete src/dotenv/ — superseded by server-side dotenv handling in fastedge-test - Add MCP_INTEGRATION.md context doc covering version pinning mechanism - Add vitest.config.ts to mirror esbuild define for __MCP_SERVER_VERSION__ - Add tests: webview HTML security properties, trigger file guards, mcpJson image pin
There was a problem hiding this comment.
🟡 Changes recommended
The debugger server reuse path can select an existing server whose auth token won’t match the new per-instance token, which can break authenticated /api/* calls and iframe auth after extension host restarts.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR hardens the FastEdge VS Code extension against webview/message-bridge abuse, local-network debugger server exposure, and insecure MCP config handling by adding stricter isolation (CSP + origin gating), per-session auth tokens, and safer file/secret practices.
Changes:
- Webview hardening: per-render CSP nonce, stricter
postMessagebridge checks/target origins, andopenExternalrestricted tohttp/https. - Debugger server auth & trust: per-session token injected into the server process + added to extension
/api/*calls;.debug-portreuse gated by workspace trust. - MCP config hardening: pin MCP Docker image tag via build-time injected
__MCP_SERVER_VERSION__, mask API key input, and apply best-effortchmod 0600for localmcp.json; adds Vitest coverage and docs.
File summaries
| File | Description |
|---|---|
vitest.config.ts |
Injects __MCP_SERVER_VERSION__ into tests to mirror esbuild behavior. |
src/globals.d.ts |
Declares build-time injected constant for TypeScript. |
src/dotenv/index.ts |
Removes deprecated dotenv logic (superseded by server-side handling). |
src/debugger/DebuggerWebviewProvider.ts |
Adds CSP nonce, origin-gated message bridge, token propagation, and http/https-only openExternal. |
src/debugger/DebuggerWebviewProvider.test.ts |
Tests for key webview HTML security properties (CSP nonce, non-wildcard origins, token fragment). |
src/debugger/DebuggerServerManager.ts |
Adds per-instance token injection and trust-gated .debug-port reuse; sends token on /api/* calls. |
src/commands/mcpJson.ts |
Pins MCP Docker image version via injected constant; masks API key input; best-effort chmod 0600 on local mcp.json. |
src/commands/mcpJson.test.ts |
Verifies pinned MCP image tag (not :latest). |
src/autorun/triggerFileHandler.ts |
Exports trigger executor; blocks execution in untrusted workspaces; adds user confirmation; removes arg forwarding. |
src/autorun/triggerFileHandler.test.ts |
Tests untrusted-workspace guard, confirmation gating, and “no args forwarded” invariant. |
mcp-server.version |
Single source of truth for the pinned MCP server image tag. |
esbuild/build-ext.js |
Reads mcp-server.version and injects __MCP_SERVER_VERSION__ at build time. |
context/features/MCP_INTEGRATION.md |
Documents MCP generation and the version pinning/bump process. |
context/CONTEXT_INDEX.md |
Adds a task entry for bumping the pinned MCP server version. |
Review details
- Files reviewed: 13/14 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The trigger file handler decodes workspace.fs.readFile() via .toString(), which is not reliable UTF-8 decoding for a generic Uint8Array and can break command parsing.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 13/14 changed files
- Comments generated: 1
- Review effort level: Lite
| export async function executeTriggerFile( | ||
| uri: vscode.Uri, | ||
| outputChannel: vscode.OutputChannel, | ||
| ): Promise<void> { |
Security hardening across six advisories:
Webview (SA-001, SA-002):
Debugger server auth (SA-003):
Autorun trigger file (SA-004):
Port file trust (SA-006):
MCP server config (SA-005):
Cleanup: