From 54c8bd29ee354155391a7668cccf6ec6fd20fe22 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:48:38 +0800 Subject: [PATCH] security: Fix XSS vulnerability in authentication check-code pages (#23295) --- web/__tests__/xss-fix-verification.test.tsx | 212 ++++++++++++++++++ .../webapp-reset-password/check-code/page.tsx | 5 +- .../webapp-signin/check-code/page.tsx | 5 +- web/app/reset-password/check-code/page.tsx | 5 +- web/app/signin/check-code/page.tsx | 5 +- web/i18n/de-DE/login.ts | 2 +- web/i18n/en-US/login.ts | 2 +- web/i18n/es-ES/login.ts | 2 +- web/i18n/fa-IR/login.ts | 3 +- web/i18n/fr-FR/login.ts | 3 +- web/i18n/hi-IN/login.ts | 3 +- web/i18n/it-IT/login.ts | 3 +- web/i18n/ja-JP/login.ts | 2 +- web/i18n/ko-KR/login.ts | 2 +- web/i18n/pl-PL/login.ts | 3 +- web/i18n/pt-BR/login.ts | 3 +- web/i18n/ro-RO/login.ts | 3 +- web/i18n/ru-RU/login.ts | 3 +- web/i18n/sl-SI/login.ts | 3 +- web/i18n/th-TH/login.ts | 2 +- web/i18n/tr-TR/login.ts | 3 +- web/i18n/uk-UA/login.ts | 3 +- web/i18n/vi-VN/login.ts | 3 +- web/i18n/zh-Hans/login.ts | 2 +- web/i18n/zh-Hant/login.ts | 2 +- 25 files changed, 248 insertions(+), 36 deletions(-) create mode 100644 web/__tests__/xss-fix-verification.test.tsx diff --git a/web/__tests__/xss-fix-verification.test.tsx b/web/__tests__/xss-fix-verification.test.tsx new file mode 100644 index 000000000..2fa5ab3c0 --- /dev/null +++ b/web/__tests__/xss-fix-verification.test.tsx @@ -0,0 +1,212 @@ +/** + * XSS Fix Verification Test + * + * This test verifies that the XSS vulnerability in check-code pages has been + * properly fixed by replacing dangerouslySetInnerHTML with safe React rendering. + */ + +import React from 'react' +import { cleanup, render } from '@testing-library/react' +import '@testing-library/jest-dom' + +// Mock i18next with the new safe translation structure +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => { + if (key === 'login.checkCode.tipsPrefix') + return 'We send a verification code to ' + + return key + }, + }), +})) + +// Mock Next.js useSearchParams +jest.mock('next/navigation', () => ({ + useSearchParams: () => ({ + get: (key: string) => { + if (key === 'email') + return 'test@example.com' + return null + }, + }), +})) + +// Fixed CheckCode component implementation (current secure version) +const SecureCheckCodeComponent = ({ email }: { email: string }) => { + const { t } = require('react-i18next').useTranslation() + + return ( +
+

Check Code

+

+ + {t('login.checkCode.tipsPrefix')} + {email} + +

+
+ ) +} + +// Vulnerable implementation for comparison (what we fixed) +const VulnerableCheckCodeComponent = ({ email }: { email: string }) => { + const mockTranslation = (key: string, params?: any) => { + if (key === 'login.checkCode.tips' && params?.email) + return `We send a verification code to ${params.email}` + + return key + } + + return ( +
+

Check Code

+

+ +

