Skip to content

Commit f92f8a7

Browse files
RossTarrantCopilot
andcommitted
fix: correct IFC labels
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 6586b84 commit f92f8a7

12 files changed

Lines changed: 136 additions & 119 deletions

pkg/github/discussions.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func ListDiscussions(t translations.TranslationHelperFunc) inventory.ServerTool
276276
result := utils.NewToolResultText(string(out))
277277
// Discussion content is user-authored (untrusted); confidentiality
278278
// follows repo visibility.
279-
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, owner, repo, result, ifc.LabelListIssues)
279+
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, owner, repo, result, ifc.LabelRepoUserContent)
280280
return result, nil, nil
281281
},
282282
)
@@ -384,7 +384,7 @@ func GetDiscussion(t translations.TranslationHelperFunc) inventory.ServerTool {
384384
result := utils.NewToolResultText(string(out))
385385
// Discussion content is user-authored (untrusted); confidentiality
386386
// follows repo visibility.
387-
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelListIssues)
387+
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelRepoUserContent)
388388
return result, nil, nil
389389
},
390390
)
@@ -592,7 +592,7 @@ func GetDiscussionComments(t translations.TranslationHelperFunc) inventory.Serve
592592
result := utils.NewToolResultText(string(out))
593593
// Discussion comments are user-authored (untrusted); confidentiality
594594
// follows repo visibility.
595-
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelListIssues)
595+
result = attachRepoVisibilityIFCLabelLazy(ctx, deps, params.Owner, params.Repo, result, ifc.LabelRepoUserContent)
596596
return result, nil, nil
597597
},
598598
)

pkg/github/gists.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,7 @@ func ListGists(t translations.TranslationHelperFunc) inventory.ServerTool {
101101
}
102102

103103
result := utils.NewToolResultText(string(r))
104-
// Gist contents are user-authored (untrusted); confidentiality is
105-
// the IFC join of each gist's own public/secret flag.
106-
visibilities := make([]bool, 0, len(gists))
107-
for _, g := range gists {
108-
visibilities = append(visibilities, g.GetPublic())
109-
}
110-
result = attachJoinedIFCLabel(ctx, deps, result, visibilities, ifc.LabelGistList)
104+
result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelGistList())
111105
return result, nil, nil
112106
},
113107
)
@@ -167,9 +161,7 @@ func GetGist(t translations.TranslationHelperFunc) inventory.ServerTool {
167161
}
168162

169163
result := utils.NewToolResultText(string(r))
170-
// Gist contents are user-authored (untrusted); confidentiality
171-
// derives from the gist's own public/secret flag.
172-
result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelGist(gist.GetPublic()))
164+
result = attachStaticIFCLabel(ctx, deps, result, ifc.LabelGist())
173165
return result, nil, nil
174166
},
175167
)

pkg/github/ifc_labels.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,11 @@ func attachRepoVisibilityIFCLabelLazy(
9797
}
9898

