From c46157a6882b819fe93624529bf1d93279c1c13e Mon Sep 17 00:00:00 2001 From: tomaioo Date: Thu, 18 Jun 2026 05:07:28 -0700 Subject: [PATCH] fix(security): 2 improvements across 1 files - Security: Command Injection via Unsanitized Terminal ID in restore() - Security: Path Traversal via Terminal ID in restore() Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com> --- src/cli_agent_orchestrator/cli/commands/terminal.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cli_agent_orchestrator/cli/commands/terminal.py b/src/cli_agent_orchestrator/cli/commands/terminal.py index e3bb8e3c..8346056c 100644 --- a/src/cli_agent_orchestrator/cli/commands/terminal.py +++ b/src/cli_agent_orchestrator/cli/commands/terminal.py @@ -2,6 +2,8 @@ import json import os +import re +import shlex import click import requests @@ -24,6 +26,11 @@ def restore(terminal_id: str): working directory and loads the saved scrollback history into the pane. The session must still exist. """ + if not re.fullmatch(r"[A-Za-z0-9_-]+", terminal_id): + raise click.ClickException( + f"Invalid terminal_id '{terminal_id}'. Only alphanumeric, underscore, and hyphen characters are allowed." + ) + snapshot_path = TERMINAL_LOG_DIR / f"{terminal_id}.snapshot.json" scrollback_path = TERMINAL_LOG_DIR / f"{terminal_id}.scrollback" @@ -57,9 +64,9 @@ def restore(terminal_id: str): login_shell = os.environ.get("SHELL", "bash") if scrollback_path.exists(): - window_shell = f"cat '{scrollback_path}'; exec {login_shell} -l" + window_shell = f"cat {shlex.quote(str(scrollback_path))}; exec {shlex.quote(login_shell)} -l" else: - window_shell = f"exec {login_shell} -l" + window_shell = f"exec {shlex.quote(login_shell)} -l" try: get_backend().create_window(