+
+ ) +} + +describe('XSS Fix Verification - Check Code Pages Security', () => { + afterEach(() => { + cleanup() + }) + + const maliciousEmail = 'test@example.com' + + it('should securely render email with HTML characters as text (FIXED VERSION)', () => { + console.log('\n🔒 Security Fix Verification Report') + console.log('===================================') + + const { container } = render() + + const spanElement = container.querySelector('span') + const strongElement = container.querySelector('strong') + const scriptElements = container.querySelectorAll('script') + + console.log('\n✅ Fixed Implementation Results:') + console.log('- Email rendered in strong tag:', strongElement?.textContent) + console.log('- HTML tags visible as text:', strongElement?.textContent?.includes('', + 'normal@email.com', + ] + + testCases.forEach((testEmail, index) => { + const { container } = render() + + const strongElement = container.querySelector('strong') + const scriptElements = container.querySelectorAll('script') + const imgElements = container.querySelectorAll('img') + const divElements = container.querySelectorAll('div:not([data-testid])') + + console.log(`\n📧 Test Case ${index + 1}: ${testEmail.substring(0, 20)}...`) + console.log(` - Script elements: ${scriptElements.length}`) + console.log(` - Img elements: ${imgElements.length}`) + console.log(` - Malicious divs: ${divElements.length - 1}`) // -1 for container div + console.log(` - Text content: ${strongElement?.textContent === testEmail ? 'SAFE' : 'ISSUE'}`) + + // All should be safe + expect(scriptElements).toHaveLength(0) + expect(imgElements).toHaveLength(0) + expect(strongElement?.textContent).toBe(testEmail) + }) + + console.log('\n✅ All test cases passed - secure rendering confirmed') + }) + + it('should validate the translation structure is secure', () => { + console.log('\n🔍 Translation Security Analysis') + console.log('=================================') + + const { t } = require('react-i18next').useTranslation() + const prefix = t('login.checkCode.tipsPrefix') + + console.log('- Translation key used: login.checkCode.tipsPrefix') + console.log('- Translation value:', prefix) + console.log('- Contains HTML tags:', prefix.includes('<')) + console.log('- Pure text content:', !prefix.includes('<') && !prefix.includes('>')) + + // Verify translation is plain text + expect(prefix).toBe('We send a verification code to ') + expect(prefix).not.toContain('<') + expect(prefix).not.toContain('>') + expect(typeof prefix).toBe('string') + + console.log('\n✅ Translation structure is secure - no HTML content') + }) + + it('should confirm React automatic escaping works correctly', () => { + console.log('\n⚡ React Security Mechanism Test') + console.log('=================================') + + // Test React's automatic escaping with various inputs + const dangerousInputs = [ + '', + '', + '">', + '\'>alert(3)', + '
click
', + ] + + dangerousInputs.forEach((input, index) => { + const TestComponent = () => {input} + const { container } = render() + + const strongElement = container.querySelector('strong') + const scriptElements = container.querySelectorAll('script') + + console.log(`\n🧪 Input ${index + 1}: ${input.substring(0, 30)}...`) + console.log(` - Rendered as text: ${strongElement?.textContent === input}`) + console.log(` - No script execution: ${scriptElements.length === 0}`) + + expect(strongElement?.textContent).toBe(input) + expect(scriptElements).toHaveLength(0) + }) + + console.log('\n🛡️ React automatic escaping is working perfectly') + }) +}) + +export {} diff --git a/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx b/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx index da754794b..91e102161 100644 --- a/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx @@ -70,7 +70,10 @@ export default function CheckCode() {

{t('login.checkCode.checkYourEmail')}

- + + {t('login.checkCode.tipsPrefix')} + {email} +
{t('login.checkCode.validTime')}

diff --git a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx index a2ba620ac..c80a00658 100644 --- a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx @@ -93,7 +93,10 @@ export default function CheckCode() {

{t('login.checkCode.checkYourEmail')}

- + + {t('login.checkCode.tipsPrefix')} + {email} +
{t('login.checkCode.validTime')}

diff --git a/web/app/reset-password/check-code/page.tsx b/web/app/reset-password/check-code/page.tsx index 480b51311..a2dfda1e5 100644 --- a/web/app/reset-password/check-code/page.tsx +++ b/web/app/reset-password/check-code/page.tsx @@ -70,7 +70,10 @@ export default function CheckCode() {

{t('login.checkCode.checkYourEmail')}

- + + {t('login.checkCode.tipsPrefix')} + {email} +
{t('login.checkCode.validTime')}

diff --git a/web/app/signin/check-code/page.tsx b/web/app/signin/check-code/page.tsx index 912df62f1..9c3f7768f 100644 --- a/web/app/signin/check-code/page.tsx +++ b/web/app/signin/check-code/page.tsx @@ -71,7 +71,10 @@ export default function CheckCode() {

{t('login.checkCode.checkYourEmail')}

- + + {t('login.checkCode.tipsPrefix')} + {email} +
{t('login.checkCode.validTime')}

diff --git a/web/i18n/de-DE/login.ts b/web/i18n/de-DE/login.ts index 7ef0e2420..ef87f05f7 100644 --- a/web/i18n/de-DE/login.ts +++ b/web/i18n/de-DE/login.ts @@ -79,9 +79,9 @@ const translation = { useAnotherMethod: 'Verwenden Sie eine andere Methode', validTime: 'Beachten Sie, dass der Code 5 Minuten lang gültig ist', emptyCode: 'Code ist erforderlich', - tips: 'Wir senden einen Verifizierungscode an {{email}}', invalidCode: 'Ungültiger Code', resend: 'Wieder senden', + tipsPrefix: 'Wir senden einen Bestätigungscode an', }, or: 'ODER', back: 'Zurück', diff --git a/web/i18n/en-US/login.ts b/web/i18n/en-US/login.ts index d47eb7c07..a00e73b90 100644 --- a/web/i18n/en-US/login.ts +++ b/web/i18n/en-US/login.ts @@ -79,7 +79,7 @@ const translation = { validate: 'Validate', checkCode: { checkYourEmail: 'Check your email', - tips: 'We send a verification code to {{email}}', + tipsPrefix: 'We send a verification code to ', validTime: 'Bear in mind that the code is valid for 5 minutes', verificationCode: 'Verification code', verificationCodePlaceholder: 'Enter 6-digit code', diff --git a/web/i18n/es-ES/login.ts b/web/i18n/es-ES/login.ts index fda14f370..8fd82ecb8 100644 --- a/web/i18n/es-ES/login.ts +++ b/web/i18n/es-ES/login.ts @@ -78,10 +78,10 @@ const translation = { emptyCode: 'Se requiere código', useAnotherMethod: 'Usar otro método', resend: 'Reenviar', - tips: 'Enviamos un código de verificación a {{email}}', verificationCode: 'Código de verificación', validTime: 'Ten en cuenta que el código es válido durante 5 minutos', invalidCode: 'Código no válido', + tipsPrefix: 'Enviamos un código de verificación a', }, or: 'O', back: 'Atrás', diff --git a/web/i18n/fa-IR/login.ts b/web/i18n/fa-IR/login.ts index da2e5197e..2b6098b95 100644 --- a/web/i18n/fa-IR/login.ts +++ b/web/i18n/fa-IR/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'نام کاربری شما', forget: 'رمز عبور خود را فراموش کرده‌اید؟', signBtn: 'ورود', - sso: 'ادامه با SSO', installBtn: 'راه‌اندازی', setAdminAccount: 'راه‌اندازی حساب مدیر', setAdminAccountDesc: 'بیشترین امتیازات برای حساب مدیر، که می‌تواند برای ایجاد برنامه‌ها و مدیریت ارائه‌دهندگان LLM و غیره استفاده شود.', @@ -81,8 +80,8 @@ const translation = { useAnotherMethod: 'از روش دیگری استفاده کنید', checkYourEmail: 'ایمیل خود را بررسی کنید', validTime: 'به خاطر داشته باشید که کد 5 دقیقه اعتبار دارد', - tips: 'کد درستی سنجی را به {{email}} ارسال می کنیم', resend: 'ارسال مجدد', + tipsPrefix: 'ما یک کد تأیید می‌فرستیم به ', }, or: 'یا', back: 'بازگشت', diff --git a/web/i18n/fr-FR/login.ts b/web/i18n/fr-FR/login.ts index 9e718cad2..38b815915 100644 --- a/web/i18n/fr-FR/login.ts +++ b/web/i18n/fr-FR/login.ts @@ -70,7 +70,6 @@ const translation = { activated: 'Connectez-vous maintenant', adminInitPassword: 'Mot de passe d\'initialisation de l\'administrateur', validate: 'Valider', - sso: 'Poursuivre avec l’authentification unique', checkCode: { verificationCode: 'Code de vérification', useAnotherMethod: 'Utiliser une autre méthode', @@ -82,7 +81,7 @@ const translation = { invalidCode: 'Code non valide', checkYourEmail: 'Vérifiez vos e-mails', validTime: 'Gardez à l’esprit que le code est valable 5 minutes', - tips: 'Nous envoyons un code de vérification à {{email}}', + tipsPrefix: 'Nous envoyons un code de vérification à', }, sendVerificationCode: 'Envoyer le code de vérification', or: 'OU', diff --git a/web/i18n/hi-IN/login.ts b/web/i18n/hi-IN/login.ts index 06019042b..e89cea327 100644 --- a/web/i18n/hi-IN/login.ts +++ b/web/i18n/hi-IN/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'आपका उपयोगकर्ता नाम', forget: 'क्या आप पासवर्ड भूल गए?', signBtn: 'साइन इन करें', - sso: 'SSO के साथ जारी रखें', installBtn: 'सेट अप करें', setAdminAccount: 'एडमिन खाता सेट कर रहे हैं', setAdminAccountDesc: @@ -86,8 +85,8 @@ const translation = { resend: 'भेजें', checkYourEmail: 'अपना ईमेल जांचें', validTime: 'ध्यान रखें कि कोड 5 मिनट के लिए वैध है', - tips: 'हम {{email}} को एक सत्यापन कोड भेजते हैं', verificationCodePlaceholder: '6-अंक कोड दर्ज करें', + tipsPrefix: 'हम एक सत्यापन कोड भेजते हैं', }, sendVerificationCode: 'पुष्टि कोड भेजें', or: 'नहीं तो', diff --git a/web/i18n/it-IT/login.ts b/web/i18n/it-IT/login.ts index 47ae79bdd..5009f9951 100644 --- a/web/i18n/it-IT/login.ts +++ b/web/i18n/it-IT/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Il tuo nome utente', forget: 'Hai dimenticato la password?', signBtn: 'Accedi', - sso: 'Continua con SSO', installBtn: 'Configura', setAdminAccount: 'Impostazione di un account amministratore', setAdminAccountDesc: @@ -91,8 +90,8 @@ const translation = { validTime: 'Tieni presente che il codice è valido per 5 minuti', didNotReceiveCode: 'Non hai ricevuto il codice?', checkYourEmail: 'Controlla la tua email', - tips: 'Inviamo un codice di verifica a {{email}}', useAnotherMethod: 'Usa un altro metodo', + tipsPrefix: 'Inviamo un codice di verifica a', }, or: 'O', back: 'Indietro', diff --git a/web/i18n/ja-JP/login.ts b/web/i18n/ja-JP/login.ts index 833bedf71..7c116c4c1 100644 --- a/web/i18n/ja-JP/login.ts +++ b/web/i18n/ja-JP/login.ts @@ -78,10 +78,10 @@ const translation = { didNotReceiveCode: 'コードが届きませんか?', resend: '再送', verificationCode: '認証コード', - tips: '確認コードを{{email}}に送信します。', validTime: 'コードは 5 分間有効であることに注意してください', emptyCode: 'コードが必要です', checkYourEmail: 'メールをチェックしてください', + tipsPrefix: '私たちは確認コードを送信します', }, useVerificationCode: '確認コードを使用する', or: '又は', diff --git a/web/i18n/ko-KR/login.ts b/web/i18n/ko-KR/login.ts index 51b68967c..b050d4b9f 100644 --- a/web/i18n/ko-KR/login.ts +++ b/web/i18n/ko-KR/login.ts @@ -73,7 +73,6 @@ const translation = { checkCode: { verify: '확인', verificationCode: '인증 코드', - tips: '{{email}}로 인증 코드를 보내드립니다.', validTime: '코드는 5 분 동안 유효합니다', checkYourEmail: '이메일 주소 확인', invalidCode: '유효하지 않은 코드', @@ -82,6 +81,7 @@ const translation = { useAnotherMethod: '다른 방법 사용', didNotReceiveCode: '코드를 받지 못하셨나요?', resend: '재전송', + tipsPrefix: '우리는 확인 코드를 보냅니다', }, back: '뒤로', or: '또는', diff --git a/web/i18n/pl-PL/login.ts b/web/i18n/pl-PL/login.ts index 8b63fec50..909d1a431 100644 --- a/web/i18n/pl-PL/login.ts +++ b/web/i18n/pl-PL/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Twoja nazwa użytkownika', forget: 'Zapomniałeś hasła?', signBtn: 'Zaloguj się', - sso: 'Kontynuuj za pomocą SSO', installBtn: 'Ustaw', setAdminAccount: 'Ustawianie konta administratora', setAdminAccountDesc: @@ -86,8 +85,8 @@ const translation = { useAnotherMethod: 'Użyj innej metody', didNotReceiveCode: 'Nie otrzymałeś kodu?', verificationCode: 'Kod weryfikacyjny', - tips: 'Wysyłamy kod weryfikacyjny na adres {{email}}', emptyCode: 'Kod jest wymagany', + tipsPrefix: 'Wysyłamy kod weryfikacyjny do', }, continueWithCode: 'Kontynuuj z kodem', setYourAccount: 'Ustaw swoje konto', diff --git a/web/i18n/pt-BR/login.ts b/web/i18n/pt-BR/login.ts index 290cd3c8b..150df678d 100644 --- a/web/i18n/pt-BR/login.ts +++ b/web/i18n/pt-BR/login.ts @@ -70,19 +70,18 @@ const translation = { activated: 'Entrar agora', adminInitPassword: 'Senha de inicialização do administrador', validate: 'Validar', - sso: 'Continuar com SSO', checkCode: { useAnotherMethod: 'Use outro método', invalidCode: 'Código inválido', verificationCodePlaceholder: 'Digite o código de 6 dígitos', checkYourEmail: 'Verifique seu e-mail', - tips: 'Enviamos um código de verificação para {{email}}', emptyCode: 'O código é necessário', verify: 'Verificar', verificationCode: 'Código de verificação', resend: 'Reenviar', didNotReceiveCode: 'Não recebeu o código?', validTime: 'Lembre-se de que o código é válido por 5 minutos', + tipsPrefix: 'Enviamos um código de verificação para', }, resetPassword: 'Redefinir senha', or: 'OU', diff --git a/web/i18n/ro-RO/login.ts b/web/i18n/ro-RO/login.ts index 342010a10..ca1b1d4e0 100644 --- a/web/i18n/ro-RO/login.ts +++ b/web/i18n/ro-RO/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Numele tău de utilizator', forget: 'Ai uitat parola?', signBtn: 'Autentificare', - sso: 'Continuă cu SSO', installBtn: 'Configurare', setAdminAccount: 'Configurare cont de administrator', setAdminAccountDesc: 'Privilegii maxime pentru contul de administrator, care poate fi utilizat pentru crearea de aplicații și gestionarea furnizorilor LLM, etc.', @@ -80,9 +79,9 @@ const translation = { verificationCodePlaceholder: 'Introduceți codul din 6 cifre', emptyCode: 'Codul este necesar', verify: 'Verifica', - tips: 'Trimitem un cod de verificare la {{email}}', useAnotherMethod: 'Utilizați o altă metodă', resend: 'Retrimite', + tipsPrefix: 'Trimitem un cod de verificare la', }, usePassword: 'Utilizați parola', useVerificationCode: 'Utilizarea codului de verificare', diff --git a/web/i18n/ru-RU/login.ts b/web/i18n/ru-RU/login.ts index 38e455901..874b0aef0 100644 --- a/web/i18n/ru-RU/login.ts +++ b/web/i18n/ru-RU/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Ваше имя пользователя', forget: 'Забыли пароль?', signBtn: 'Войти', - sso: 'Продолжить с SSO', installBtn: 'Настроить', setAdminAccount: 'Настройка учетной записи администратора', setAdminAccountDesc: 'Максимальные привилегии для учетной записи администратора, которые можно использовать для создания приложений, управления поставщиками LLM и т. д.', @@ -79,10 +78,10 @@ const translation = { emptyCode: 'Код обязателен для заполнения', verificationCode: 'Проверочный код', checkYourEmail: 'Проверьте свою электронную почту', - tips: 'Мы отправляем код подтверждения на {{email}}', validTime: 'Имейте в виду, что код действителен в течение 5 минут', verificationCodePlaceholder: 'Введите 6-значный код', useAnotherMethod: 'Используйте другой метод', + tipsPrefix: 'Мы отправляем код проверки на', }, back: 'Назад', changePasswordBtn: 'Установите пароль', diff --git a/web/i18n/sl-SI/login.ts b/web/i18n/sl-SI/login.ts index 479b8b922..acb6aba2c 100644 --- a/web/i18n/sl-SI/login.ts +++ b/web/i18n/sl-SI/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Vaše uporabniško ime', forget: 'Ste pozabili geslo?', signBtn: 'Prijava', - sso: 'Nadaljujte z SSO', installBtn: 'Namesti', setAdminAccount: 'Nastavitev administratorskega računa', setAdminAccountDesc: 'Najvišje pravice za administratorski račun, ki se lahko uporablja za ustvarjanje aplikacij in upravljanje LLM ponudnikov itd.', @@ -76,13 +75,13 @@ const translation = { verificationCodePlaceholder: 'Vnesite 6-mestno kodo', resend: 'Poslati', verificationCode: 'Koda za preverjanje', - tips: 'Kodo za preverjanje pošljemo na {{email}}', verify: 'Preveriti', validTime: 'Upoštevajte, da je koda veljavna 5 minut', checkYourEmail: 'Preverjanje e-pošte', didNotReceiveCode: 'Niste prejeli kode?', invalidCode: 'Neveljavna koda', useAnotherMethod: 'Uporabite drug način', + tipsPrefix: 'Pošljemo kodo za preverjanje na', }, useVerificationCode: 'Uporaba kode za preverjanje', licenseInactive: 'Licenca je neaktivna', diff --git a/web/i18n/th-TH/login.ts b/web/i18n/th-TH/login.ts index 3db8da4da..621b9999a 100644 --- a/web/i18n/th-TH/login.ts +++ b/web/i18n/th-TH/login.ts @@ -79,7 +79,6 @@ const translation = { validate: 'ตรวจ สอบ', checkCode: { checkYourEmail: 'ตรวจสอบอีเมลของคุณ', - tips: 'เราส่งรหัสยืนยันไปที่ {{email}}', validTime: 'โปรดทราบว่ารหัสนี้ใช้ได้นาน 5 นาที', verificationCode: 'รหัสยืนยัน', verificationCodePlaceholder: 'ป้อนรหัส 6 หลัก', @@ -89,6 +88,7 @@ const translation = { useAnotherMethod: 'ใช้วิธีอื่น', emptyCode: 'ต้องใช้รหัส', invalidCode: 'รหัสไม่ถูกต้อง', + tipsPrefix: 'เราส่งรหัสตรวจสอบไปยัง', }, resetPassword: 'รีเซ็ตรหัสผ่าน', resetPasswordDesc: 'พิมพ์อีเมลที่คุณใช้ลงทะเบียนบน Dify แล้วเราจะส่งอีเมลรีเซ็ตรหัสผ่านให้คุณ', diff --git a/web/i18n/tr-TR/login.ts b/web/i18n/tr-TR/login.ts index b525dd0dd..96832ae58 100644 --- a/web/i18n/tr-TR/login.ts +++ b/web/i18n/tr-TR/login.ts @@ -9,7 +9,6 @@ const translation = { namePlaceholder: 'Kullanıcı adınız', forget: 'Şifrenizi mi unuttunuz?', signBtn: 'Giriş yap', - sso: 'SSO ile devam et', installBtn: 'Kurulum', setAdminAccount: 'Yönetici hesabı ayarlama', setAdminAccountDesc: 'Yönetici hesabı için maksimum ayrıcalıklar, uygulama oluşturma ve LLM sağlayıcılarını yönetme gibi işlemler için kullanılabilir.', @@ -81,8 +80,8 @@ const translation = { verificationCodePlaceholder: '6 haneli kodu girin', useAnotherMethod: 'Başka bir yöntem kullanın', didNotReceiveCode: 'Kodu almadınız mı?', - tips: '{{email}} adresine bir doğrulama kodu gönderiyoruz', resend: 'Tekrar Gönder', + tipsPrefix: 'Bir doğrulama kodu gönderiyoruz', }, enterYourName: 'Lütfen kullanıcı adınızı giriniz', resetPassword: 'Şifre Sıfırlama', diff --git a/web/i18n/uk-UA/login.ts b/web/i18n/uk-UA/login.ts index b586f3f24..a6b8d725e 100644 --- a/web/i18n/uk-UA/login.ts +++ b/web/i18n/uk-UA/login.ts @@ -70,7 +70,6 @@ const translation = { activated: 'Увійти зараз', adminInitPassword: 'Пароль ініціалізації адміністратора', validate: 'Перевірити', - sso: 'Продовжуйте працювати з SSW', checkCode: { didNotReceiveCode: 'Не отримали код?', invalidCode: 'Невірний код', @@ -81,8 +80,8 @@ const translation = { verify: 'Перевірити', verificationCode: 'Код підтвердження', useAnotherMethod: 'Використовуйте інший спосіб', - tips: 'Ми надсилаємо код підтвердження на адресу {{email}}', validTime: 'Майте на увазі, що код дійсний протягом 5 хвилин', + tipsPrefix: 'Ми відправляємо код підтвердження на', }, back: 'Задній', backToLogin: 'Назад до входу', diff --git a/web/i18n/vi-VN/login.ts b/web/i18n/vi-VN/login.ts index 520d5250a..1e770402d 100644 --- a/web/i18n/vi-VN/login.ts +++ b/web/i18n/vi-VN/login.ts @@ -70,7 +70,6 @@ const translation = { activated: 'Đăng nhập ngay', adminInitPassword: 'Mật khẩu khởi tạo quản trị viên', validate: 'Xác thực', - sso: 'Tiếp tục với SSO', checkCode: { checkYourEmail: 'Kiểm tra email của bạn', verify: 'Xác minh', @@ -82,7 +81,7 @@ const translation = { useAnotherMethod: 'Sử dụng phương pháp khác', emptyCode: 'Mã là bắt buộc', verificationCodePlaceholder: 'Nhập mã gồm 6 chữ số', - tips: 'Chúng tôi gửi mã xác minh đến {{email}}', + tipsPrefix: 'Chúng tôi gửi mã xác minh đến', }, back: 'Lưng', withSSO: 'Tiếp tục với SSO', diff --git a/web/i18n/zh-Hans/login.ts b/web/i18n/zh-Hans/login.ts index 2276436d0..d0b2cbe8c 100644 --- a/web/i18n/zh-Hans/login.ts +++ b/web/i18n/zh-Hans/login.ts @@ -79,7 +79,6 @@ const translation = { validate: '验证', checkCode: { checkYourEmail: '验证您的电子邮件', - tips: '验证码已经发送到您的邮箱 {{email}}', validTime: '请注意验证码 5 分钟内有效', verificationCode: '验证码', verificationCodePlaceholder: '输入 6 位验证码', @@ -89,6 +88,7 @@ const translation = { useAnotherMethod: '使用其他方式登录', emptyCode: '验证码不能为空', invalidCode: '验证码无效', + tipsPrefix: '我们发送一个验证码到', }, resetPassword: '重置密码', resetPasswordDesc: '请输入您的电子邮件地址以重置密码。我们将向您发送一封电子邮件。', diff --git a/web/i18n/zh-Hant/login.ts b/web/i18n/zh-Hant/login.ts index 818732327..64f812285 100644 --- a/web/i18n/zh-Hant/login.ts +++ b/web/i18n/zh-Hant/login.ts @@ -76,12 +76,12 @@ const translation = { didNotReceiveCode: '沒有收到驗證碼?', emptyCode: '驗證碼是必需的', checkYourEmail: '檢查您的電子郵件', - tips: '我們將驗證碼發送到 {{email}}', verificationCodePlaceholder: '輸入 6 位代碼', useAnotherMethod: '使用其他方法', validTime: '請記住,該代碼的有效期為 5 分鐘', verificationCode: '驗證碼', invalidCode: '無效代碼', + tipsPrefix: '我們發送一個驗證碼到', }, continueWithCode: 'Continue With Code', or: '或',