From c0939481b66574bf0c0a1c9204b01fa83d3d8ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Mon, 11 May 2026 22:43:43 +0900 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B1=D0=B5=D0=B7=D0=BE=D0=BF=D0=B0=D1=81=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20shell=20runner=20=D0=B4=D0=BB=D1=8F=20workspace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/shell_runner.py | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 backend/shell_runner.py diff --git a/backend/shell_runner.py b/backend/shell_runner.py new file mode 100644 index 0000000..978115c --- /dev/null +++ b/backend/shell_runner.py @@ -0,0 +1,57 @@ +import os +import subprocess +from pathlib import Path + +DATA_DIR = Path(os.getenv('DEVCONSOLE_DATA_DIR', '/var/lib/devconsole')) +PROJECTS_DIR = DATA_DIR / 'projects' + +BLOCKED_TOKENS = [ + 'rm -rf /', + 'mkfs', + ':(){', + 'dd if=', + 'shutdown', + 'reboot', + 'poweroff', +] + + +def _is_inside_projects(path: Path) -> bool: + try: + path.resolve().relative_to(PROJECTS_DIR.resolve()) + return True + except ValueError: + return False + + +def run_command(command: str, cwd: str | None = None, timeout: int = 900) -> dict: + if not command.strip(): + raise ValueError('Command is empty') + + lowered = command.lower() + for token in BLOCKED_TOKENS: + if token in lowered: + raise ValueError(f'Blocked unsafe command: {token}') + + workdir = Path(cwd or PROJECTS_DIR).resolve() + PROJECTS_DIR.mkdir(parents=True, exist_ok=True) + + if not _is_inside_projects(workdir): + raise ValueError('Command cwd must be inside DevConsole projects directory') + + process = subprocess.run( + command, + cwd=workdir, + shell=True, + text=True, + capture_output=True, + timeout=timeout, + ) + + return { + 'command': command, + 'cwd': workdir.as_posix(), + 'returncode': process.returncode, + 'stdout': process.stdout[-20000:], + 'stderr': process.stderr[-20000:], + }