From e5d650c8a45733630ac9be0000eed74e2ab5f42c Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Wed, 24 Dec 2025 11:11:53 +1100 Subject: [PATCH] Add server shell access --- app.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index a782290..17cd312 100644 --- a/app.py +++ b/app.py @@ -538,13 +538,22 @@ def api_terminal_exec(): return jsonify({"error": "No command provided"}), 400 try: - # Use shell execution via sh() - # sh() uses subprocess.check_output with shell=False by default (list of strings) - # However, to support pipe/redirection for the user, we should allow shell=True-like behavior - # Let's wrap it in ['sh', '-c', command] - output = sh(["sh", "-c", command]) + # HOST ESCAPE: execute command on the host by mounting host / and using chroot + # We use a tiny alpine container to bridge to the host. + # This requires the flask container to have docker socket access (which it does). + host_cmd = [ + DOCKER, "run", "--rm", + "-v", "/:/host", + "alpine", "chroot", "/host", "sh", "-c", command + ] + + output = sh(host_cmd) return jsonify({"output": output, "status": "success"}) except subprocess.CalledProcessError as e: - return jsonify({"output": e.output or str(e), "error": True, "status": "error"}) + # Cast output to string as it might be bytes + out = e.output + if hasattr(out, "decode"): + out = out.decode("utf-8", errors="replace") + return jsonify({"output": out or str(e), "error": True, "status": "error"}) except Exception as e: return jsonify({"output": str(e), "error": True, "status": "error"})