diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index a4b32872..40c69377 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -297,11 +297,34 @@ terminalWss.on( switch (msg.type) { case "input": + // Validate input data type and length + if (typeof msg.data !== "string") { + ws.send(JSON.stringify({ type: "error", message: "Invalid input type" })); + break; + } + // Limit input size to 1MB to prevent memory issues + if (msg.data.length > 1024 * 1024) { + ws.send(JSON.stringify({ type: "error", message: "Input too large" })); + break; + } // Write user input to terminal terminalService.write(sessionId, msg.data); break; case "resize": + // Validate resize dimensions are positive integers within reasonable bounds + if ( + typeof msg.cols !== "number" || + typeof msg.rows !== "number" || + !Number.isInteger(msg.cols) || + !Number.isInteger(msg.rows) || + msg.cols < 1 || + msg.cols > 1000 || + msg.rows < 1 || + msg.rows > 500 + ) { + break; // Silently ignore invalid resize requests + } // Resize terminal with deduplication and rate limiting if (msg.cols && msg.rows) { const now = Date.now(); diff --git a/apps/server/src/routes/terminal/common.ts b/apps/server/src/routes/terminal/common.ts index 85039c39..7ce223d6 100644 --- a/apps/server/src/routes/terminal/common.ts +++ b/apps/server/src/routes/terminal/common.ts @@ -2,6 +2,7 @@ * Common utilities and state for terminal routes */ +import { randomBytes } from "crypto"; import { createLogger } from "../../lib/logger.js"; import type { Request, Response, NextFunction } from "express"; import { getTerminalService } from "../../services/terminal-service.js"; @@ -49,12 +50,10 @@ export function getTokenData( } /** - * Generate a secure random token + * Generate a cryptographically secure random token */ export function generateToken(): string { - return `term-${Date.now()}-${Math.random() - .toString(36) - .slice(2, 17)}${Math.random().toString(36).slice(2, 17)}`; + return `term-${randomBytes(32).toString("base64url")}`; } /** diff --git a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx index 2ee46236..4a10cc11 100644 --- a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx +++ b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx @@ -668,8 +668,8 @@ export function TerminalPanel({ // Use event.code for keyboard-layout-independent key detection const code = event.code; - // Ctrl+Shift+D - Split right (uses Ctrl+Shift to avoid Alt+D readline conflict) - if (event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey && code === 'KeyD') { + // Alt+D - Split right + if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey && code === 'KeyD') { event.preventDefault(); if (canTrigger) { lastShortcutTimeRef.current = now; @@ -678,8 +678,8 @@ export function TerminalPanel({ return false; } - // Ctrl+Shift+S - Split down (uses Ctrl+Shift to avoid readline conflicts) - if (event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey && code === 'KeyS') { + // Alt+S - Split down + if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey && code === 'KeyS') { event.preventDefault(); if (canTrigger) { lastShortcutTimeRef.current = now; @@ -688,8 +688,8 @@ export function TerminalPanel({ return false; } - // Ctrl+Shift+W - Close terminal (uses Ctrl+Shift to avoid readline conflicts) - if (event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey && code === 'KeyW') { + // Alt+W - Close terminal + if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey && code === 'KeyW') { event.preventDefault(); if (canTrigger) { lastShortcutTimeRef.current = now; @@ -698,8 +698,8 @@ export function TerminalPanel({ return false; } - // Ctrl+Shift+T - New terminal tab (uses Ctrl+Shift for consistency) - if (event.ctrlKey && event.shiftKey && !event.altKey && !event.metaKey && code === 'KeyT') { + // Alt+T - New terminal tab + if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey && code === 'KeyT') { event.preventDefault(); if (canTrigger && onNewTabRef.current) { lastShortcutTimeRef.current = now; @@ -1757,7 +1757,7 @@ export function TerminalPanel({ e.stopPropagation(); onSplitHorizontal(); }} - title="Split Right (Cmd+D)" + title="Split Right (Alt+D)" > @@ -1769,7 +1769,7 @@ export function TerminalPanel({ e.stopPropagation(); onSplitVertical(); }} - title="Split Down (Cmd+Shift+D)" + title="Split Down (Alt+S)" > @@ -1799,7 +1799,7 @@ export function TerminalPanel({ e.stopPropagation(); onClose(); }} - title="Close Terminal (Cmd+W)" + title="Close Terminal (Alt+W)" >