Skip to content

Add ifc label for get_file_contents tool#2454

Open
gokhanarkan wants to merge 1 commit into
gokhanarkan/fides-list-issuesfrom
gokhanarkan/fides-get-file-contents
Open

Add ifc label for get_file_contents tool#2454
gokhanarkan wants to merge 1 commit into
gokhanarkan/fides-list-issuesfrom
gokhanarkan/fides-get-file-contents

Conversation

@gokhanarkan
Copy link
Copy Markdown
Member

Emits an IFC SecurityLabel on the get_file_contents tool result when the InsidersMode flag is enabled, mirroring the pattern landed for get_me in #2432 and list_issues in #2453.

Refs github/copilot-mcp-core#1623, github/copilot-mcp-core#1389. One of the ingress tools listed in #1623's tool table.

Chained on #2453. Base is currently gokhanarkan/fides-list-issues because that PR adds the shared Public*/Private* label constructors in pkg/ifc/ifc.go. Once #2453 merges, GitHub will auto-retarget this PR's base to main.

What this PR does

  • Wires _meta.ifc onto the get_file_contents CallToolResult when deps.GetFlags(ctx).InsidersMode is true. No behaviour change when the flag is off.
  • Public repos → PublicUntrusted() (file contents can be authored by anyone via PRs, so integrity is untrusted; readers are ["public"]).
  • Private repos → PrivateTrusted([owner]) — only collaborators can land changes in private repos, so integrity is trusted. [owner] is a placeholder reader set; full collaborator enumeration is deferred (see limitation).
  • The label is attached at every success return path (text file, binary file, empty file, large-file resource link, directory, and the matchFiles Git-tree fallback) via a small attachIFC closure to avoid duplicating the insiders block five times.

New shared helper

FetchRepoIsPrivate(ctx, client, owner, repo) (bool, error) in pkg/github/repositories.go. Thin wrapper around client.Repositories.Get. Exported so upcoming ingress tools (search_issues, issue_read) can reuse the same visibility lookup. Flagging this for reviewers in case the export surface is a concern.

The lookup is invoked lazily and only when InsidersMode is on, so non-insiders pay no extra round trip. If the lookup fails, we skip the label rather than fail the user-facing call (label is best-effort metadata, not security gating yet).

LabelGetFileContents(isPrivate, readers) is a new helper in pkg/ifc/ifc.go that composes the existing PublicUntrusted/PrivateTrusted constructors.

Known limitation (called out for reviewers)

Same as #2453: private-repo confidentiality currently uses [owner] rather than the full collaborator list. Fetching collaborators requires a paginated REST call; rather than bake that into this PR, a shared visibility/collaborators helper is intended as a follow-up that get_file_contents, list_issues, search_issues, and issue_read can all share. TODO(fides) marker is at the call site.

Tests

Test_GetFileContents_IFC_InsidersMode mirrors Test_ListIssues_IFC_InsidersMode with three subtests:

  1. Insiders off → result.Meta == nil.
  2. Insiders on, public repo → integrity=untrusted, confidentiality=["public"].
  3. Insiders on, private repo → integrity=trusted, confidentiality=["<owner>"].

Existing Test_GetFileContents cases are unchanged (the GetReposByOwnerByRepo mock is already wired in those cases — its response is just inspected only when insiders is on).

Validation

  • go test -race ./... — green.
  • gofmt -s clean; go vet ./... clean.
  • (./script/lint itself fails locally with a pre-existing golangci-lint Go-version mismatch unrelated to this change.)
  • No tool schema/annotation changes → no toolsnap or README regeneration needed.

Emits an IFC SecurityLabel on the get_file_contents tool result when the
InsidersMode flag is enabled, mirroring the pattern landed for get_me in
#2432 and list_issues in #2453.

Public repositories are labelled PublicUntrusted (anyone can author file
content via pull requests). Private repositories are labelled
PrivateTrusted with the repository owner as a placeholder reader, since
only collaborators can land changes there. Full collaborator enumeration
is intentionally deferred to a follow-up shared helper.

A new exported FetchRepoIsPrivate helper wraps Repositories.Get for
visibility lookups; it is invoked lazily and only when InsidersMode is
on, so non-insiders pay no extra round trip. Visibility lookup failures
skip the label rather than fail the user-facing call.

Refs github/copilot-mcp-core#1623, github/copilot-mcp-core#1389.
Copilot AI review requested due to automatic review settings May 11, 2026 16:43
@gokhanarkan gokhanarkan requested a review from a team as a code owner May 11, 2026 16:43
@gokhanarkan gokhanarkan self-assigned this May 11, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds IFC SecurityLabel annotations to the get_file_contents tool result behind InsidersMode, aligning this ingress tool with the existing _meta.ifc pattern used by other tools (e.g., get_me, list_issues).

Changes:

  • Add ifc.LabelGetFileContents(isPrivate, readers) helper for computing labels for get_file_contents.
  • Add FetchRepoIsPrivate(ctx, client, owner, repo) helper and use it (lazily) to determine repo visibility when attaching IFC metadata.
  • Add unit tests covering insiders off / insiders on (public) / insiders on (private) label emission for get_file_contents.
Show a summary per file
File Description
pkg/ifc/ifc.go Adds LabelGetFileContents helper to compute IFC labels for file-content results.
pkg/github/repositories.go Adds FetchRepoIsPrivate and attaches _meta.ifc to successful get_file_contents responses in insiders mode.
pkg/github/repositories_test.go Adds Test_GetFileContents_IFC_InsidersMode to validate label emission behavior.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Comment on lines +745 to +756
ifcVisibilityLoaded bool
ifcIsPrivate bool
)
attachIFC := func(r *mcp.CallToolResult) *mcp.CallToolResult {
if r == nil || r.IsError || !deps.GetFlags(ctx).InsidersMode {
return r
}
if !ifcVisibilityLoaded {
if isPrivate, err := FetchRepoIsPrivate(ctx, client, owner, repo); err == nil {
ifcIsPrivate = isPrivate
}
ifcVisibilityLoaded = true
Comment on lines +660 to +665
func FetchRepoIsPrivate(ctx context.Context, client *github.Client, owner, repo string) (bool, error) {
r, _, err := client.Repositories.Get(ctx, owner, repo)
if err != nil {
return false, err
}
return r.GetPrivate(), nil
Comment on lines +487 to +494
makeMockClient := func(isPrivate bool) *http.Client {
return MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
GetReposGitRefByOwnerByRepoByRef: mockResponse(t, http.StatusOK, "{\"ref\": \"refs/heads/main\", \"object\": {\"sha\": \"\"}}"),
GetReposByOwnerByRepo: mockResponse(t, http.StatusOK, map[string]any{
"name": "repo",
"default_branch": "main",
"private": isPrivate,
}),
)
}

// FetchRepoIsPrivate returns the visibility of a repository. It is a thin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants