Skip to content

Commit 3dbe641

Browse files
authored
fix: MCP server actor permission check fails closed on repo lookup error (#23285)
1 parent a3f3c8d commit 3dbe641

2 files changed

Lines changed: 48 additions & 6 deletions

File tree

pkg/cli/mcp_server_helpers.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,20 @@ func checkActorPermission(ctx context.Context, actor string, validateActor bool,
196196
// Get repository using cached lookup
197197
repo, err := getRepository()
198198
if err != nil {
199-
mcpLog.Printf("Tool %s: failed to get repository context, allowing access: %v", toolName, err)
200-
// If we can't determine the repository, allow access (fail open)
201-
return nil
199+
mcpLog.Printf("Tool %s: failed to get repository context, denying access: %v", toolName, err)
200+
return newMCPError(jsonrpc.CodeInternalError, "permission check failed", map[string]any{
201+
"error": err.Error(),
202+
"tool": toolName,
203+
"reason": "Could not determine repository context to verify permissions. Ensure you are running from within a git repository with gh authenticated.",
204+
})
202205
}
203206

204207
if repo == "" {
205-
mcpLog.Printf("Tool %s: no repository context, allowing access", toolName)
206-
// No repository context, allow access
207-
return nil
208+
mcpLog.Printf("Tool %s: no repository context, denying access", toolName)
209+
return newMCPError(jsonrpc.CodeInvalidRequest, "permission check failed", map[string]any{
210+
"tool": toolName,
211+
"reason": "No repository context available. Run from within a git repository.",
212+
})
208213
}
209214

210215
// Query actor's role in the repository with caching

pkg/cli/mcp_server_helpers_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package cli
44

55
import (
6+
"context"
67
"encoding/json"
78
"testing"
89
)
@@ -112,3 +113,39 @@ func TestValidateWorkflowName_Empty(t *testing.T) {
112113
t.Errorf("validateWorkflowName(\"\") returned error: %v", err)
113114
}
114115
}
116+
117+
// TestCheckActorPermission_AllowsWhenValidationDisabled verifies that access is
118+
// always granted when validateActor is false, regardless of actor or repo context.
119+
func TestCheckActorPermission_AllowsWhenValidationDisabled(t *testing.T) {
120+
err := checkActorPermission(context.Background(), "", false, "logs")
121+
if err != nil {
122+
t.Errorf("checkActorPermission with validateActor=false: expected nil, got %v", err)
123+
}
124+
}
125+
126+
// TestCheckActorPermission_DeniesWhenNoActorAndValidationEnabled verifies that access
127+
// is denied when actor is empty and validation is enabled.
128+
func TestCheckActorPermission_DeniesWhenNoActorAndValidationEnabled(t *testing.T) {
129+
err := checkActorPermission(context.Background(), "", true, "logs")
130+
if err == nil {
131+
t.Error("checkActorPermission with empty actor and validateActor=true: expected error, got nil")
132+
}
133+
}
134+
135+
// TestCheckActorPermission_DeniesWhenNoRepoContext verifies the fail-closed behavior
136+
// when no repository context is available (GITHUB_REPOSITORY unset, no git context).
137+
// This tests the security fix: permission check must fail closed, not open.
138+
func TestCheckActorPermission_DeniesWhenNoRepoContext(t *testing.T) {
139+
// Ensure no GITHUB_REPOSITORY is set (and cache is empty) so getRepository fails/returns "".
140+
t.Setenv("GITHUB_REPOSITORY", "")
141+
142+
// Clear any cached repo value so getRepository does a fresh lookup.
143+
mcpCache.SetRepo("")
144+
145+
// With GITHUB_REPOSITORY="" and no git context in the test environment,
146+
// getRepository will either return "" or an error — both paths must deny access.
147+
err := checkActorPermission(context.Background(), "someuser", true, "logs")
148+
if err == nil {
149+
t.Error("checkActorPermission with no repo context: expected error (fail closed), got nil (fail open)")
150+
}
151+
}

0 commit comments

Comments
 (0)