from __future__ import annotations import json import os import socket import threading import uuid from pathlib import Path from codexpi.database import utcnow def fake_host_channel(path: Path, token: str): ready = threading.Event() def serve(): if path.exists(): path.unlink() with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server: server.bind(str(path)) server.listen(1) ready.set() connection, _ = server.accept() with connection: request = json.loads(connection.recv(100_000).split(b"\n", 1)[0]) assert request["token"] == token connection.sendall(json.dumps({"ok": True, "exit_code": 0, "answer": "Action hôte de test terminée."}).encode() + b"\n") thread = threading.Thread(target=serve, daemon=True) thread.start() ready.wait(2) return thread def test_master_agent_channel_is_separate_confirmed_and_audited(logged_in): client, app, _, _, headers = logged_in base = app.state.settings.data_dir / "host-agent" base.mkdir() token_file, socket_path = base / "agent.token", base / "agent.sock" token = "fixture-token-which-is-longer-than-thirty-two-characters" token_file.write_text(token, encoding="utf-8") os.chmod(token_file, 0o600) thread = fake_host_channel(socket_path, token) created = client.post( "/api/master-agent/sessions", headers=headers, json={"title": "Maintenance de test", "privileged": True}, ) assert created.status_code == 201 session_id = created.json()["id"] refused = client.post( f"/api/master-agent/sessions/{session_id}/messages", headers=headers, json={"prompt": "Effectuer une action réversible de test.", "acknowledge_privileged": False}, ) assert refused.status_code == 409 accepted = client.post( f"/api/master-agent/sessions/{session_id}/messages", headers=headers, json={"prompt": "Effectuer une action réversible de test.", "acknowledge_privileged": True}, ) assert accepted.status_code == 200, accepted.text assert accepted.json()["answer"] == "Action hôte de test terminée." thread.join(2) history = client.get("/api/master-agent").json()["sessions"] assert [message["role"] for message in history[0]["messages"]] == ["user", "assistant"] def test_project_user_cannot_access_master_agent(logged_in): client, app, _, _, headers = logged_in app.state.auth.create_user("member", "a-different-long-test-password", is_admin=False) client.post("/api/auth/logout", headers=headers) login = client.post("/api/auth/login", json={"username": "member", "password": "a-different-long-test-password"}).json() member_headers = {"X-CSRF-Token": login["csrf_token"]} assert client.get("/api/master-agent").status_code == 403 assert client.post("/api/master-agent/sessions", headers=member_headers, json={"title": "No", "privileged": False}).status_code == 403 def test_additional_admin_is_not_the_host_owner(logged_in): client, app, _, _, headers = logged_in app.state.auth.create_user("other-admin", "another-long-administrator-password", is_admin=True) client.post("/api/auth/logout", headers=headers) login = client.post("/api/auth/login", json={"username": "other-admin", "password": "another-long-administrator-password"}).json() admin_headers = {"X-CSRF-Token": login["csrf_token"]} assert client.get("/api/master-agent").status_code == 403 assert client.post("/api/master-agent/sessions", headers=admin_headers, json={"title": "No", "privileged": False}).status_code == 403 def test_additional_admin_cannot_queue_the_privileged_pi_doctor(logged_in): client, app, projects, _, headers = logged_in owner_id = str(app.state.db.one("SELECT id FROM users LIMIT 1")["id"]) project_id = str(uuid.uuid4()) conversation_id = str(uuid.uuid4()) root = projects / "pi-doctor" root.mkdir() stamp = utcnow() app.state.db.execute( "INSERT INTO projects(id,name,path,owner_id,full_access,execution_group,created_at) VALUES(?,?,?,?,?,?,?)", (project_id, "Pi Doctor", str(root), owner_id, 1, "__global_agent__", stamp), ) app.state.db.execute( "INSERT INTO conversations(id,project_id,user_id,title,status,created_at,updated_at) VALUES(?,?,?,?,?,?,?)", (conversation_id, project_id, owner_id, "Host", "idle", stamp, stamp), ) app.state.auth.create_user("other-host-admin", "another-long-administrator-password", is_admin=True) client.post("/api/auth/logout", headers=headers) login = client.post( "/api/auth/login", json={"username": "other-host-admin", "password": "another-long-administrator-password"}, ).json() response = client.post( f"/api/conversations/{conversation_id}/queue", headers={"X-CSRF-Token": login["csrf_token"]}, json={"content": "Inspecter l’hôte.", "attachment_ids": []}, ) assert response.status_code == 403