diff --git a/components/terminal/xterm-terminal.tsx b/components/terminal/xterm-terminal.tsx index 6e572e5..763b583 100644 --- a/components/terminal/xterm-terminal.tsx +++ b/components/terminal/xterm-terminal.tsx @@ -357,15 +357,17 @@ export function XtermTerminal({ return null; } + // Remove credentials from URL — they are sent in the WebSocket init message instead + url.searchParams.delete('authorization'); + // Add session ID as arg parameter for ttyd-auth.sh - // URL format: ?authorization=base64(user:pass)&arg=SESSION_ID url.searchParams.append('arg', terminalSessionId.current); const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:'; const wsPath = url.pathname.replace(/\/$/, '') + '/ws'; const wsFullUrl = `${wsProtocol}//${url.host}${wsPath}${url.search}`; - console.log('[XtermTerminal] Connecting to:', wsFullUrl.replace(authorization, '***')); + console.log('[XtermTerminal] Connecting to:', wsFullUrl); return { wsFullUrl, authorization }; } catch (error) { console.error('[XtermTerminal] Failed to parse URL:', error); diff --git a/lib/actions/sandbox.ts b/lib/actions/sandbox.ts index 49653fe..18674b3 100644 --- a/lib/actions/sandbox.ts +++ b/lib/actions/sandbox.ts @@ -40,9 +40,13 @@ export async function runCommand( const { ttyd } = await getSandboxTtydContext(sandboxId, session.user.id) const { baseUrl, accessToken, authorization } = ttyd - const output = await execCommand(baseUrl, accessToken, command, timeoutMs, authorization) + const result = await execCommand(baseUrl, accessToken, command, timeoutMs, authorization) - return { success: true, output } + if (result.exitCode !== 0) { + return { success: false, error: `Command failed with exit code ${result.exitCode}`, output: result.output } + } + + return { success: true, output: result.output } } catch (error) { console.error('Failed to execute command in sandbox:', error) const errorMessage = error instanceof TtydExecError ? error.message : 'Unknown error' diff --git a/lib/util/ttyd-exec.ts b/lib/util/ttyd-exec.ts index d789cd1..0cfefab 100644 --- a/lib/util/ttyd-exec.ts +++ b/lib/util/ttyd-exec.ts @@ -167,8 +167,7 @@ function stripAnsiCodes(str: string): string { function buildWsUrl( ttydUrl: string, accessToken: string, - sessionId?: string, - authorization?: string + sessionId?: string ): string { const url = new URL(ttydUrl) const wsProtocol = url.protocol === 'https:' ? 'wss:' : 'ws:' @@ -180,10 +179,7 @@ function buildWsUrl( if (sessionId) { params.append('arg', sessionId) } - if (authorization) { - params.append('authorization', authorization) - } - + // authorization is sent securely in the WebSocket init message — not added to URL return `${wsProtocol}//${url.host}${wsPath}?${params.toString()}` } @@ -256,7 +252,7 @@ export async function executeTtydCommand(options: TtydExecOptions): Promise { +): Promise<{ output: string; exitCode: number }> { const result = await executeTtydCommand({ ttydUrl, accessToken, @@ -579,7 +575,7 @@ export async function execCommand( throw new TtydExecError('Command timed out', TtydExecErrorCode.TIMEOUT) } - return result.output + return { output: result.output, exitCode: result.exitCode } } /** diff --git a/next.config.ts b/next.config.ts index 46354ed..d390cf1 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from 'next' const nextConfig: NextConfig = { + turbopack: false as any, reactStrictMode: true, output: 'standalone', compress: true,