9999
// attachJoinedIFCLabel attaches an IFC label computed by joining a set of
100-
// per-item visibilities (true == private for repositories, true == public for
101-
// gists) when IFC labels are enabled. joinFn is the lattice join for the
102-
// relevant item kind (e.g. ifc.LabelSearchIssues or ifc.LabelGistList). The
103-
// visibility slice is cheap to build from an already-fetched response, so
104-
// callers may construct it unconditionally and let this helper own the
105-
// feature-flag gate.
100+
// per-item visibilities (true == private) when IFC labels are enabled. joinFn
101+
// is the lattice join for the relevant item kind (e.g. ifc.LabelSearchIssues or
102+
// ifc.LabelGistList). The visibility slice is cheap to build from an
103+
// already-fetched response, so callers may construct it unconditionally and let
104+
// this helper own the feature-flag gate.
106105
func attachJoinedIFCLabel(
107106
ctx context.Context,
108107
deps ToolDependencies,

pkg/github/issues.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,7 @@ Options are:
804804
// attachIFC adds the IFC label to a successful tool result when
805805
// IFC labels are enabled. If the visibility lookup fails the
806806
// label is omitted rather than misclassifying the result.
807-
attachIFC := newRepoVisibilityIFCLabeler(ctx, deps, client, owner, repo, ifc.LabelListIssues)
807+
attachIFC := newRepoVisibilityIFCLabeler(ctx, deps, client, owner, repo, ifc.LabelRepoUserContent)
808808

809809
switch method {
810810
case "get":

pkg/github/issues_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ func Test_IssueRead_IFC_InsidersMode(t *testing.T) {
356356
assert.Equal(t, "public", ifcMap["confidentiality"])
357357
})
358358

359-
t.Run("insiders mode enabled on private repo with get_comments emits private untrusted", func(t *testing.T) {
359+
t.Run("insiders mode enabled on private repo with get_comments emits private trusted", func(t *testing.T) {
360360
deps := BaseDeps{
361361
Client: mustNewGHClient(t, makeMockClient(true, 0)),
362362
featureChecker: featureCheckerFor(FeatureFlagIFCLabels),
@@ -370,7 +370,7 @@ func Test_IssueRead_IFC_InsidersMode(t *testing.T) {
370370

371371
require.NotNil(t, result.Meta)
372372
ifcMap := unmarshalIFC(t, result.Meta["ifc"])
373-
assert.Equal(t, "untrusted", ifcMap["integrity"])
373+
assert.Equal(t, "trusted", ifcMap["integrity"])
374374
assert.Equal(t, "private", ifcMap["confidentiality"])
375375
})
376376

@@ -2852,7 +2852,7 @@ func Test_ListIssues_IFC_InsidersMode(t *testing.T) {
28522852
assert.Equal(t, "public", ifcMap["confidentiality"])
28532853
})
28542854

2855-
t.Run("insiders mode enabled on private repo emits private untrusted label", func(t *testing.T) {
2855+
t.Run("insiders mode enabled on private repo emits private trusted label", func(t *testing.T) {
28562856
matcher := githubv4mock.NewQueryMatcher(query, vars, makeResponse(true))
28572857
gqlClient := githubv4.NewClient(githubv4mock.NewMockedHTTPClient(matcher))
28582858
deps := BaseDeps{
@@ -2875,7 +2875,7 @@ func Test_ListIssues_IFC_InsidersMode(t *testing.T) {
28752875
var ifcMap map[string]any
28762876
require.NoError(t, json.Unmarshal(ifcJSON, &ifcMap))
28772877

2878-
assert.Equal(t, "untrusted", ifcMap["integrity"])
2878+
assert.Equal(t, "trusted", ifcMap["integrity"])
28792879
assert.Equal(t, "private", ifcMap["confidentiality"])
28802880
})
28812881
}

pkg/github/projects.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,9 @@ Use this tool to list projects for a user or organization, or list project field
232232
// labels are enabled. Project titles, item content, field
233233
// definitions, and status updates are user-authored free text
234234
// (untrusted); confidentiality is conservatively private since the
235-
// project's public flag is not available across every sub-result.
235+
// project's privacy is not available across every sub-result.
236236
attachIFC := func(r *mcp.CallToolResult) *mcp.CallToolResult {
237-
return attachStaticIFCLabel(ctx, deps, r, ifc.LabelProject(false))
237+
return attachStaticIFCLabel(ctx, deps, r, ifc.LabelProject(true))
238238
}
239239

240240
switch method {
@@ -349,9 +349,9 @@ Use this tool to get details about individual projects, project fields, and proj
349349
// attachIFC adds the IFC label to a successful result when IFC
350350
// labels are enabled. Project data is user-authored free text
351351
// (untrusted); confidentiality is conservatively private since the
352-
// project's public flag is not available across every sub-result.
352+
// project's privacy is not available across every sub-result.
353353
attachIFC := func(r *mcp.CallToolResult) *mcp.CallToolResult {
354-
return attachStaticIFCLabel(ctx, deps, r, ifc.LabelProject(false))
354+
return attachStaticIFCLabel(ctx, deps, r, ifc.LabelProject(true))
355355
}
356356

357357
// Handle get_project_status_update early — it only needs status_update_id

pkg/github/pullrequests.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Possible options:
115115
// visibility lookup fails the label is omitted rather than
116116
// misclassifying the result.
117117
attachIFC := func(r *mcp.CallToolResult) *mcp.CallToolResult {
118-
return attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, r, ifc.LabelListIssues)
118+
return attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, r, ifc.LabelRepoUserContent)
119119
}
120120

121121
switch method {
@@ -1339,7 +1339,7 @@ func ListPullRequests(t translations.TranslationHelperFunc) inventory.ServerTool
13391339
result := utils.NewToolResultText(string(r))
13401340
// Pull request titles/bodies are user-authored (untrusted);
13411341
// confidentiality follows repo visibility.
1342-
result = attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, result, ifc.LabelListIssues)
1342+
result = attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, result, ifc.LabelRepoUserContent)
13431343
return result, nil, nil
13441344
})
13451345
}

pkg/github/repositories.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,9 +2121,10 @@ func ListStarredRepositories(t translations.TranslationHelperFunc) inventory.Ser
21212121
result := utils.NewToolResultText(string(r))
21222122
// A starred-repository listing exposes repository data across many
21232123
// repos; reuse the multi-repo join shared with search_repositories
2124-
// (untrusted integrity; confidentiality private if any matched repo
2125-
// is private). Visibility is read directly from the response, so no
2126-
// extra API call is needed.
2124+
// (public-only results stay public-untrusted, mixed-visibility
2125+
// results become private-untrusted, all-private results become
2126+
// private-trusted). Visibility is read directly from the response,
2127+
// so no extra API call is needed.
21272128
visibilities := make([]bool, 0, len(minimalRepos))
21282129
for _, mr := range minimalRepos {
21292130
visibilities = append(visibilities, mr.Private)

pkg/github/search.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,9 @@ func SearchRepositories(t translations.TranslationHelperFunc) inventory.ServerTo
173173
// every matched repository and attaches the result to callResult when IFC
174174
// labels are enabled. Visibility is read directly from the search response —
175175
// no extra API call. The join math is shared with search_issues via
176-
// ifc.LabelSearchIssues: integrity is always untrusted; confidentiality is
177-
// private if any matched repository is private, otherwise public. The
176+
// ifc.LabelSearchIssues: public-only results stay public-untrusted,
177+
// mixed-visibility results become private-untrusted, and all-private results
178+
// become private-trusted. The
178179
// feature-flag check is centralized here (mirroring the attach* helpers in
179180
// ifc_labels.go) so the handler can call this unconditionally.
180181
func attachSearchRepositoriesIFCLabel(ctx context.Context, deps ToolDependencies, repos []*github.Repository, callResult *mcp.CallToolResult) {
@@ -302,9 +303,9 @@ func SearchCode(t translations.TranslationHelperFunc) inventory.ServerTool {
302303
}
303304

304305
callResult := utils.NewToolResultText(string(r))
305-
// Code search spans repositories and exposes file contents
306-
// (untrusted). Confidentiality is the IFC join across every matched
307-
// repository's visibility, read directly from the search response.
306+
// Code search spans repositories; the IFC label is the conservative
307+
// join across every matched repository's visibility, read directly
308+
// from the search response.
308309
visibilities := make([]bool, 0, len(result.CodeResults))
309310
for _, code := range result.CodeResults {
310311
if code.Repository != nil {
@@ -593,9 +594,9 @@ func SearchCommits(t translations.TranslationHelperFunc) inventory.ServerTool {
593594
}
594595

595596
callResult := utils.NewToolResultText(string(r))
596-
// Commit search spans repositories and exposes commit content
597-
// (untrusted). Confidentiality is the IFC join across every matched
598-
// repository's visibility, read directly from the search response.
597+
// Commit search spans repositories; the IFC label is the conservative
598+
// join across every matched repository's visibility, read directly
599+
// from the search response.
599600
visibilities := make([]bool, 0, len(result.Commits))
600601
for _, commit := range result.Commits {
601602
if commit.Repository != nil {

pkg/github/search_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func Test_SearchRepositories_IFC_InsidersMode(t *testing.T) {
238238
assert.Equal(t, "public", ifcMap["confidentiality"])
239239
})
240240

241-
t.Run("insiders mode any private match emits private untrusted", func(t *testing.T) {
241+
t.Run("insiders mode mixed public and private emits private untrusted", func(t *testing.T) {
242242
deps := BaseDeps{
243243
Client: mustNewGHClient(t, makeMockClient([]repoFixture{
244244
{owner: "octocat", name: "private-repo", isPrivate: true},

0 commit comments

Comments
 (0)