|
|
@@ -111,7 +111,11 @@ export class WebviewController {
|
|
|
const uiUrlWithMode = this.buildUiUrlWithMode(externalUi.toString())
|
|
|
const iframeSrc = `${uiUrlWithMode}&ideBridge=${encodeURIComponent(externalBridge.toString())}&ideBridgeToken=${encodeURIComponent(session.token)}`
|
|
|
|
|
|
- const html = await this.generateHtmlContent(iframeSrc)
|
|
|
+ // Extract origins for dynamic CSP (Remote-SSH compatibility)
|
|
|
+ const uiOrigin = new URL(externalUi.toString()).origin
|
|
|
+ const bridgeOrigin = new URL(externalBridge.toString()).origin
|
|
|
+
|
|
|
+ const html = await this.generateHtmlContent(iframeSrc, { uiOrigin, bridgeOrigin })
|
|
|
this.webview.html = html
|
|
|
|
|
|
// Message handling is now done entirely by CommunicationBridge
|
|
|
@@ -226,14 +230,50 @@ export class WebviewController {
|
|
|
return base.includes("?") ? `${base}&mode=${uiMode}` : `${base}?mode=${uiMode}`
|
|
|
}
|
|
|
|
|
|
- private async generateHtmlContent(uiUrl: string): Promise<string> {
|
|
|
+ private async generateHtmlContent(
|
|
|
+ uiUrl: string,
|
|
|
+ origins: { uiOrigin: string; bridgeOrigin: string },
|
|
|
+ ): Promise<string> {
|
|
|
const htmlUri = vscode.Uri.joinPath(this.context.extensionUri, "resources", "webview", "index.html")
|
|
|
const bytes = await vscode.workspace.fs.readFile(htmlUri)
|
|
|
let html = Buffer.from(bytes).toString("utf8")
|
|
|
- html = html.replace(/\$\{uiUrl\}/g, uiUrl).replace(/\$\{cspSource\}/g, this.webview.cspSource)
|
|
|
+
|
|
|
+ // Build dynamic CSP origins - include both specific origins and localhost fallbacks
|
|
|
+ const cspOrigins = this.buildCspOrigins(origins.uiOrigin, origins.bridgeOrigin)
|
|
|
+
|
|
|
+ html = html
|
|
|
+ .replace(/\$\{uiUrl\}/g, uiUrl)
|
|
|
+ .replace(/\$\{cspSource\}/g, this.webview.cspSource)
|
|
|
+ .replace(/\$\{cspOrigins\}/g, cspOrigins)
|
|
|
+
|
|
|
return html
|
|
|
}
|
|
|
|
|
|
+ private buildCspOrigins(uiOrigin: string, bridgeOrigin: string): string {
|
|
|
+ // Collect unique origins, always include localhost fallbacks for compatibility
|
|
|
+ const origins = new Set<string>([
|
|
|
+ "http://127.0.0.1:*",
|
|
|
+ "https://127.0.0.1:*",
|
|
|
+ "http://localhost:*",
|
|
|
+ "https://localhost:*",
|
|
|
+ ])
|
|
|
+
|
|
|
+ // Add the actual resolved origins (handles Remote-SSH tunnels, codespaces, etc.)
|
|
|
+ for (const origin of [uiOrigin, bridgeOrigin]) {
|
|
|
+ try {
|
|
|
+ const url = new URL(origin)
|
|
|
+ // Add with wildcard port for flexibility
|
|
|
+ origins.add(`${url.protocol}//${url.hostname}:*`)
|
|
|
+ // Also add the exact origin
|
|
|
+ origins.add(origin)
|
|
|
+ } catch {
|
|
|
+ // Skip invalid origins
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Array.from(origins).join(" ")
|
|
|
+ }
|
|
|
+
|
|
|
private normalizePath(rawPath: string): string | null {
|
|
|
try {
|
|
|
if (!rawPath || rawPath.trim().length === 0) return null
|