fix(kvb-org-operator): run no repo-configured command before the operator's yes
Agents name the directories of operator rows and can write the repos behind them. Before the operator answered, list and run started git there unsandboxed, and git ran commands the repo config names: core.fsmonitor, the post-index-change hook on an index refresh, filter clean drivers, the pager, external diff and textconv drivers, gpg through log.showSignature, and a lazy fetch of a missing object that runs remote uploadpack through sh. Preview output and task names also reached the terminal raw, so escape sequences could overwrite the displayed command. Every git call before the yes now runs as git -c core.fsmonitor=false -c core.hooksPath=/dev/null -c log.showSignature=false --no-pager --no-optional-locks with GIT_NO_LAZY_FETCH=1, diffs add --no-ext-diff --no-textconv --ignore-submodules=all, a repo whose config names filter commands gets no working-tree preview, and preview output, task, check, table cells and listed commands go through visible(). The approved command itself still runs through $SHELL -ic unchanged. Found by a skeptic while scanning the color change; each hole was reproduced red before the fix. Accepted MEDUSA false positives after kvb-think-criticize (rows in security-accepted.jsonl): MEDUSA-MM-SCAN-058 x3, MEDUSA-GENAI-SCAN-1270 x4, MEDUSA-WM-SCAN-354, MEDUSA-CGS-SCAN-040. 🎯 Fix: 90 % 💥 Break: 3 % 💸 Cost: – ⏱️ Time: – 🛡️ Security: 100 % 🏆 Score: 87 🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 % ``` /home/kevinveenbirkenbach/Repositories/github.com/kevinveenbirkenbach/skills/.worktrees/feature/kvb-org-operator-run-color ``` Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
57ab29eb0d
commit
85462df5ab
2 files changed
+88
-23
No files matched your search
+50
-1
@@ -3,10 +3,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
@@ -234,11 +236,58 @@ class TestOperator(unittest.TestCase):
|
||||
self._add("push", repo, "false", "branch ahead", f"pushed:{repo}")
|
||||
result = self._feed("y\n")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("$ git --no-pager log --oneline '@{u}..HEAD'", result.stdout)
|
||||
self.assertIn("--no-pager --no-optional-locks log --oneline '@{u}..HEAD'", result.stdout)
|
||||
self.assertRegex(result.stdout, r"[0-9a-f]{7,} two")
|
||||
self.assertIn("failed operator/push: exit 1; the row stays open", result.stdout)
|
||||
self.assertEqual(self._open(), ["operator/push"])
|
||||
|
||||
def test_run_preview_runs_no_command_a_repo_config_names(self):
|
||||
marker = self.base / "ran"
|
||||
trap = self._repo("trap", upstream=False)
|
||||
(trap / "keep").write_text("k", encoding="utf-8")
|
||||
self._git(trap, "add", "keep")
|
||||
self._git(trap, "commit", "-qm", "keep")
|
||||
self._git(trap, "config", "core.fsmonitor", f"touch {marker}")
|
||||
self._git(trap, "config", "diff.external", f"touch {marker}")
|
||||
hook = trap / ".git" / "hooks" / "post-index-change"
|
||||
hook.write_text(f"#!/bin/sh\ntouch {marker}\n", encoding="utf-8")
|
||||
hook.chmod(0o755)
|
||||
future = time.time() + 1000
|
||||
os.utime(trap / "keep", (future, future))
|
||||
filtered = self._repo("filtered", upstream=False)
|
||||
self._git(filtered, "config", "filter.x.clean", f"touch {marker}")
|
||||
(filtered / ".gitattributes").write_text("* filter=x\n", encoding="utf-8")
|
||||
for repo in (trap, filtered):
|
||||
(repo / "one").write_text("changed", encoding="utf-8")
|
||||
self._add(repo.name, repo, "true", "r", "manual")
|
||||
result = self._feed("d\ns\nd\ns\n")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn(" M one", result.stdout)
|
||||
self.assertIn("+changed", result.stdout)
|
||||
self.assertNotIn("?? 2", result.stdout)
|
||||
self.assertFalse(marker.exists())
|
||||
|
||||
def test_list_and_preview_never_fetch_a_missing_object(self):
|
||||
marker = self.base / "fetched"
|
||||
repo = self._repo("lazy", upstream=True)
|
||||
self._git(repo, "config", "remote.origin.promisor", "true")
|
||||
self._git(repo, "config", "remote.origin.uploadpack", f"touch {marker}; git-upload-pack")
|
||||
(repo / ".git" / "refs" / "remotes" / "origin" / "main").write_text("1" * 40 + "\n", encoding="utf-8")
|
||||
self._add("lazy", repo, "true", "r", f"pushed:{repo}")
|
||||
self._out("list")
|
||||
self.assertEqual(self._feed("s\n").returncode, 0)
|
||||
self.assertFalse(marker.exists())
|
||||
|
||||
def test_run_shows_control_characters_of_the_preview(self):
|
||||
repo = self._repo("spoof", upstream=True)
|
||||
(repo / "three").write_text("3", encoding="utf-8")
|
||||
self._git(repo, "add", "three")
|
||||
self._git(repo, "commit", "-qm", "x\x1b[1A\x1b[2Kfake")
|
||||
self._add("spoof", repo, "true", "r", f"pushed:{repo}")
|
||||
out = self._feed("s\n").stdout
|
||||
self.assertNotIn("\x1b", out)
|
||||
self.assertIn("x\\x1b[1A\\x1b[2Kfake", out)
|
||||
|
||||
def test_decision_rows_stay_out_of_blocks_and_run(self):
|
||||
self._add("ask", self.base, "decide: keep fix B", "operator only", "decision")
|
||||
self._add("go", self.base, "true", "runnable", "manual")
|
||||
|
||||
Reference in new issue
Block a user