Fix 10 audit bugs: path prefix matching, verdict parsing, CORS, stale-task detection, phase mapping
CI / build (push) Has been cancelled
CI / build (push) Has been cancelled
Batch 1 (High severity): - Bug 1: --audit cat3 now checks .automaton/tasks/ paths - Bug 4: Verdict PASS/FAIL uses structured ## Status: line parsing - Bug 5: register-guards.sh checks .json/.jsonc, writes plugin key, strips comments - Bug 7: --can-edit/--scope-check path prefix uses os.sep boundary Batch 2 (Medium/Low severity): - Bug 2: migrate-project.sh find command parentheses for -prune binding - Bug 3: vram_detect model prefix matching with known-suffix whitelist - Bug 6: dashboard reads .state file before artifact heuristic fallback - Bug 8: removed wildcard CORS, added security headers (nosniff, DENY) - Bug 9: stale-task detection uses .state.lastedit instead of .state mtime - Bug 10: TEST_PLAN.md maps to test_design (was implement) 249 tests pass (up from 235). All 10 tasks driven through full workflow to completion.
This commit is contained in:
+191
-1
@@ -572,4 +572,194 @@ class TestCodeReviewListStates:
|
||||
assert code == 0
|
||||
assert "code_review * requires approval" in out
|
||||
assert "code_review:awaiting_approval" in out
|
||||
assert "code_review:approved" in out
|
||||
assert "code_review:approved" in out
|
||||
|
||||
|
||||
class TestCat3AuditRegularProjectPaths:
|
||||
"""Bug 1: Category 3 audit must recognize .automaton/tasks/ paths for regular projects."""
|
||||
|
||||
def test_regular_project_task_path_not_flagged(self, tmp_project):
|
||||
"""Files inside .automaton/tasks/ in a regular project should NOT be flagged as unauthorized."""
|
||||
import subprocess
|
||||
task_dir = _create_task(tmp_project, "edit-task", "implement")
|
||||
# Simulate a change inside the task folder
|
||||
(task_dir / "IMPLEMENTATION.md").write_text("# Impl")
|
||||
subprocess.run(["git", "init"], cwd=str(tmp_project), capture_output=True)
|
||||
subprocess.run(["git", "add", "-A"], cwd=str(tmp_project), capture_output=True)
|
||||
subprocess.run(["git", "commit", "-m", "init"], cwd=str(tmp_project), capture_output=True)
|
||||
# Make a change inside task folder
|
||||
(task_dir / "IMPLEMENTATION.md").write_text("# Updated Impl")
|
||||
out, code = _run_status(["--audit"], tmp_project)
|
||||
# The task file should NOT appear as unauthorized
|
||||
assert ".automaton/tasks/edit-task/IMPLEMENTATION.md" not in out or "[PASS]" in out
|
||||
|
||||
|
||||
class TestVerdictPassInference:
|
||||
"""Bug 4: _infer_state_from_artifacts must not use substring 'PASS' search."""
|
||||
|
||||
def test_fail_verdict_with_pass_in_body_is_human_intervention(self, tmp_project):
|
||||
"""A FAIL verdict mentioning 'PASS' in body must be human_intervention, not complete."""
|
||||
task_dir = tmp_project / ".automaton" / "tasks" / "verdict-fail-pass"
|
||||
task_dir.mkdir(parents=True)
|
||||
(task_dir / "VERDICT.md").write_text(
|
||||
"# Verdict\n## Status: FAIL\n\nAll unit tests PASS but spec is not met.\n"
|
||||
)
|
||||
out, code = _run_status(["--upgrade", "--task", "verdict-fail-pass"], tmp_project)
|
||||
assert code == 0
|
||||
state = (task_dir / ".state").read_text().strip()
|
||||
assert state == "human_intervention", f"Expected human_intervention, got {state}"
|
||||
|
||||
def test_pass_verdict_is_complete(self, tmp_project):
|
||||
"""A PASS verdict with ## Status: PASS should be complete."""
|
||||
task_dir = tmp_project / ".automaton" / "tasks" / "verdict-pass"
|
||||
task_dir.mkdir(parents=True)
|
||||
(task_dir / "VERDICT.md").write_text("# Verdict\n## Status: PASS\n\nAll good.\n")
|
||||
out, code = _run_status(["--upgrade", "--task", "verdict-pass"], tmp_project)
|
||||
assert code == 0
|
||||
state = (task_dir / ".state").read_text().strip()
|
||||
assert state == "complete", f"Expected complete, got {state}"
|
||||
|
||||
def test_needs_review_verdict_is_human_intervention(self, tmp_project):
|
||||
"""A NEEDS_REVIEW verdict should be human_intervention."""
|
||||
task_dir = tmp_project / ".automaton" / "tasks" / "verdict-nr"
|
||||
task_dir.mkdir(parents=True)
|
||||
(task_dir / "VERDICT.md").write_text("# Verdict\n## Status: NEEDS_REVIEW\n\nNeeds manual review.\n")
|
||||
out, code = _run_status(["--upgrade", "--task", "verdict-nr"], tmp_project)
|
||||
assert code == 0
|
||||
state = (task_dir / ".state").read_text().strip()
|
||||
assert state == "human_intervention", f"Expected human_intervention, got {state}"
|
||||
|
||||
|
||||
class TestCanEditPathPrefix:
|
||||
"""Bug 7: --can-edit and --scope-check must not match sibling directories."""
|
||||
|
||||
def test_scope_check_rejects_sibling_directory(self, tmp_project):
|
||||
"""A file in a sibling directory (e.g. project-evil) must be OUT_OF_SCOPE."""
|
||||
_create_task(tmp_project, "scope-sibling", "implement")
|
||||
sibling = tmp_project.parent / (tmp_project.name + "-evil")
|
||||
sibling.mkdir(exist_ok=True)
|
||||
evil_file = sibling / "file.py"
|
||||
evil_file.write_text("# evil")
|
||||
out, code = _run_status(
|
||||
["--scope-check", "--task", "scope-sibling", "--file", str(evil_file)],
|
||||
tmp_project,
|
||||
)
|
||||
assert code == 1
|
||||
assert "OUT_OF_SCOPE" in out
|
||||
|
||||
def test_scope_check_accepts_subdirectory(self, tmp_project):
|
||||
"""A file inside the project directory should be IN_SCOPE."""
|
||||
_create_task(tmp_project, "scope-sub", "implement")
|
||||
sub = tmp_project / "subdir"
|
||||
sub.mkdir()
|
||||
test_file = sub / "file.py"
|
||||
test_file.write_text("# ok")
|
||||
out, code = _run_status(
|
||||
["--scope-check", "--task", "scope-sub", "--file", str(test_file)],
|
||||
tmp_project,
|
||||
)
|
||||
assert code == 0
|
||||
assert "IN_SCOPE" in out
|
||||
|
||||
def test_can_edit_task_rejects_sibling_directory(self, tmp_project):
|
||||
"""--can-edit --task --file must reject files in sibling directories."""
|
||||
task_dir = _create_task(tmp_project, "edit-sibling", "implement")
|
||||
sibling = tmp_project.parent / (tmp_project.name + "-evil")
|
||||
sibling.mkdir(exist_ok=True)
|
||||
evil_file = sibling / "file.py"
|
||||
evil_file.write_text("# evil")
|
||||
out, code = _run_status(
|
||||
["--can-edit", "--task", "edit-sibling", "--file", str(evil_file)],
|
||||
tmp_project,
|
||||
)
|
||||
assert code == 1
|
||||
assert "OUT_OF_SCOPE" in out
|
||||
|
||||
|
||||
class TestStateLastEdit:
|
||||
"""Bug 9: --can-edit should use .state.lastedit for staleness, not .state mtime."""
|
||||
|
||||
def test_can_edit_creates_lastedit_on_allowed(self, tmp_project):
|
||||
"""ALLOWED response should create .state.lastedit file."""
|
||||
task_dir = _create_task(tmp_project, "lastedit-create", "implement")
|
||||
assert not (task_dir / ".state.lastedit").exists()
|
||||
out, code = _run_status(["--can-edit", "--task", "lastedit-create"], tmp_project)
|
||||
assert code == 0
|
||||
assert "ALLOWED" in out
|
||||
assert (task_dir / ".state.lastedit").exists()
|
||||
|
||||
def test_can_edit_creates_lastedit_with_file_scope(self, tmp_project):
|
||||
"""ALLOWED with --file should create .state.lastedit for primary task."""
|
||||
task_dir = _create_task(tmp_project, "lastedit-file", "implement")
|
||||
test_file = tmp_project / "src" / "main.py"
|
||||
test_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
test_file.write_text("# test")
|
||||
assert not (task_dir / ".state.lastedit").exists()
|
||||
out, code = _run_status(
|
||||
["--can-edit", "--file", str(test_file)],
|
||||
tmp_project,
|
||||
)
|
||||
assert code == 0
|
||||
assert "ALLOWED" in out
|
||||
assert (task_dir / ".state.lastedit").exists()
|
||||
|
||||
def test_stale_uses_lastedit_not_state_mtime(self, tmp_project):
|
||||
"""If .state is old but .state.lastedit is recent, task should not be stale."""
|
||||
import time
|
||||
task_dir = _create_task(tmp_project, "stale-lastedit", "implement")
|
||||
# Make .state old (31 minutes ago)
|
||||
state_file = task_dir / ".state"
|
||||
old_time = time.time() - 31 * 60
|
||||
os.utime(state_file, (old_time, old_time))
|
||||
# Make .state.lastedit recent (1 minute ago)
|
||||
lastedit = task_dir / ".state.lastedit"
|
||||
lastedit.touch()
|
||||
recent_time = time.time() - 60
|
||||
os.utime(lastedit, (recent_time, recent_time))
|
||||
out, code = _run_status(["--can-edit", "--task", "stale-lastedit"], tmp_project)
|
||||
assert code == 0
|
||||
assert "ALLOWED" in out
|
||||
|
||||
def test_stale_when_lastedit_old(self, tmp_project):
|
||||
"""If .state.lastedit is old, task should be denied as stale."""
|
||||
import time
|
||||
task_dir = _create_task(tmp_project, "stale-old", "implement")
|
||||
# Make .state recent (1 minute ago)
|
||||
state_file = task_dir / ".state"
|
||||
recent_time = time.time() - 60
|
||||
os.utime(state_file, (recent_time, recent_time))
|
||||
# Make .state.lastedit old (31 minutes ago)
|
||||
lastedit = task_dir / ".state.lastedit"
|
||||
lastedit.touch()
|
||||
old_time = time.time() - 31 * 60
|
||||
os.utime(lastedit, (old_time, old_time))
|
||||
out, code = _run_status(["--can-edit", "--task", "stale-old"], tmp_project)
|
||||
assert code == 1
|
||||
assert "stale" in out.lower()
|
||||
|
||||
def test_falls_back_to_state_mtime_without_lastedit(self, tmp_project):
|
||||
"""Without .state.lastedit, should fall back to .state mtime (backward compat)."""
|
||||
import time
|
||||
task_dir = _create_task(tmp_project, "fallback-state", "implement")
|
||||
# No .state.lastedit — .state is 31 minutes old
|
||||
state_file = task_dir / ".state"
|
||||
old_time = time.time() - 31 * 60
|
||||
os.utime(state_file, (old_time, old_time))
|
||||
out, code = _run_status(["--can-edit", "--task", "fallback-state"], tmp_project)
|
||||
assert code == 1
|
||||
assert "stale" in out.lower()
|
||||
|
||||
|
||||
class TestTestPlanPhaseMapping:
|
||||
"""Bug 10: TEST_PLAN.md should map to test_design, not implement."""
|
||||
|
||||
def test_test_plan_maps_to_test_design(self, tmp_project):
|
||||
"""--upgrade should infer test_design when TEST_PLAN.md exists (no .state)."""
|
||||
task_dir = tmp_project / ".automaton" / "tasks" / "testplan-infer"
|
||||
task_dir.mkdir(parents=True)
|
||||
(task_dir / "SPEC.md").write_text("# Spec")
|
||||
(task_dir / "TEST_PLAN.md").write_text("# Tests")
|
||||
out, code = _run_status(["--upgrade", "--task", "testplan-infer"], tmp_project)
|
||||
assert code == 0
|
||||
assert "test_design" in out
|
||||
assert (task_dir / ".state").read_text().strip() == "test_design"
|
||||
Reference in New Issue
Block a user