mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Fix VSCode iframe SameSite cookie issue with cross-origin fallback (#8881)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
0fd83ff38a
commit
7652ccb000
@ -47,6 +47,7 @@ const SCAN_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
||||
|
||||
// Attributes that typically don't contain user-facing text
|
||||
const NON_TEXT_ATTRIBUTES = [
|
||||
"allow",
|
||||
"className",
|
||||
"i18nKey",
|
||||
"testId",
|
||||
@ -69,6 +70,7 @@ const NON_TEXT_ATTRIBUTES = [
|
||||
"aria-describedby",
|
||||
"aria-hidden",
|
||||
"role",
|
||||
"sandbox",
|
||||
];
|
||||
|
||||
function shouldIgnorePath(filePath) {
|
||||
@ -114,6 +116,7 @@ const EXCLUDED_TECHNICAL_STRINGS = [
|
||||
"add-secret-form", // Test ID for secret form
|
||||
"edit-secret-form", // Test ID for secret form
|
||||
"search-api-key-input", // Input name for search API key
|
||||
"noopener,noreferrer", // Options for window.open
|
||||
];
|
||||
|
||||
function isExcludedTechnicalString(str) {
|
||||
|
||||
@ -138,7 +138,9 @@ export enum I18nKey {
|
||||
VSCODE$LOADING = "VSCODE$LOADING",
|
||||
VSCODE$URL_NOT_AVAILABLE = "VSCODE$URL_NOT_AVAILABLE",
|
||||
VSCODE$FETCH_ERROR = "VSCODE$FETCH_ERROR",
|
||||
VSCODE$IFRAME_PERMISSIONS = "VSCODE$IFRAME_PERMISSIONS",
|
||||
VSCODE$CROSS_ORIGIN_WARNING = "VSCODE$CROSS_ORIGIN_WARNING",
|
||||
VSCODE$URL_PARSE_ERROR = "VSCODE$URL_PARSE_ERROR",
|
||||
VSCODE$OPEN_IN_NEW_TAB = "VSCODE$OPEN_IN_NEW_TAB",
|
||||
INCREASE_TEST_COVERAGE = "INCREASE_TEST_COVERAGE",
|
||||
AUTO_MERGE_PRS = "AUTO_MERGE_PRS",
|
||||
FIX_README = "FIX_README",
|
||||
|
||||
@ -2207,21 +2207,53 @@
|
||||
"tr": "VS Code URL'si alınamadı",
|
||||
"uk": "Не вдалося отримати VS Code URL"
|
||||
},
|
||||
"VSCODE$IFRAME_PERMISSIONS": {
|
||||
"en": "clipboard-read; clipboard-write",
|
||||
"ja": "clipboard-read; clipboard-write",
|
||||
"zh-CN": "clipboard-read; clipboard-write",
|
||||
"zh-TW": "clipboard-read; clipboard-write",
|
||||
"ko-KR": "clipboard-read; clipboard-write",
|
||||
"de": "clipboard-read; clipboard-write",
|
||||
"no": "clipboard-read; clipboard-write",
|
||||
"it": "clipboard-read; clipboard-write",
|
||||
"pt": "clipboard-read; clipboard-write",
|
||||
"es": "clipboard-read; clipboard-write",
|
||||
"ar": "clipboard-read; clipboard-write",
|
||||
"fr": "clipboard-read; clipboard-write",
|
||||
"tr": "clipboard-read; clipboard-write",
|
||||
"uk": "clipboard-read; clipboard-write"
|
||||
"VSCODE$CROSS_ORIGIN_WARNING": {
|
||||
"en": "The code editor cannot be embedded due to browser security restrictions. Cross-origin cookies are being blocked.",
|
||||
"ja": "ブラウザのセキュリティ制限により、コードエディタを埋め込むことができません。クロスオリジンCookieがブロックされています。",
|
||||
"zh-CN": "由于浏览器安全限制,无法嵌入代码编辑器。跨源Cookie被阻止。",
|
||||
"zh-TW": "由於瀏覽器安全限制,無法嵌入代碼編輯器。跨源Cookie被阻止。",
|
||||
"ko-KR": "브라우저 보안 제한으로 인해 코드 편집기를 삽입할 수 없습니다. 교차 출처 쿠키가 차단되고 있습니다.",
|
||||
"de": "Der Code-Editor kann aufgrund von Browser-Sicherheitsbeschränkungen nicht eingebettet werden. Cross-Origin-Cookies werden blockiert.",
|
||||
"no": "Koderedigereren kan ikke bygges inn på grunn av nettleserens sikkerhetsbegrensninger. Cross-origin cookies blir blokkert.",
|
||||
"it": "L'editor di codice non può essere incorporato a causa delle restrizioni di sicurezza del browser. I cookie cross-origin vengono bloccati.",
|
||||
"pt": "O editor de código não pode ser incorporado devido a restrições de segurança do navegador. Cookies de origem cruzada estão sendo bloqueados.",
|
||||
"es": "El editor de código no se puede incrustar debido a las restricciones de seguridad del navegador. Las cookies de origen cruzado están siendo bloqueadas.",
|
||||
"ar": "لا يمكن تضمين محرر التعليمات البرمجية بسبب قيود أمان المتصفح. يتم حظر ملفات تعريف الارتباط عبر المصدر.",
|
||||
"fr": "L'éditeur de code ne peut pas être intégré en raison des restrictions de sécurité du navigateur. Les cookies cross-origin sont bloqués.",
|
||||
"tr": "Tarayıcı güvenlik kısıtlamaları nedeniyle kod düzenleyici yerleştirilemiyor. Çapraz kaynaklı çerezler engelleniyor.",
|
||||
"uk": "Редактор коду не може бути вбудований через обмеження безпеки браузера. Блокуються файли cookie з різних джерел."
|
||||
},
|
||||
"VSCODE$URL_PARSE_ERROR": {
|
||||
"en": "Error parsing URL",
|
||||
"ja": "URLの解析エラー",
|
||||
"zh-CN": "URL解析错误",
|
||||
"zh-TW": "URL解析錯誤",
|
||||
"ko-KR": "URL 구문 분석 오류",
|
||||
"de": "Fehler beim Parsen der URL",
|
||||
"no": "Feil ved parsing av URL",
|
||||
"it": "Errore durante l'analisi dell'URL",
|
||||
"pt": "Erro ao analisar URL",
|
||||
"es": "Error al analizar URL",
|
||||
"ar": "خطأ في تحليل عنوان URL",
|
||||
"fr": "Erreur d'analyse de l'URL",
|
||||
"tr": "URL ayrıştırma hatası",
|
||||
"uk": "Помилка аналізу URL"
|
||||
},
|
||||
"VSCODE$OPEN_IN_NEW_TAB": {
|
||||
"en": "Open in New Tab",
|
||||
"ja": "新しいタブで開く",
|
||||
"zh-CN": "在新标签页中打开",
|
||||
"zh-TW": "在新標籤頁中打開",
|
||||
"ko-KR": "새 탭에서 열기",
|
||||
"de": "In neuem Tab öffnen",
|
||||
"no": "Åpne i ny fane",
|
||||
"it": "Apri in una nuova scheda",
|
||||
"pt": "Abrir em nova aba",
|
||||
"es": "Abrir en nueva pestaña",
|
||||
"ar": "فتح في علامة تبويب جديدة",
|
||||
"fr": "Ouvrir dans un nouvel onglet",
|
||||
"tr": "Yeni Sekmede Aç",
|
||||
"uk": "Відкрити в новій вкладці"
|
||||
},
|
||||
"INCREASE_TEST_COVERAGE": {
|
||||
"en": "Increase test coverage",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSelector } from "react-redux";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
@ -12,6 +12,29 @@ function VSCodeTab() {
|
||||
const { curAgentState } = useSelector((state: RootState) => state.agent);
|
||||
const isRuntimeInactive = RUNTIME_INACTIVE_STATES.includes(curAgentState);
|
||||
const iframeRef = React.useRef<HTMLIFrameElement>(null);
|
||||
const [isCrossProtocol, setIsCrossProtocol] = useState(false);
|
||||
const [iframeError, setIframeError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.url) {
|
||||
try {
|
||||
const iframeProtocol = new URL(data.url).protocol;
|
||||
const currentProtocol = window.location.protocol;
|
||||
|
||||
// Check if the iframe URL has a different protocol than the current page
|
||||
setIsCrossProtocol(iframeProtocol !== currentProtocol);
|
||||
} catch (e) {
|
||||
// Silently handle URL parsing errors
|
||||
setIframeError(t("VSCODE$URL_PARSE_ERROR"));
|
||||
}
|
||||
}
|
||||
}, [data?.url]);
|
||||
|
||||
const handleOpenInNewTab = () => {
|
||||
if (data?.url) {
|
||||
window.open(data.url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
};
|
||||
|
||||
if (isRuntimeInactive) {
|
||||
return (
|
||||
@ -29,14 +52,36 @@ function VSCodeTab() {
|
||||
);
|
||||
}
|
||||
|
||||
if (error || (data && data.error) || !data?.url) {
|
||||
if (error || (data && data.error) || !data?.url || iframeError) {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center text-center justify-center text-2xl text-tertiary-light">
|
||||
{data?.error || String(error) || t(I18nKey.VSCODE$URL_NOT_AVAILABLE)}
|
||||
{iframeError ||
|
||||
data?.error ||
|
||||
String(error) ||
|
||||
t(I18nKey.VSCODE$URL_NOT_AVAILABLE)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If cross-origin, show a button to open in new tab
|
||||
if (isCrossProtocol) {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center gap-4">
|
||||
<div className="text-xl text-tertiary-light text-center max-w-md">
|
||||
{t("VSCODE$CROSS_ORIGIN_WARNING")}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleOpenInNewTab}
|
||||
className="px-4 py-2 bg-primary text-white rounded hover:bg-primary-dark transition-colors"
|
||||
>
|
||||
{t("VSCODE$OPEN_IN_NEW_TAB")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If same origin, use the iframe
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<iframe
|
||||
@ -44,7 +89,7 @@ function VSCodeTab() {
|
||||
title={t(I18nKey.VSCODE$TITLE)}
|
||||
src={data.url}
|
||||
className="w-full h-full border-0"
|
||||
allow={t(I18nKey.VSCODE$IFRAME_PERMISSIONS)}
|
||||
allow="clipboard-read; clipboard-write"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user