security agent cleanup

This commit is contained in:
Roger Oriol
2026-07-26 21:07:47 +02:00
parent 0bb3b1c601
commit 7ff6bf858d
21 changed files with 206 additions and 195 deletions

View File

@@ -1,15 +1,15 @@
"""Session-level abort & kill switches (checklist §6.3).
"""Session-level abort & kill switches.
Three pieces:
1. ``AbortController`` a thread-safe flag set by a signal handler
1. ``AbortController``: a thread-safe flag set by a signal handler
(SIGINT / SIGTERM) or by any code path that detects a fatal
condition (cost cap, iteration cap, user request).
2. ``FileRollback`` snapshots original file bytes before each
2. ``FileRollback``: snapshots original file bytes before each
write/edit so that an abort can offer to revert reversible state.
3. ``kill_in_flight`` a helper that sends SIGINT to any python
3. ``kill_in_flight``: a helper that sends SIGINT to any python
process running inside the sandbox container, stopping a hanging
``docker exec`` without tearing down the container itself.
@@ -81,7 +81,7 @@ class AbortController:
"""Register SIGINT / SIGTERM handlers that call ``trigger``.
Only call this from the main thread (signal.signal requirement).
The previous handlers are not saved we deliberately replace
The previous handlers are not saved, we deliberately replace
them because the abort controller is the final arbiter.
"""
def _handler(signum, frame):
@@ -94,7 +94,7 @@ class AbortController:
self._registered_signals.append(sig)
except (ValueError, OSError):
# Not in main thread, or signal not supported on this
# platform skip silently.
# platform, skip silently.
pass
def remove_signal_handlers(self) -> None:
@@ -126,7 +126,7 @@ def kill_in_flight(container: str) -> None:
)
except Exception:
# Best-effort: if the container is already gone or pkill isn't
# available, the exec subprocess's own timeout (§4.3) will
# available, the exec subprocess's own timeout will
# eventually clean up.
pass
@@ -136,14 +136,14 @@ def kill_in_flight(container: str) -> None:
# ---------------------------------------------------------------------------
class FileRollback:
"""Snapshot original file bytes before each write/edit (§6.3).
"""Snapshot original file bytes before each write/edit.
On abort, ``offer_rollback`` walks the snapshot list and restores
each file to its pre-edit state, prompting the user for
confirmation.
Only ``write_file`` and ``edit_file`` are reversible; ``run_bash``
side effects (e.g. ``git commit``) are not the audit log is the
side effects (e.g. ``git commit``) are not, the audit log is the
only record for those.
"""
@@ -154,7 +154,8 @@ class FileRollback:
def _ensure_backup_dir(self) -> Path:
if self._backup_dir is None:
self._backup_dir = Path(__file__).resolve().parent / ".rollback_backups"
self._backup_dir = Path(
__file__).resolve().parent / ".rollback_backups"
self._backup_dir.mkdir(parents=True, exist_ok=True)
return self._backup_dir
@@ -171,7 +172,7 @@ class FileRollback:
shutil.copy2(p, backup)
self._snapshots.append((str(p.resolve()), backup))
except OSError:
# If we can't snapshot, we just can't roll back don't
# If we can't snapshot, we just can't roll back, don't
# block the tool call.
pass