feat: auto-sudo-password support multi language and sudo-rs (#11019)
Package-Build / Lint (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Docs / build (push) Has been cancelled
Package-Build / macOS-Build (arm64, aarch64-apple-darwin) (push) Has been cancelled
Package-Build / macOS-Build (x86_64, x86_64-apple-darwin) (push) Has been cancelled
Package-Build / Linux-Build (amd64, x64, ubuntu-24.04, x86_64-unknown-linux-gnu) (push) Has been cancelled
Package-Build / Linux-Build (arm64, arm64, ubuntu-24.04-arm, aarch64-unknown-linux-gnu, aarch64-linux-gnu-) (push) Has been cancelled
Package-Build / Linux-Build (armhf, arm, ubuntu-24.04, arm-unknown-linux-gnueabihf, arm-linux-gnueabihf-) (push) Has been cancelled
Package-Build / Windows-Build (arm64, aarch64-pc-windows-msvc) (push) Has been cancelled
Package-Build / Windows-Build (x64, x86_64-pc-windows-msvc) (push) Has been cancelled

This commit is contained in:
Ponder
2026-05-17 04:24:58 +08:00
committed by GitHub
parent af022064a4
commit fdd3945d05
+67 -5
View File
@@ -3,12 +3,61 @@ import { Injectable } from '@angular/core'
import { TerminalDecorator, BaseTerminalTabComponent, XTermFrontend, SessionMiddleware } from 'tabby-terminal'
import { SSHProfile, SSHTabComponent, PasswordStorageService } from 'tabby-ssh'
const SUDO_PROMPT_REGEX = /^\[sudo\] password for ([^:]+):\s*$/im
const SUDO_PROMPT_MARKER = '[sudo'
// Multi-language sudo prompt patterns
// Each pattern captures the username in a capture group (empty for sudo-rs)
const SUDO_PROMPT_PATTERNS: RegExp[] = [
// Traditional sudo patterns (with username)
// English: [sudo] password for username:
/^\[sudo\] password for ([^:]+):\s*$/im,
// German: [sudo] Passwort für username:
/^\[sudo\] Passwort für ([^:]+):\s*$/im,
// French: [sudo] Mot de passe de username :
/^\[sudo\] Mot de passe de ([^:]+)\s+:\s*$/im,
// Spanish: [sudo] contraseña para username:
/^\[sudo\] [Cc]ontraseña para ([^:]+):\s*$/im,
// Portuguese: [sudo] senha para username:
/^\[sudo\] [Ss]enha para ([^:]+):\s*$/im,
// Italian: [sudo] password di username:
/^\[sudo\] [Pp]assword di ([^:]+):\s*$/im,
// Simplified Chinese: [sudo] username 的密码:
/^\[sudo\] ([^\s]+) 的密码[:]\s*$/im,
// Traditional Chinese: [sudo] username 的密碼:
/^\[sudo\] ([^\s]+) 的密碼[:]\s*$/im,
// Japanese: [sudo] username のパスワード:
/^\[sudo\] ([^\s]+) のパスワード[:]\s*$/im,
// Korean: [sudo] username 암호:
/^\[sudo\] ([^\s]+) 암호[:]\s*$/im,
// Russian: [sudo] пароль для username:
/^\[sudo\] пароль для ([^:]+):\s*$/im,
// Polish: [sudo] hasło użytkownika username:
/^\[sudo\] hasło użytkownika ([^:]+):\s*$/im,
// Turkish: [sudo] username için parola:
/^\[sudo\] ([^\s]+) için parola:\s*$/im,
// Czech: [sudo] heslo pro username:
/^\[sudo\] [Hh]eslo pro ([^:]+):\s*$/im,
// Swedish: [sudo] lösenord för username:
/^\[sudo\] lösenord för ([^:]+):\s*$/im,
// Danish: [sudo] adgangskode for username:
/^\[sudo\] adgangskode for ([^:]+):\s*$/im,
// Indonesian: [sudo] kata sandi untuk username:
/^\[sudo\] kata sandi untuk ([^:]+):\s*$/im,
// Ukrainian: [sudo] пароль до username:
/^\[sudo\] пароль до ([^:]+):\s*$/im,
// Croatian: [sudo] lozinka za username:
/^\[sudo\] lozinka za ([^:]+):\s*$/im,
// sudo-rs pattern (no username displayed, use empty capture group)
// Matches: [sudo: authenticate] <password word>:
/^\[sudo: authenticate\] .+?[:]\s*$/im,
]
export class AutoSudoPasswordMiddleware extends SessionMiddleware {
private pendingPasswordToPaste: string | null = null
private pasteHint = `${colors.black.bgBlackBright(' Tabby ')} ${colors.gray('Press Enter to paste saved password')}`
private pasteHintLength = colors.stripColor(this.pasteHint).length
// Cache the last matched pattern index for performance optimization
private lastMatchedPatternIndex = 0
constructor (
private profile: SSHProfile,
@@ -17,10 +66,23 @@ export class AutoSudoPasswordMiddleware extends SessionMiddleware {
feedFromSession (data: Buffer): void {
const text = data.toString('utf-8')
const match = SUDO_PROMPT_REGEX.exec(text)
if (match) {
const username = match[1]
this.handlePrompt(username)
if (!text.toLowerCase().includes(SUDO_PROMPT_MARKER)) {
this.outputToTerminal.next(data)
return
}
// Try patterns starting from the last successful match for better performance
const patternCount = SUDO_PROMPT_PATTERNS.length
for (let i = 0; i < patternCount; i++) {
const idx = (this.lastMatchedPatternIndex + i) % patternCount
const pattern = SUDO_PROMPT_PATTERNS[idx]
const match = pattern.exec(text)
if (match) {
this.lastMatchedPatternIndex = idx // Remember this pattern for next time
// For sudo-rs patterns, match[1] is undefined, use current SSH user
const username = match[1] || this.profile.options.user
this.handlePrompt(username)
break
}
}
this.outputToTerminal.next(data)
}