A pure move; behaviour unchanged. - models (L9, L7): llm, catalog, probe, render, render_anthropic, context - log (L7, L6): canonical, views - state (L8): store (was state), reconcile, worker, cancel, dump - knowledge (L10, L6.4): vault, memory - conversation (L5): transport, matrix - execution (L13): ssh (was execution), testing - authority (P3, P4, L4): actions - gitea, oplog, envfile stay at the top until each gains a second module. Every import, monkeypatch target, script and doc reference rewritten. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
122 lines
3.9 KiB
Python
122 lines
3.9 KiB
Python
import pytest
|
|
|
|
from nisse_core.authority.actions import (
|
|
CONTAINED,
|
|
DANGEROUS,
|
|
HARMLESS,
|
|
REFUSED,
|
|
UNKNOWN,
|
|
Action,
|
|
Command,
|
|
Verdict,
|
|
classify_shell,
|
|
inside,
|
|
resolve,
|
|
split_shell,
|
|
unwrap,
|
|
worst,
|
|
)
|
|
|
|
|
|
def test_an_approval_binds_to_the_exact_tool_and_arguments() -> None:
|
|
a = Action("run_command", {"command": "git push"})
|
|
assert a.key() == Action("run_command", {"command": "git push"}).key()
|
|
assert a.key() != Action("run_command", {"command": "git push -f"}).key()
|
|
assert a.key() != Action("write_file", {"command": "git push"}).key()
|
|
|
|
|
|
def test_the_worst_verdict_wins() -> None:
|
|
verdicts = [
|
|
Verdict(HARMLESS, "a", ""),
|
|
Verdict(DANGEROUS, "b", ""),
|
|
Verdict(UNKNOWN, "c", ""),
|
|
]
|
|
assert worst(verdicts).rule == "b"
|
|
assert worst([Verdict(DANGEROUS, "d", ""), Verdict(REFUSED, "r", "")]).rule == "r"
|
|
assert worst([]).effect == HARMLESS
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("line", "commands"),
|
|
[
|
|
("pytest -q", [(["pytest", "-q"], [])]),
|
|
(
|
|
"pytest 2>/dev/null && git status",
|
|
[(["pytest"], ["/dev/null"]), (["git", "status"], [])],
|
|
),
|
|
(
|
|
"ls | grep x; echo hi >> log",
|
|
[(["ls"], []), (["grep", "x"], []), (["echo", "hi"], ["log"])],
|
|
),
|
|
("make test 2>&1 | tail -5", [(["make", "test"], []), (["tail", "-5"], [])]),
|
|
("echo 'a > b'", [(["echo", "a > b"], [])]),
|
|
("cd sub\nls", [(["cd", "sub"], []), (["ls"], [])]),
|
|
("ls # a comment", [(["ls"], [])]),
|
|
],
|
|
)
|
|
def test_a_line_splits_into_its_commands(line: str, commands: list) -> None:
|
|
assert split_shell(line) == commands
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"line",
|
|
["echo $(whoami)", "echo `id`", "cat <<EOF\nx\nEOF", "(cd /; rm x)", "echo 'open"],
|
|
)
|
|
def test_what_cant_be_read_statically_is_not_split(line: str) -> None:
|
|
assert split_shell(line) is None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("argv", "command"),
|
|
[
|
|
(["sudo", "apt-get", "install", "x"], ["apt-get", "install", "x"]),
|
|
(["FOO=1", "BAR=2", "pytest"], ["pytest"]),
|
|
(["env", "-i", "A=1", "make"], ["make"]),
|
|
(["timeout", "60", "pytest", "-x"], ["pytest", "-x"]),
|
|
(["timeout", "-k", "5", "60", "pytest"], ["pytest"]),
|
|
(["timeout", "--signal=KILL", "60", "make"], ["make"]),
|
|
(["nohup", "python", "serve.py"], ["python", "serve.py"]),
|
|
],
|
|
)
|
|
def test_wrappers_are_seen_through(argv: list[str], command: list[str]) -> None:
|
|
assert unwrap(argv) == command
|
|
|
|
|
|
def test_paths_resolve_without_touching_the_filesystem() -> None:
|
|
assert resolve("a/../b", "/w") == "/w/b"
|
|
assert resolve("/etc/passwd", "/w") == "/etc/passwd"
|
|
assert resolve("$HOME/x", "/w") is None
|
|
assert resolve("~/x", "/w") is None
|
|
assert resolve("x", None) is None
|
|
assert inside("/w/b", "/w") and inside("/w", "/w/")
|
|
assert not inside("/wx", "/w") and not inside(None, "/w")
|
|
|
|
|
|
def judge(command: Command) -> Verdict:
|
|
"""A toy rule set: rm is dangerous outside /w, ls harmless."""
|
|
if command.argv[0] == "ls":
|
|
return Verdict(HARMLESS, "ls", "")
|
|
if command.argv[0] == "rm":
|
|
target = resolve(command.argv[-1], command.cwd)
|
|
if inside(target, "/w"):
|
|
return Verdict(CONTAINED, "rm-inside", "")
|
|
return Verdict(DANGEROUS, "rm-outside", "")
|
|
return Verdict(UNKNOWN, "no-rule", "")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("line", "rule"),
|
|
[
|
|
("ls && rm x", "rm-inside"),
|
|
("cd /tmp && rm x", "rm-outside"),
|
|
("cd sub && rm x", "rm-inside"),
|
|
("cd && rm x", "rm-outside"), # cd with no argument: home, unknown
|
|
("bash -c 'cd /etc; rm passwd'", "rm-outside"),
|
|
("sh -c 'ls'", "ls"),
|
|
("ls; frobnicate", "no-rule"),
|
|
("ls $(rm -rf /)", "unreadable"),
|
|
],
|
|
)
|
|
def test_a_line_is_as_bad_as_its_worst_command(line: str, rule: str) -> None:
|
|
assert classify_shell(line, "/w", judge).rule == rule
|