feat(branch): add pkgmgr branch tree, a hardened meta-tree of all repos
pkgmgr branch tree --format json|yaml --output FILE scans every known repository with gc's own classification (plan_repo with fetch=False) through the hardened git runner and writes one document atomically (flock, mkstemp, fsync, os.replace): per repo path, remote, default, missing, forge status, scanned_at, warnings and every branch and worktree item, plus orphan shared worktrees under ~/.local/share/worktrees. Forge facts take the token only from an unlocked Secret Service item, never unlocking, and cache per repository for 600 s. open, close, drop and gc --apply/purge refresh only their repository's entry and never overwrite a newer scan. Scan hardening for repositories an agent may write: log.showSignature=false joins the scan flags; status is skipped for local or worktree filter.* config and for worktrees with gitlinks; --end-of-options precedes every ref argument and -- every remote, branch and du path, and worktree list runs with -z. Before, a remote named --output=<path> made rev-list create that file on the host, and a worktree path with blank lines forged a record. MEDUSA acceptances (kvb-think-criticize 2026-11-07, 1 skeptic): EXA004 cli/parser/branch_cmd.py:68 by design (-f/--force help text on an operator CLI); B603 actions/branch/gc.py:146 false positive (docker argv list, no shell); B603 actions/branch/gc.py:311 fixed (du gets --, paths from worktree list -z); B603 core/git/run.py:83 by design (argv list, scan callers guard names). 🎯 Fix: 92 % 💥 Break: 4 % 💸 Cost: 104 % ⏱️ Time: 108 % 🛡️ Security: 100 % 🏆 Score: 79 🚦 Gate: ⚠️ cost 104 % > 100, time 108 % > 100: ask the operator before the commit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c2f0a56699
commit
54f0cbb37b
14 files changed
+1226
-88
No files matched your search
@@ -331,6 +331,54 @@ class TestBranchGc(unittest.TestCase):
|
||||
self.assertTrue(item.result.startswith("skipped: changed since plan"))
|
||||
self.assertTrue(os.path.isdir(self.merged_wt))
|
||||
|
||||
def test_worktree_path_with_newlines_cannot_forge_a_record(self) -> None:
|
||||
forged = os.path.join(self.root, "a\n\nworktree -evil")
|
||||
git(self.repo, "worktree", "add", "-b", "forged", forged, "main")
|
||||
|
||||
paths = {tree.path: tree.branch for tree in gc._worktrees(self.repo)}
|
||||
|
||||
self.assertEqual(paths[forged], "forged")
|
||||
self.assertNotIn("-evil", paths)
|
||||
|
||||
def test_ref_named_like_an_option_writes_no_file(self) -> None:
|
||||
target = os.path.join(self.root, "pwn")
|
||||
os.makedirs(target)
|
||||
remote = f"--output={target}"
|
||||
main = git(self.repo, "rev-parse", "main")
|
||||
git(self.repo, "remote", "remove", "origin")
|
||||
git(self.repo, "config", f"remote.{remote}.url", self.origin)
|
||||
git(self.repo, "update-ref", f"refs/remotes/{remote}/main", main)
|
||||
git(
|
||||
self.repo,
|
||||
"symbolic-ref",
|
||||
f"refs/remotes/{remote}/HEAD",
|
||||
f"refs/remotes/{remote}/main",
|
||||
)
|
||||
|
||||
plan = self._plan(host_ns=True, cwds={})
|
||||
|
||||
self.assertEqual(plan.default, f"{remote}/main")
|
||||
self.assertEqual(os.listdir(target), [])
|
||||
|
||||
def test_execute_deletes_branches_named_like_options(self) -> None:
|
||||
sha = git(self.repo, "rev-parse", "merged")
|
||||
other = os.path.join(self.root, "other.git")
|
||||
git(self.root, "init", "--bare", other)
|
||||
git(self.repo, "push", other, "merged:refs/heads/-evil")
|
||||
git(self.repo, "config", "remote.-r.url", other)
|
||||
git(self.repo, "update-ref", "refs/heads/-evil", sha)
|
||||
git(self.repo, "update-ref", "refs/remotes/-r/-evil", sha)
|
||||
plan = self._plan(host_ns=True, cwds={})
|
||||
item = self._items(plan)["-evil"]
|
||||
self.assertEqual(item.remotes, {"-r": sha})
|
||||
|
||||
with patch.object(gc, "_process_cwds", return_value={}):
|
||||
gc.execute(plan, item)
|
||||
|
||||
self.assertEqual(item.result, "done")
|
||||
self.assertEqual(git(self.repo, "for-each-ref", "refs/heads/-evil"), "")
|
||||
self.assertEqual(git(other, "for-each-ref", "refs/heads/-evil"), "")
|
||||
|
||||
def test_branch_gc_json_and_refusal(self) -> None:
|
||||
out = io.StringIO()
|
||||
with (
|
||||
|
||||
Reference in new issue
Block a user