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)"
>