Skip to content

Commit 7cfb354

Browse files
authored
Dont filter content from Copilot (#1464)
* Dont filter content from trusted bots * Final changes * Use only debug level * Add logs and comments
1 parent 781a95f commit 7cfb354

File tree

2 files changed

+54
-18
lines changed

2 files changed

+54
-18
lines changed

internal/ghmcp/server.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ type MCPServerConfig struct {
6262

6363
const stdioServerLogPrefix = "stdioserver"
6464

65-
func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
65+
func NewMCPServer(cfg MCPServerConfig, logger *slog.Logger) (*server.MCPServer, error) {
6666
apiHost, err := parseAPIHost(cfg.Host)
6767
if err != nil {
6868
return nil, fmt.Errorf("failed to parse API host: %w", err)
@@ -88,6 +88,9 @@ func NewMCPServer(cfg MCPServerConfig) (*server.MCPServer, error) {
8888
if cfg.RepoAccessTTL != nil {
8989
repoAccessOpts = append(repoAccessOpts, lockdown.WithTTL(*cfg.RepoAccessTTL))
9090
}
91+
92+
repoAccessLogger := logger.With("component", "lockdown")
93+
repoAccessOpts = append(repoAccessOpts, lockdown.WithLogger(repoAccessLogger))
9194
var repoAccessCache *lockdown.RepoAccessCache
9295
if cfg.LockdownMode {
9396
repoAccessCache = lockdown.GetInstance(gqlClient, repoAccessOpts...)
@@ -273,7 +276,7 @@ func RunStdioServer(cfg StdioServerConfig) error {
273276
ContentWindowSize: cfg.ContentWindowSize,
274277
LockdownMode: cfg.LockdownMode,
275278
RepoAccessTTL: cfg.RepoAccessCacheTTL,
276-
})
279+
}, logger)
277280
if err != nil {
278281
return fmt.Errorf("failed to create MCP server: %w", err)
279282
}

pkg/lockdown/lockdown.go

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import (
1515
// RepoAccessCache caches repository metadata related to lockdown checks so that
1616
// multiple tools can reuse the same access information safely across goroutines.
1717
type RepoAccessCache struct {
18-
client *githubv4.Client
19-
mu sync.Mutex
20-
cache *cache2go.CacheTable
21-
ttl time.Duration
22-
logger *slog.Logger
18+
client *githubv4.Client
19+
mu sync.Mutex
20+
cache *cache2go.CacheTable
21+
ttl time.Duration
22+
logger *slog.Logger
23+
trustedBotLogins map[string]struct{}
2324
}
2425

2526
type repoAccessCacheEntry struct {
@@ -85,6 +86,9 @@ func GetInstance(client *githubv4.Client, opts ...RepoAccessOption) *RepoAccessC
8586
client: client,
8687
cache: cache2go.Cache(defaultRepoAccessCacheKey),
8788
ttl: defaultRepoAccessTTL,
89+
trustedBotLogins: map[string]struct{}{
90+
"copilot": {},
91+
},
8892
}
8993
for _, opt := range opts {
9094
if opt != nil {
@@ -109,13 +113,22 @@ type CacheStats struct {
109113
Evictions int64
110114
}
111115

116+
// IsSafeContent determines if the specified user can safely access the requested repository content.
117+
// Safe access applies when any of the following is true:
118+
// - the content was created by a trusted bot;
119+
// - the author currently has push access to the repository;
120+
// - the repository is private;
121+
// - the content was created by the viewer.
112122
func (c *RepoAccessCache) IsSafeContent(ctx context.Context, username, owner, repo string) (bool, error) {
113123
repoInfo, err := c.getRepoAccessInfo(ctx, username, owner, repo)
114124
if err != nil {
115-
c.logDebug("error checking repo access info for content filtering", "owner", owner, "repo", repo, "user", username, "error", err)
116125
return false, err
117126
}
118-
if repoInfo.IsPrivate || repoInfo.ViewerLogin == username {
127+
128+
c.logDebug(ctx, fmt.Sprintf("evaluated repo access for user %s to %s/%s for content filtering, result: hasPushAccess=%t, isPrivate=%t",
129+
username, owner, repo, repoInfo.HasPushAccess, repoInfo.IsPrivate))
130+
131+
if c.isTrustedBot(username) || repoInfo.IsPrivate || repoInfo.ViewerLogin == strings.ToLower(username) {
119132
return true, nil
120133
}
121134
return repoInfo.HasPushAccess, nil
@@ -136,30 +149,34 @@ func (c *RepoAccessCache) getRepoAccessInfo(ctx context.Context, username, owner
136149
if err == nil {
137150
entry := cacheItem.Data().(*repoAccessCacheEntry)
138151
if cachedHasPush, known := entry.knownUsers[userKey]; known {
139-
c.logDebug("repo access cache hit", "owner", owner, "repo", repo, "user", username)
152+
c.logDebug(ctx, fmt.Sprintf("repo access cache hit for user %s to %s/%s", username, owner, repo))
140153
return RepoAccessInfo{
141154
IsPrivate: entry.isPrivate,
142155
HasPushAccess: cachedHasPush,
143156
ViewerLogin: entry.viewerLogin,
144157
}, nil
145158
}
146-
c.logDebug("known users cache miss", "owner", owner, "repo", repo, "user", username)
159+
160+
c.logDebug(ctx, "known users cache miss, fetching from graphql API")
161+
147162
info, queryErr := c.queryRepoAccessInfo(ctx, username, owner, repo)
148163
if queryErr != nil {
149164
return RepoAccessInfo{}, queryErr
150165
}
166+
151167
entry.knownUsers[userKey] = info.HasPushAccess
152168
entry.viewerLogin = info.ViewerLogin
153169
entry.isPrivate = info.IsPrivate
154170
c.cache.Add(key, c.ttl, entry)
171+
155172
return RepoAccessInfo{
156173
IsPrivate: entry.isPrivate,
157174
HasPushAccess: entry.knownUsers[userKey],
158175
ViewerLogin: entry.viewerLogin,
159176
}, nil
160177
}
161178

162-
c.logDebug("repo access cache miss", "owner", owner, "repo", repo, "user", username)
179+
c.logDebug(ctx, fmt.Sprintf("repo access cache miss for user %s to %s/%s", username, owner, repo))
163180

164181
info, queryErr := c.queryRepoAccessInfo(ctx, username, owner, repo)
165182
if queryErr != nil {
@@ -223,19 +240,35 @@ func (c *RepoAccessCache) queryRepoAccessInfo(ctx context.Context, username, own
223240
}
224241
}
225242

243+
c.logDebug(ctx, fmt.Sprintf("queried repo access info for user %s to %s/%s: isPrivate=%t, hasPushAccess=%t, viewerLogin=%s",
244+
username, owner, repo, bool(query.Repository.IsPrivate), hasPush, query.Viewer.Login))
245+
226246
return RepoAccessInfo{
227247
IsPrivate: bool(query.Repository.IsPrivate),
228248
HasPushAccess: hasPush,
229249
ViewerLogin: string(query.Viewer.Login),
230250
}, nil
231251
}
232252

233-
func cacheKey(owner, repo string) string {
234-
return fmt.Sprintf("%s/%s", strings.ToLower(owner), strings.ToLower(repo))
253+
func (c *RepoAccessCache) log(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr) {
254+
if c == nil || c.logger == nil {
255+
return
256+
}
257+
if !c.logger.Enabled(ctx, level) {
258+
return
259+
}
260+
c.logger.LogAttrs(ctx, level, msg, attrs...)
235261
}
236262

237-
func (c *RepoAccessCache) logDebug(msg string, args ...any) {
238-
if c != nil && c.logger != nil {
239-
c.logger.Debug(msg, args...)
240-
}
263+
func (c *RepoAccessCache) logDebug(ctx context.Context, msg string, attrs ...slog.Attr) {
264+
c.log(ctx, slog.LevelDebug, msg, attrs...)
265+
}
266+
267+
func (c *RepoAccessCache) isTrustedBot(username string) bool {
268+
_, ok := c.trustedBotLogins[strings.ToLower(username)]
269+
return ok
270+
}
271+
272+
func cacheKey(owner, repo string) string {
273+
return fmt.Sprintf("%s/%s", strings.ToLower(owner), strings.ToLower(repo))
241274
}

0 commit comments

Comments
 (0)