fix(cli): confine agent runtime paths
This commit is contained in:
1 parent
bfe068b367
commit
f262dffc2b
5 files changed
+266
-17
No files matched your search
@@ -0,0 +1,127 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from elydora._runtime_paths import (
|
||||
ensure_private_directory,
|
||||
require_physical_directory,
|
||||
resolve_agent_directory,
|
||||
)
|
||||
from elydora import cli
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"agent_id",
|
||||
[
|
||||
"../escape",
|
||||
"..\\escape",
|
||||
"C:escape",
|
||||
"agent.",
|
||||
"agent ",
|
||||
"CON",
|
||||
"COM¹.log",
|
||||
".",
|
||||
"..",
|
||||
],
|
||||
)
|
||||
def test_agent_directory_rejects_unsafe_cross_platform_names(
|
||||
tmp_path: Path,
|
||||
agent_id: str,
|
||||
) -> None:
|
||||
with pytest.raises(ValueError, match="agent ID"):
|
||||
resolve_agent_directory(str(tmp_path / ".elydora"), agent_id)
|
||||
|
||||
|
||||
def test_agent_directory_is_one_private_physical_child(tmp_path: Path) -> None:
|
||||
root = tmp_path / ".elydora"
|
||||
agent_dir = Path(resolve_agent_directory(str(root), "agent-1"))
|
||||
|
||||
ensure_private_directory(str(root))
|
||||
ensure_private_directory(str(agent_dir))
|
||||
|
||||
assert agent_dir == root / "agent-1"
|
||||
assert require_physical_directory(str(agent_dir)) is True
|
||||
if os.name != "nt":
|
||||
assert root.stat().st_mode & 0o777 == 0o700
|
||||
assert agent_dir.stat().st_mode & 0o777 == 0o700
|
||||
|
||||
|
||||
def test_private_directory_rejects_symbolic_links(tmp_path: Path) -> None:
|
||||
target = tmp_path / "target"
|
||||
target.mkdir()
|
||||
link = tmp_path / "agent-link"
|
||||
try:
|
||||
link.symlink_to(target, target_is_directory=True)
|
||||
except OSError as error:
|
||||
pytest.skip(f"Directory symbolic links are unavailable: {error}")
|
||||
|
||||
with pytest.raises(OSError, match="physical directory"):
|
||||
ensure_private_directory(str(link))
|
||||
with pytest.raises(OSError, match="physical directory"):
|
||||
require_physical_directory(str(link))
|
||||
|
||||
|
||||
def test_install_rejects_unsafe_agent_id_before_writes(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
args = cli.build_parser().parse_args(
|
||||
[
|
||||
"install",
|
||||
"--agent",
|
||||
"opencode",
|
||||
"--org_id",
|
||||
"org-1",
|
||||
"--agent_id",
|
||||
"../escape",
|
||||
"--private_key",
|
||||
"unused",
|
||||
"--kid",
|
||||
"key-1",
|
||||
]
|
||||
)
|
||||
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
cli.cmd_install(args)
|
||||
|
||||
assert exc_info.value.code == 1
|
||||
assert "Invalid agent ID for local storage" in capsys.readouterr().err
|
||||
assert (tmp_path / ".elydora").exists() is False
|
||||
|
||||
|
||||
def test_uninstall_validates_config_directory_before_plugin_changes(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
runtime_dir = tmp_path / ".elydora" / "stored-directory"
|
||||
runtime_dir.mkdir(parents=True)
|
||||
(runtime_dir / "config.json").write_text(
|
||||
json.dumps({"agent_name": "opencode", "agent_id": "different-agent"}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
plugin_calls: list[str] = []
|
||||
|
||||
class Plugin:
|
||||
def uninstall(self, agent_id: str = "") -> None:
|
||||
plugin_calls.append(agent_id)
|
||||
|
||||
monkeypatch.setattr(cli, "_get_plugin", lambda _name: Plugin())
|
||||
args = cli.build_parser().parse_args(["uninstall", "--agent", "opencode"])
|
||||
|
||||
with pytest.raises(SystemExit) as exc_info:
|
||||
cli.cmd_uninstall(args)
|
||||
|
||||
assert exc_info.value.code == 1
|
||||
assert "crosses its runtime directory" in capsys.readouterr().err
|
||||
assert plugin_calls == []
|
||||
assert runtime_dir.is_dir()
|
||||
Reference in new issue
Block a user