CI / build (push) Has been cancelled
- Archive 79 completed framework-dev tasks from tasks/ -> tasks/complete/ - status.py: add --cleanup-done and --install-cleanup-schedule commands - Add scripts/automaton-cleanup.sh for periodic task archiving - Dashboard: rename 'Background' tab -> 'Agent', 'Cleanup' agent -> 'Completed Task Archiver', remove redundant group headers and pill badges, dim inactive agent placeholders - .rules.md: add Self-Documenting UI Names rule - New tests: test_cleanup_done.py, expanded test_app.py and test_task.py
42 lines
4.1 KiB
Markdown
42 lines
4.1 KiB
Markdown
# Adversarial Bug Report: add-status-brakes
|
|
|
|
Adversarial pass: attack the brakes layer as a hostile agent would, looking for ways to escape enforcement.
|
|
|
|
## Attack vectors tried
|
|
|
|
### A1 — Can a halted loop be silently resumed by re-running `--create-loop` with the same name?
|
|
`cmd_create_loop` refuses duplicates (exit 2). Attacker cannot overwrite a halted loop's state via create. Defended.
|
|
|
|
### A2 — Can a hostile harness skip `--check-gate` and just call `--transition`?
|
|
Not if the loop owns the task — R8 refuses the transition until `--approve --loop` clears the halt. Defended.
|
|
|
|
BUT: if the loop never `current_task`-claimed the task, `_loop_owning_task` returns None and the transition proceeds. The agent can edit a task that isn't claimed by any loop. That is correct behavior (humans and ad-hoc agents can still work), but it means a hostile agent could **race the loop runner to claim a task**. Mitigation: loop runner should call a `--claim-loop-task` (not in v1) or set `current_task` atomically before transitioning. **Not a brakes-layer bug** — it's a runner-layer concern (task 3). Note for task 3.
|
|
|
|
### A3 — Can `--pause-loop` be used to bypass the worktree drift gate?
|
|
No — pausing just stops the scheduler; `.state.loop` remains the source of truth. `--check-gate` still runs all gates; a paused loop's gates return `paused:` (not ok). The runner will not tick. Defended.
|
|
|
|
### A4 — Can the agent hand-write `.state.loop` to fake "running"?
|
|
Yes — `.state.loop` is a plain JSON file. A fully local adversary with filesystem access can edit it. This is an accepted threat model: the framework assumes the harness is cooperative for state files but enforces via the pre-edit hook (`--can-edit`) for source edits. **`.state.loop` is not in any task's file_scope**, so it's never editable by a loop agent. Defended by file-scope design.
|
|
|
|
### A5 — Race: two concurrent `--check-gate` invocations both halt the loop
|
|
Both call `_halt_loop` which uses atomic tmp+rename. Last writer wins. Both write the same halt_reason (deterministic from gate), so the result is consistent. No corruption. Defended.
|
|
|
|
### A6 — Can `--approve --loop` be called while the loop is mid-tick?
|
|
`--approve` does tmp+rename. If a tick is concurrently writing iteration_count, the approve's write wins and the tick's increment is lost. Window is small (subprocess boundary). Acceptable for v1; the next tick re-reads and re-increments. Not a corruption vector. **Note for v1.1:** file-locking (fcntl) on `.state.loop` would close this race. Add to BACKLOG.
|
|
|
|
### A7 — Can `--install-schedule` be pointed at a different project than the loop?
|
|
`--install-schedule` uses `_find_project_dir(args.project)` and writes the stub at `loop_path / run-tick.*`. The stub `cd`s into the project root and invokes the runner with the loop name. An attacker could swap the loop_name in the stub after generation, but that's just running an arbitrary loop — not a privilege escalation. Not an attack.
|
|
|
|
### A8 — Can the schedule wake the loop after it's halted?
|
|
Yes — the OS unit fires `run-tick` on schedule. `run-tick` invokes `loop-runner.py --mode tick --loop NAME`, which **must** call `--check-gate` first and exit 1 if not ok. The runner's contract (task 3) is: gate first, then work. The OS unit itself cannot refuse. So a halted loop's schedule will fire `run-tick`, which will no-op via the runner's gate check. The `--pause-loop` best-effort disable is belt-and-braces. Defended by runner contract (must be enforced in task 3).
|
|
|
|
## Hardening recommendations (for BACKLOG)
|
|
|
|
1. `fcntl` file-lock on `.state.loop` for tick/approve race (A6) — v1.1.
|
|
2. `--claim-loop-task` to atomically set `current_task` before a runner touches the task (A2) — task 3.
|
|
3. `_enable_schedule` Linux parity with Darwin/Windows (O4) — task 5 / v1.1.
|
|
4. `blast_radius.base_branch` parameterization for drift diff (O3) — task 5.
|
|
|
|
## Verdict
|
|
|
|
PASS — no exploitable escape from the brakes layer. All adversarial vectors are either defended today or have explicit runner-contract mitigations landing in tasks 3/5. Hardening items routed to `design/loops/BACKLOG.md`. |