From f68d7cbc324770e07f66dbb32f0f9836722bdbf2 Mon Sep 17 00:00:00 2001 From: StanPonomarev Date: Sun, 19 Jul 2026 18:38:38 +0200 Subject: [PATCH] Remove command permission lookup --- README.md | 7 +++---- src/agentci/adapters/gitea.py | 7 ------- src/agentci/api/webhook.py | 13 ------------- tests/test_webhook.py | 27 +++------------------------ 4 files changed, 6 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 83ebc60..41b8d37 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ one persistent Docker Compose service on the same Docker network as Gitea. | PR | `/agent iterate [message]` | Resume an agent implementation and its reviewer once. | | PR | `/agent fix [message]` | Start a fresh one-shot fix session and push one commit. | -Only repository writers and administrators can enqueue commands. Each command -gets separate queued and started comments. Final plans, PR results, failures, -and remaining review findings are posted separately. +Anyone who can comment on an issue or pull request can enqueue commands. Each +command gets separate queued and started comments. Final plans, PR results, +failures, and remaining review findings are posted separately. ## Deploy @@ -83,4 +83,3 @@ uv run pytest The tests fail if any tracked Python file exceeds 250 lines. Prompts and JSON schemas live outside Python so orchestration modules remain small and readable. - diff --git a/src/agentci/adapters/gitea.py b/src/agentci/adapters/gitea.py index 2e20e22..adfe0a3 100644 --- a/src/agentci/adapters/gitea.py +++ b/src/agentci/adapters/gitea.py @@ -34,13 +34,6 @@ class GiteaClient: async def close(self) -> None: await self.client.aclose() - async def has_write_permission(self, owner: str, repo: str, username: str) -> bool: - response = await self._request( - "GET", f"/repos/{owner}/{repo}/collaborators/{username}/permission" - ) - permission = str(response.json().get("permission", "")).lower() - return permission in {"write", "admin", "owner"} - async def repository(self, owner: str, repo: str) -> RepositoryInfo: data = (await self._request("GET", f"/repos/{owner}/{repo}")).json() return RepositoryInfo( diff --git a/src/agentci/api/webhook.py b/src/agentci/api/webhook.py index 8e3d762..0a5b8a3 100644 --- a/src/agentci/api/webhook.py +++ b/src/agentci/api/webhook.py @@ -70,19 +70,6 @@ async def _handle_command(container: Any, event: CommandEvent) -> Response: if not event.body.strip().startswith("/agent"): return Response(status_code=status.HTTP_204_NO_CONTENT) log.info("agent command received", extra=extra) - permitted = await container.gitea.has_write_permission( - event.repo_owner, event.repo_name, event.requester - ) - if not permitted: - log.warning("agent command rejected: insufficient permission", extra=extra) - if await container.storage.record_delivery(event.delivery_id, event.comment_id): - await container.gitea.create_comment( - event.repo_owner, - event.repo_name, - event.issue_number, - "Agent command rejected: repository write permission is required.", - ) - return Response(status_code=status.HTTP_202_ACCEPTED) try: command = parse_command(event.body) except CommandError as exc: diff --git a/tests/test_webhook.py b/tests/test_webhook.py index 50a92a3..00b7bf3 100644 --- a/tests/test_webhook.py +++ b/tests/test_webhook.py @@ -29,13 +29,9 @@ class FakeStorage: class FakeGitea: - def __init__(self, permitted: bool = True) -> None: - self.permitted = permitted + def __init__(self) -> None: self.comments: list[str] = [] - async def has_write_permission(self, *_args): - return self.permitted - async def create_comment(self, _owner, _repo, _number, body): self.comments.append(body) return len(self.comments) @@ -76,25 +72,8 @@ async def test_authorized_command_is_queued() -> None: assert "queued" in gitea.comments[0] -async def test_unauthorized_command_is_rejected_and_deduplicated() -> None: - storage = FakeStorage() - gitea = FakeGitea(permitted=False) - container = SimpleNamespace(storage=storage, gitea=gitea) - event = _event_from_payload("delivery", payload("/agent implement")) - assert event is not None - await _handle_command(container, event) - await _handle_command(container, event) - assert storage.jobs == [] - assert len(gitea.comments) == 1 - assert "write permission" in gitea.comments[0] - - -async def test_non_command_does_not_query_permission() -> None: - class ExplodingGitea(FakeGitea): - async def has_write_permission(self, *_args): - raise AssertionError("permission lookup should not run") - - container = SimpleNamespace(storage=FakeStorage(), gitea=ExplodingGitea()) +async def test_non_command_is_ignored() -> None: + container = SimpleNamespace(storage=FakeStorage(), gitea=FakeGitea()) event = _event_from_payload("delivery", payload("ordinary discussion")) assert event is not None response = await _handle_command(container, event)