fix: command injection in watch_serve.sh, SSH host key verification, log permissions, input validation

ober

9618aa593c587b2845bd41da6ca546d89df23331

diff --git a/jerboa_proxy.py b/jerboa_proxy.py
index c391b14..b328793 100644
--- a/jerboa_proxy.py
+++ b/jerboa_proxy.py
@@ -13,7 +13,7 @@ Why this exists (proven this session):
 
 Run:  python3 jerboa_proxy.py [UPSTREAM_BASE] [PORT]
 """
-import json, sys, urllib.request, urllib.error, http.server, socketserver, time
+import json, os, sys, urllib.request, urllib.error, http.server, socketserver, time
 
 UPSTREAM = (sys.argv[1] if len(sys.argv) > 1 else
             "https://x8d9bjmhior1nx-8000.proxy.runpod.net").rstrip("/")
@@ -25,10 +25,13 @@ GROUNDING = ("You are an expert in Jerboa Scheme, a Chez-Scheme-based dialect wi
              "Gerbil-flavored prelude. Provide accurate, idiomatic Jerboa code with correct "
              "imports. Module paths use (jerboa ...) and (std ...) — never :std/foo (Gerbil) "
              "or (srfi :NN) (R7).")
-LOG = "/tmp/jerboa_proxy.log"
+LOG_DIR = os.path.join(os.path.expanduser("~"), ".jerboa")
+LOG = os.path.join(LOG_DIR, "proxy.log")
+os.makedirs(LOG_DIR, exist_ok=True)
 
 def log(m):
-    with open(LOG, "a") as f:
+    fd = os.open(LOG, os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
+    with os.fdopen(fd, "a") as f:
         f.write(f"{time.strftime('%H:%M:%S')} {m}\n")
 
 def ground(raw):
@@ -70,11 +73,17 @@ def ground(raw):
 class H(http.server.BaseHTTPRequestHandler):
     protocol_version = "HTTP/1.0"  # close-delimited: simplest correct SSE passthrough
     def _do(self, method):
-        n = int(self.headers.get("Content-Length", 0) or 0)
+        try:
+            n = int(self.headers.get("Content-Length", 0) or 0)
+        except (ValueError, TypeError):
+            self.send_response(400); self.end_headers()
+            self.wfile.write(b"Invalid Content-Length"); return
         raw = self.rfile.read(n) if n else b""
         if self.path.endswith("/chat/completions") and raw:
             raw, note = ground(raw)
             log(f"POST {self.path} {note} stream={b'stream' in raw}")
+        # Intentional: forward the client's Authorization header to the upstream
+        # RunPod proxy, which requires it for authentication.
         req = urllib.request.Request(
             UPSTREAM + self.path, data=raw or None, method=method,
             headers={"Content-Type": "application/json", "User-Agent": UA,
diff --git a/upload_hf.sh b/upload_hf.sh
index bb11d04..2bf06aa 100755
--- a/upload_hf.sh
+++ b/upload_hf.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-cd /Users/user/mine/jerboa-lora
+cd "$(dirname "$0")"
 source .venv-mlx/bin/activate
 export HF_HUB_DISABLE_XET=1
 
diff --git a/v6_runpod.py b/v6_runpod.py
index cc65992..f771b35 100644
--- a/v6_runpod.py
+++ b/v6_runpod.py
@@ -149,9 +149,7 @@ def ssh_base(state: dict[str, Any]) -> list[str]:
         "-p",
         str(port),
         "-o",
-        "StrictHostKeyChecking=no",
-        "-o",
-        "UserKnownHostsFile=/dev/null",
+        "StrictHostKeyChecking=accept-new",
         "-o",
         "LogLevel=ERROR",
         "-o",
@@ -191,9 +189,7 @@ def run_scp(state: dict[str, Any], local: Path, remote: str, *, recursive: bool 
         "-P",
         str(port),
         "-o",
-        "StrictHostKeyChecking=no",
-        "-o",
-        "UserKnownHostsFile=/dev/null",
+        "StrictHostKeyChecking=accept-new",
         "-o",
         "LogLevel=ERROR",
         "-o",
@@ -218,9 +214,7 @@ def scp_back(state: dict[str, Any], remote: str, local: Path, *, recursive: bool
         "-P",
         str(port),
         "-o",
-        "StrictHostKeyChecking=no",
-        "-o",
-        "UserKnownHostsFile=/dev/null",
+        "StrictHostKeyChecking=accept-new",
         "-o",
         "LogLevel=ERROR",
         "-o",
diff --git a/watch_serve.sh b/watch_serve.sh
index 88dd3dd..f761fde 100644
--- a/watch_serve.sh
+++ b/watch_serve.sh
@@ -1,8 +1,10 @@
 #!/bin/bash
 # watch_serve.sh <ip> <port>   — watchdog for 70GB pull + vLLM load on the pod
-IP=$1; PORT=$2
-CP=/tmp/rp_cm_$PORT
-SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPath=$CP -o ControlPersist=120 -i $HOME/.ssh/runpod_v5 -p $PORT root@$IP"
+IP="$1"; PORT="$2"
+case "$IP" in *[!0-9.]*|"") echo "Invalid IP" >&2; exit 1;; esac
+case "$PORT" in *[!0-9]*|"") echo "Invalid PORT" >&2; exit 1;; esac
+CP="/tmp/rp_cm_${PORT}"
+SSH="ssh -o StrictHostKeyChecking=accept-new -o ControlMaster=auto -o ControlPath=${CP} -o ControlPersist=120 -i ${HOME}/.ssh/runpod_v5 -p ${PORT} root@${IP}"
 prev=""; stall=0
 for i in $(seq 1 90); do
   ts=$(date +%H:%M:%S)