From 93cf95f7990ca3dc6e5da495a1a4fb37ee322e89 Mon Sep 17 00:00:00 2001 From: Junghwan <70629228+shaun0927@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:43:17 +0900 Subject: [PATCH] fix(security): enforce workspace access on GetIssueGCCheck (#1121) The daemon GC check endpoint did not verify the caller's access to the issue's workspace, letting a daemon token or PAT scoped to workspace A read issue status/updated_at for any issue UUID across the instance. Mirror the pattern used by every other handler in daemon.go: look up the issue's workspace and gate on requireDaemonWorkspaceAccess. Closes #1112 Co-authored-by: shaun0927 --- server/internal/handler/daemon.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/internal/handler/daemon.go b/server/internal/handler/daemon.go index 5dbe1ef3d..81d358a6d 100644 --- a/server/internal/handler/daemon.go +++ b/server/internal/handler/daemon.go @@ -1033,6 +1033,8 @@ func (h *Handler) GetIssueUsage(w http.ResponseWriter, r *http.Request) { } // GetIssueGCCheck returns minimal issue info needed by the daemon GC loop. +// Gated on workspace access so a daemon token scoped to workspace A cannot +// read issue metadata from workspace B via UUID enumeration. func (h *Handler) GetIssueGCCheck(w http.ResponseWriter, r *http.Request) { issueID := chi.URLParam(r, "issueId") issue, err := h.Queries.GetIssue(r.Context(), parseUUID(issueID)) @@ -1040,6 +1042,9 @@ func (h *Handler) GetIssueGCCheck(w http.ResponseWriter, r *http.Request) { writeError(w, http.StatusNotFound, "issue not found") return } + if !h.requireDaemonWorkspaceAccess(w, r, uuidToString(issue.WorkspaceID)) { + return + } writeJSON(w, http.StatusOK, map[string]any{ "status": issue.Status, "updated_at": issue.UpdatedAt.Time,