fix(orchestrator): ship no bytecode cache from the repository into the install
install copied the gitignored __pycache__ directories of the sandbox-writable tree into ~/.local/share/kvb-orchestrator with cp -a. Python loads a cached .pyc whose header matches the source mtime and size, and cp -a keeps the mtime, so a .pyc planted by a sandboxed agent would have run in the host runner and PermissionRequest hook without showing in any diff. install now removes every __pycache__ right after the copy; the compose build reads the same cleaned directory. 🎯 Fix: 95 % 💥 Break: 2 % 💸 Cost: – ⏱️ Time: – 🛡️ Security: 100 % 🏆 Score: 93 🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f940b4415
commit
de574d5c3d
2 files changed
+10
No files matched your search
@@ -140,6 +140,8 @@ install_hub() {
|
|||||||
mkdir -p "${HUB}" "${STATE}" "${APPROVALS}" "${COUNCIL}" "${HOME}/.claude/projects" "${HOME}/Repositories" "${HOME}/.local/share/worktrees" "${UNITS}" "${APPROVERS}"
|
mkdir -p "${HUB}" "${STATE}" "${APPROVALS}" "${COUNCIL}" "${HOME}/.claude/projects" "${HOME}/Repositories" "${HOME}/.local/share/worktrees" "${UNITS}" "${APPROVERS}"
|
||||||
chmod 700 "${APPROVERS}"
|
chmod 700 "${APPROVERS}"
|
||||||
cp -a "${REPO_ROOT}/orchestrator/." "${HUB}/"
|
cp -a "${REPO_ROOT}/orchestrator/." "${HUB}/"
|
||||||
|
# Python loads a cached .pyc whose header matches the source mtime and size, and cp -a keeps the mtime: one planted in the sandbox-writable tree would run on the host
|
||||||
|
find "${HUB}" -name __pycache__ -type d -prune -exec rm -rf {} +
|
||||||
chmod +x "${HUB}/client/permission_hook.py" "${HUB}/client/runner.py" "${HUB}/client/approval.py"
|
chmod +x "${HUB}/client/permission_hook.py" "${HUB}/client/runner.py" "${HUB}/client/approval.py"
|
||||||
log "skills: the PermissionRequest hook client is ${HUB}/client/permission_hook.py"
|
log "skills: the PermissionRequest hook client is ${HUB}/client/permission_hook.py"
|
||||||
log "skills: approvals need a pinned passkey: settings, 'Passkey registrieren', then 'make orchestrator-pin CREDENTIAL=<blob>'; until then every executor refuses"
|
log "skills: approvals need a pinned passkey: settings, 'Passkey registrieren', then 'make orchestrator-pin CREDENTIAL=<blob>'; until then every executor refuses"
|
||||||
|
|||||||
@@ -137,6 +137,14 @@ class TestOrchestratorMigration(unittest.TestCase):
|
|||||||
legacy_calls = [call for call in second["calls"][len(first["calls"]) :] if any(OLD in arg for arg in call)]
|
legacy_calls = [call for call in second["calls"][len(first["calls"]) :] if any(OLD in arg for arg in call)]
|
||||||
self.assertEqual(legacy_calls, [["docker", "image", "inspect", f"{OLD}:local"], ["claude", "mcp", "get", OLD]])
|
self.assertEqual(legacy_calls, [["docker", "image", "inspect", f"{OLD}:local"], ["claude", "mcp", "get", OLD]])
|
||||||
|
|
||||||
|
def test_install_ships_no_bytecode_from_the_tree(self):
|
||||||
|
planted = REPO_ROOT / "orchestrator" / "client" / "__pycache__" / "planted.cpython-0.pyc"
|
||||||
|
planted.parent.mkdir(exist_ok=True)
|
||||||
|
planted.write_bytes(b"planted")
|
||||||
|
self.addCleanup(planted.unlink, True)
|
||||||
|
self.install()
|
||||||
|
self.assertEqual(sorted((self.home / ".local" / "share" / NEW).rglob("__pycache__")), [])
|
||||||
|
|
||||||
def test_old_names_live_only_in_the_migration(self):
|
def test_old_names_live_only_in_the_migration(self):
|
||||||
done = subprocess.run(["git", "grep", "-niE", "kvb[-_]hub", "--", ".", ":!docs/requirements", f":!{Path(__file__).relative_to(REPO_ROOT)}"], cwd=REPO_ROOT, capture_output=True, text=True, check=False)
|
done = subprocess.run(["git", "grep", "-niE", "kvb[-_]hub", "--", ".", ":!docs/requirements", f":!{Path(__file__).relative_to(REPO_ROOT)}"], cwd=REPO_ROOT, capture_output=True, text=True, check=False)
|
||||||
hits = sorted(line.split(":", 2)[0] + ":" + line.split(":", 2)[2].strip() for line in done.stdout.splitlines())
|
hits = sorted(line.split(":", 2)[0] + ":" + line.split(":", 2)[2].strip() for line in done.stdout.splitlines())
|
||||||
|
|||||||
Reference in new issue
Block a user