diff --git a/.gitignore b/.gitignore
index 22fafbd..be78ef6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ Thumbs.db
# Logs
*.log
+.clawmetry-fleet.db
diff --git a/clawmetry/cli.py b/clawmetry/cli.py
index dc11912..d382335 100755
--- a/clawmetry/cli.py
+++ b/clawmetry/cli.py
@@ -556,6 +556,7 @@ def _register_nemoclaw_sandbox_daemons() -> None:
import shutil
import os
import platform
+ from xml.sax.saxutils import escape
if platform.system() != "Darwin":
return
@@ -607,8 +608,9 @@ def _register_nemoclaw_sandbox_daemons() -> None:
docker_path = shutil.which("docker") or "/usr/local/bin/docker"
for pod in pods:
- label = f"com.clawmetry.sandbox.{pod}"
- plist_path = launch_agents / f"{label}.plist"
+ label = f"com.clawmetry.sandbox.{escape(pod)}"
+ pod_xml = escape(pod)
+ plist_path = launch_agents / f"com.clawmetry.sandbox.{pod}.plist"
sync_script = "/usr/local/lib/python3.11/dist-packages/clawmetry/sync.py"
plist = f"""
@@ -624,7 +626,7 @@ def _register_nemoclaw_sandbox_daemons() -> None:
exec
-n
openshell
- {pod}
+ {pod_xml}
--
python3
{sync_script}
@@ -632,8 +634,8 @@ def _register_nemoclaw_sandbox_daemons() -> None:
RunAtLoad
KeepAlive
ThrottleInterval 30
- StandardOutPath /tmp/clawmetry-{pod}.log
- StandardErrorPath /tmp/clawmetry-{pod}.log
+ StandardOutPath /tmp/clawmetry-{pod_xml}.log
+ StandardErrorPath /tmp/clawmetry-{pod_xml}.log
"""
plist_path.write_text(plist)
@@ -1670,8 +1672,18 @@ def _cmd_update() -> None:
print("Checking for updates...")
try:
result = subprocess.run(
- [sys.executable, "-m", "pip", "install", "--upgrade", "--break-system-packages", "clawmetry"],
- capture_output=True, text=True, timeout=120,
+ [
+ sys.executable,
+ "-m",
+ "pip",
+ "install",
+ "--upgrade",
+ "--break-system-packages",
+ "clawmetry",
+ ],
+ capture_output=True,
+ text=True,
+ timeout=120,
)
if result.returncode == 0:
# Check new version
diff --git a/tests/test_cli.py b/tests/test_cli.py
index e4513d7..4781907 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,4 +1,7 @@
from pathlib import Path
+from xml.etree import ElementTree as ET
+
+import pytest
import clawmetry.cli as cli
@@ -30,9 +33,51 @@ def test_print_nemoclaw_preset_hint_emits_command(monkeypatch, capsys):
helper = "/tmp/add-nemoclaw-clawmetry-preset.sh"
monkeypatch.setattr(cli, "_get_nemoclaw_preset_script", lambda: helper)
- cli._print_nemoclaw_preset_hint(lambda text: text, lambda text: text, lambda text: text)
+ cli._print_nemoclaw_preset_hint(
+ lambda text: text, lambda text: text, lambda text: text
+ )
out = capsys.readouterr().out
assert "NemoClaw detected" in out
assert "allow your NemoClaw sandboxes to reach ClawMetry Cloud" in out
assert helper in out
+
+
+def test_pod_name_xml_escaping():
+ from xml.sax.saxutils import escape
+
+ malicious_pod = "test-pod&injectionattacker"
+ pod_xml = escape(malicious_pod)
+ docker_path = "/usr/bin/docker"
+ cluster = "openshell-cluster"
+ sync_script = "/usr/local/lib/python3.11/dist-packages/clawmetry/sync.py"
+ label = f"com.clawmetry.sandbox.{escape(malicious_pod)}"
+
+ plist = f"""
+
+
+
+ Label {label}
+ ProgramArguments
+
+ {docker_path}
+ exec
+ {cluster}
+ kubectl
+ exec
+ -n
+ openshell
+ {pod_xml}
+ --
+ python3
+ {sync_script}
+
+ RunAtLoad
+ KeepAlive
+ ThrottleInterval 30
+ StandardOutPath /tmp/clawmetry-{pod_xml}.log
+ StandardErrorPath /tmp/clawmetry-{pod_xml}.log
+
+"""
+
+ ET.fromstring(plist)