mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-04 07:20:10 +00:00
188 lines
7.3 KiB
Python
188 lines
7.3 KiB
Python
"""Agent-authored / downloaded-then-executed provenance (OPE-114 §1).
|
|
|
|
The reviewer never sees file contents, so `python scripts/setup.py` cannot be judged from
|
|
its text. These cover the one fact that makes it judgeable: did the agent itself create
|
|
that file, and how long ago.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from coworker import provenance as prov
|
|
from coworker.provenance import DOWNLOADED, WRITTEN, SessionFiles
|
|
|
|
|
|
@pytest.fixture()
|
|
def files(tmp_path) -> SessionFiles:
|
|
return SessionFiles(tmp_path)
|
|
|
|
|
|
def _wrote(files: SessionFiles, path: str, step: int) -> None:
|
|
files.record("write_file", {"path": path, "content": "x"}, {"ok": True}, step=step)
|
|
|
|
|
|
# -- the core case -------------------------------------------------------------
|
|
def test_agent_written_script_is_flagged_when_run(files):
|
|
_wrote(files, "scripts/setup.py", 12)
|
|
match = files.match("run_shell", {"command": "python scripts/setup.py"}, step=15)
|
|
assert match is not None
|
|
assert match.render() == "scripts/setup.py was created by the agent 3 steps ago"
|
|
assert not match.downloaded
|
|
|
|
|
|
def test_pre_existing_file_produces_no_fact(files):
|
|
_wrote(files, "scripts/setup.py", 12)
|
|
# A file this session never created is invisible — no fact, no extra caution.
|
|
assert files.match("run_shell", {"command": "python tools/other.py"}, step=15) is None
|
|
|
|
|
|
def test_step_distance_reads_naturally(files):
|
|
_wrote(files, "a.py", 5)
|
|
assert "just now" in files.match("run_shell", {"command": "python a.py"}, step=5).render()
|
|
assert "1 step ago" in files.match("run_shell", {"command": "python a.py"}, step=6).render()
|
|
|
|
|
|
# -- path normalisation --------------------------------------------------------
|
|
@pytest.mark.parametrize("spelling", ["a.py", "./a.py", "sub/../a.py"])
|
|
def test_one_file_one_key_however_it_is_spelled(files, tmp_path, spelling):
|
|
_wrote(files, "a.py", 3)
|
|
assert files.match("run_shell", {"command": f"python {spelling}"}, step=4) is not None
|
|
|
|
|
|
def test_absolute_spelling_matches_the_relative_write(files, tmp_path):
|
|
_wrote(files, "a.py", 3)
|
|
absolute = (tmp_path / "a.py").as_posix()
|
|
assert files.match("run_shell", {"command": f"python {absolute}"}, step=4) is not None
|
|
|
|
|
|
# -- what counts as "created" --------------------------------------------------
|
|
def test_patch_and_diff_writes_register(files):
|
|
# Exercises the existing blob parsing in write_paths(): a patch names its files in the
|
|
# body, not in a path argument.
|
|
files.record(
|
|
"apply_patch",
|
|
{"patch": "*** Begin Patch\n*** Update File: build.sh\n*** End Patch"},
|
|
{"ok": True},
|
|
step=2,
|
|
)
|
|
assert files.match("run_shell", {"command": "bash build.sh"}, step=3) is not None
|
|
|
|
|
|
def test_failed_calls_are_never_recorded(files):
|
|
# The engine only records on status == "ok"; a write that raised left nothing to run.
|
|
# Guard the contract at the call site the engine relies on.
|
|
paths, origin = prov.created_paths("write_file", {"path": "a.py"}, {"ok": True})
|
|
assert (paths, origin) == (["a.py"], WRITTEN)
|
|
|
|
|
|
def test_reads_and_unrelated_tools_create_nothing():
|
|
assert prov.created_paths("read_file", {"path": "a.py"}, {"ok": True}) == ([], "")
|
|
assert prov.created_paths("ask_user", {"question": "?"}, None) == ([], "")
|
|
|
|
|
|
# -- downloads -----------------------------------------------------------------
|
|
@pytest.mark.parametrize(
|
|
"command,expected",
|
|
[
|
|
("curl -o tool.sh https://x.io/a", ["tool.sh"]),
|
|
("curl -O https://x.io/a/tool.sh", ["tool.sh"]),
|
|
("wget -O tool.sh https://x.io/a", ["tool.sh"]),
|
|
("Invoke-WebRequest https://x.io/a -OutFile tool.ps1", ["tool.ps1"]),
|
|
],
|
|
)
|
|
def test_shell_fetchers_record_their_output_path(command, expected):
|
|
assert prov.created_paths("run_shell", {"command": command}, None) == (
|
|
expected,
|
|
DOWNLOADED,
|
|
)
|
|
|
|
|
|
def test_curl_capital_o_does_not_swallow_the_url():
|
|
# `-o FILE` and `-O` are different curl flags; folding their case would record the URL
|
|
# itself as the downloaded file.
|
|
paths, _ = prov.created_paths(
|
|
"run_shell", {"command": "curl -O https://x.io/a/tool.sh"}, None
|
|
)
|
|
assert paths == ["tool.sh"]
|
|
|
|
|
|
def test_plain_fetch_without_an_output_flag_records_nothing():
|
|
assert prov.created_paths("run_shell", {"command": "curl https://x.io/a"}, None) == (
|
|
[],
|
|
"",
|
|
)
|
|
|
|
|
|
def test_tools_that_report_their_target_in_the_result(files):
|
|
files.record("github_clone", {"owner": "o", "repo": "r"}, {"ok": True, "path": "vendor/r"}, step=4)
|
|
match = files.match("run_shell", {"command": "bash vendor/r/install.sh"}, step=5)
|
|
assert match is None # a file INSIDE the clone is not the clone itself
|
|
match = files.match("run_shell", {"command": "bash vendor/r"}, step=5)
|
|
assert match is not None and match.downloaded
|
|
|
|
|
|
# -- command parsing -----------------------------------------------------------
|
|
@pytest.mark.parametrize(
|
|
"command,target",
|
|
[
|
|
("python scripts/setup.py", "scripts/setup.py"),
|
|
("bash deploy.sh", "deploy.sh"),
|
|
("./run.sh", "./run.sh"),
|
|
("node build.js", "build.js"),
|
|
("cd /tmp && python scripts/setup.py", "scripts/setup.py"),
|
|
],
|
|
)
|
|
def test_paths_are_found_wherever_they_sit_in_the_command(command, target):
|
|
assert target in prov.command_paths(command)
|
|
|
|
|
|
def test_flags_and_urls_are_not_paths():
|
|
found = prov.command_paths("curl --silent https://x.io/a.py")
|
|
assert "--silent" not in found and "https://x.io/a.py" not in found
|
|
|
|
|
|
def test_implicit_targets_are_checked(files):
|
|
# `make deploy` never names the Makefile, so without the table it would look like it
|
|
# touches nothing.
|
|
_wrote(files, "Makefile", 2)
|
|
assert files.match("run_shell", {"command": "make deploy"}, step=3) is not None
|
|
_wrote(files, "package.json", 4)
|
|
assert files.match("run_shell", {"command": "npm run build"}, step=5) is not None
|
|
|
|
|
|
def test_newest_creation_wins(files):
|
|
_wrote(files, "a.py", 2)
|
|
_wrote(files, "b.py", 9)
|
|
match = files.match("run_shell", {"command": "python a.py && python b.py"}, step=10)
|
|
assert match.path == "b.py" # the one whose contents the agent controlled most recently
|
|
|
|
|
|
def test_download_over_a_written_path_becomes_a_download(files):
|
|
_wrote(files, "tool.sh", 2)
|
|
files.record("run_shell", {"command": "curl -o tool.sh https://x.io/a"}, None, step=6)
|
|
assert files.match("run_shell", {"command": "bash tool.sh"}, step=7).downloaded
|
|
|
|
|
|
# -- the documented blind spot -------------------------------------------------
|
|
def test_transitive_imports_are_a_known_miss(files):
|
|
# The agent writes a helper and runs a pre-existing entry point that imports it. No
|
|
# cheap analysis finds this; the limit is asserted so it stays a known gap rather than
|
|
# an assumed capability.
|
|
_wrote(files, "helper.py", 3)
|
|
assert files.match("run_shell", {"command": "python main.py"}, step=4) is None
|
|
|
|
|
|
# -- the rendered line ---------------------------------------------------------
|
|
def test_fact_is_fixed_vocabulary_and_never_carries_content(files):
|
|
files.record(
|
|
"write_file",
|
|
{"path": "a.py", "content": "SECRET_TOKEN = 'sk-live-abc123'"},
|
|
{"ok": True},
|
|
step=1,
|
|
)
|
|
rendered = files.match("run_shell", {"command": "python a.py"}, step=2).render()
|
|
assert "sk-live" not in rendered and "SECRET_TOKEN" not in rendered
|
|
assert rendered == "a.py was created by the agent 1 step ago"
|