// Package ifc provides Information Flow Control labels for annotating MCP tool outputs. // The actual IFC enforcement engine lives in a separate service; this package only // defines the label schema used for annotations. package ifc type Integrity string const ( IntegrityTrusted Integrity = "trusted" IntegrityUntrusted Integrity = "untrusted" ) type Confidentiality string const ( ConfidentialityPublic Confidentiality = "public" ConfidentialityPrivate Confidentiality = "private" ) type SecurityLabel struct { Integrity Integrity `json:"integrity"` Confidentiality Confidentiality `json:"confidentiality"` } // PublicTrusted returns a label for trusted, publicly readable data. func PublicTrusted() SecurityLabel { return SecurityLabel{ Integrity: IntegrityTrusted, Confidentiality: ConfidentialityPublic, } } // PublicUntrusted returns a label for untrusted, publicly readable data. func PublicUntrusted() SecurityLabel { return SecurityLabel{ Integrity: IntegrityUntrusted, Confidentiality: ConfidentialityPublic, } } // PrivateTrusted returns a label for trusted data restricted to the readers // of the originating repository. The reader set is opaque on the wire (a // single "private" marker); the client engine resolves the concrete readers // from the GitHub API on demand at egress decision time. func PrivateTrusted() SecurityLabel { return SecurityLabel{ Integrity: IntegrityTrusted, Confidentiality: ConfidentialityPrivate, } } // PrivateUntrusted returns a label for untrusted data restricted to the // readers of the originating repository. See PrivateTrusted for the reader // resolution model. func PrivateUntrusted() SecurityLabel { return SecurityLabel{ Integrity: IntegrityUntrusted, Confidentiality: ConfidentialityPrivate, } } // LabelGetMe returns the IFC label for the authenticated user's own profile // (get_me). // // Integrity is trusted: this is GitHub-maintained data about the caller's own // account, not attacker-authored content. // // Confidentiality is private. The result includes fields that are NOT part of // the user's public profile — private_gists, total_private_repos, and // owned_private_repos — which are visible only to the authenticated user. The // result therefore must not be treated as world-readable. func LabelGetMe() SecurityLabel { return PrivateTrusted() } // LabelListIssues returns the IFC label for a list_issues result. // Public repositories are universally readable; private repositories are // restricted to their collaborators (resolved client-side from the marker). // Public repository issue contents are attacker-controllable, while private // repository issues are treated as trusted collaborator-authored data. func LabelListIssues(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicUntrusted() } // LabelRepoUserContent returns the IFC label for user-authored content scoped // to a repository when that tool has not opted into a more specific integrity // policy. Public repository content is untrusted because it may be authored by // outside contributors. Private repository content is trusted because users who // can read it are trusted collaborators. func LabelRepoUserContent(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicUntrusted() } // LabelGetFileContents returns the IFC label for a get_file_contents result. // Public repository file contents may be authored by anyone via pull requests // and are therefore untrusted. In private repositories only collaborators can // land changes, so contents are treated as trusted. func LabelGetFileContents(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicUntrusted() } // LabelSearchIssues returns the IFC label for a multi-repository search // result, joining per-repository labels across all matched repositories. // Used by both search_issues and search_repositories. // // Public-only results are untrusted and public. All-private results are trusted // and private because private repository content is treated as trusted // collaborator-authored data. Mixed public/private results are untrusted and // private: the public items keep the joined payload's integrity untrusted, // while the private items keep the joined payload's confidentiality private. // The reader set is opaque (the "private" marker); the client engine resolves // concrete readers on demand at egress decision time. // // An empty result set is treated as public-untrusted (no repository data is // leaked). // // Why a single joined label rather than one label per item: a tool result is // delivered as one opaque payload (a single content block) and the IFC engine // makes one allow/deny decision per flow at egress. Once the items share a // buffer in the agent's context they can be copied anywhere together, so the // only sound bound for the whole result is the meet of every item's label. // Per-item labels would only become load-bearing if the enforcement engine // could partition a result and route individual items to different sinks; // until then they would invite unsafe declassification of a "public" item that // actually arrived alongside private data. func LabelSearchIssues(repoVisibilities []bool) SecurityLabel { var anyPrivate, anyPublic bool for _, isPrivate := range repoVisibilities { if isPrivate { anyPrivate = true } else { anyPublic = true } } switch { case anyPrivate && anyPublic: return PrivateUntrusted() case anyPrivate: return PrivateTrusted() default: return PublicUntrusted() } } // LabelRepoMetadata returns the IFC label for structural repository metadata // that only collaborators with write access can define: labels, branches, // tags, releases, issue types, issue field definitions, discussion // categories, and the collaborator roster. // // Integrity is trusted because, unlike issue/PR/comment bodies, these // artifacts cannot be authored by arbitrary outsiders — creating a branch, // tag, release, or label requires push access, so the data reflects decisions // made by the repository's trusted writers rather than attacker-controllable // input. // // Confidentiality follows repository visibility: public repositories are // universally readable; private repositories restrict the reader set (the // opaque "private" marker, resolved client-side at egress time). func LabelRepoMetadata(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicTrusted() } // LabelRelease returns the IFC label for repository releases (list_releases, // get_latest_release, get_release_by_tag). // // Integrity is trusted: releases are published by collaborators with push // access, not by arbitrary outsiders. // // Confidentiality is public only when the repository is public AND no returned // release is a draft. Draft releases are visible only to users with push // access — they are NOT world-readable even on a public repository — so a // result containing one must be private. hasDraft reflects whether any release // in the result is a draft; private repositories are always private regardless. func LabelRelease(isPrivate bool, hasDraft bool) SecurityLabel { if isPrivate || hasDraft { return PrivateTrusted() } return PublicTrusted() } // LabelCollaboratorRoster returns the IFC label for a repository's collaborator // list (list_repository_collaborators). // // Integrity is trusted: the roster is GitHub-maintained membership data, not // attacker-authored content. // // Confidentiality is always private. Listing collaborators requires push // access to the repository, so the roster is never world-readable — not even // for public repositories. This mirrors LabelTeam: membership data is // restricted regardless of the repository's own visibility. func LabelCollaboratorRoster() SecurityLabel { return PrivateTrusted() } // LabelCommitContents returns the IFC label for committed repository content // reachable from the default branch and its history: commits, commit diffs, // and the repository file tree. // // It shares the reasoning of LabelGetFileContents. In public repositories any // outsider can land content via a pull request, so the integrity of committed // content is untrusted. In private repositories only collaborators can push, // so committed content is trusted. Confidentiality follows repository // visibility. func LabelCommitContents(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicUntrusted() } // LabelActionsResult returns the IFC label for GitHub Actions resources: // workflow definitions, runs, jobs, artifacts, and job logs. // // Integrity is untrusted. Workflow logs echo arbitrary text produced during a // run — including output derived from pull-request branches, dependency // downloads, and other attacker-influenceable sources — so log and artifact // content must be treated as low integrity. Workflow definitions are // themselves editable through pull requests in public repositories. // // Confidentiality follows repository visibility. func LabelActionsResult(isPrivate bool) SecurityLabel { if isPrivate { return PrivateUntrusted() } return PublicUntrusted() } // LabelSecurityAlert returns the IFC label for security findings: code // scanning alerts, secret scanning alerts, and Dependabot alerts. // // Integrity is untrusted because alert payloads embed attacker-influenceable // material — the offending code snippet, the matched secret string, or a // vulnerable dependency's advisory text — none of which the agent should treat // as a trustworthy instruction source. // // Confidentiality is always private. Security alerts are access-restricted by // GitHub regardless of repository visibility (only users with a security role // can read them), so the reader set is narrow even for public repositories. // Secret scanning results additionally surface the secret material itself. func LabelSecurityAlert() SecurityLabel { return PrivateUntrusted() } // LabelGlobalSecurityAdvisory returns the IFC label for advisories served from // the public GitHub Advisory Database (global advisories). // // The advisory database is world-readable, so confidentiality is public. // Integrity is untrusted: advisory descriptions are externally authored prose // and must not be treated as a trusted instruction source. func LabelGlobalSecurityAdvisory() SecurityLabel { return PublicUntrusted() } // LabelRepositorySecurityAdvisory returns the IFC label for repository- or // organization-scoped security advisories. // // Integrity is untrusted (externally authored advisory prose). // // Confidentiality is public only when the repository is public AND every // advisory in the result is in the "published" state. Repository security // advisories also exist in draft, triage, and closed states; those are visible // only to maintainers and are NOT world-readable even on a public repository. // Treating any non-published advisory as private (allPublished == false) // prevents misclassifying an unpublished advisory from a public repo as // public-readable. Private repositories are always private regardless of state. func LabelRepositorySecurityAdvisory(isPrivate bool, allPublished bool) SecurityLabel { if isPrivate || !allPublished { return PrivateUntrusted() } return PublicUntrusted() } // LabelGist returns the IFC label for gist content. // // Integrity is untrusted: gist contents are arbitrary user-authored text. // Confidentiality is public because secret gists are URL-accessible and cannot // be modeled as private to a GitHub reader set. func LabelGist() SecurityLabel { return PublicUntrusted() } // LabelGistList returns the IFC label for a list of gists belonging to a user, // joining the per-gist confidentiality across the result set. // // Integrity is untrusted (user-authored content). Confidentiality is public // because even secret gists are URL-accessible. // // See LabelSearchIssues for why list results carry a single joined label // rather than one label per item. func LabelGistList() SecurityLabel { return PublicUntrusted() } // LabelProject returns the IFC label for GitHub Project metadata (Projects v2), // such as get_project results and project field definitions. // // Public project metadata can contain public user-authored text, so it is // untrusted. Private project metadata is treated as trusted // collaborator-controlled data. // // Confidentiality derives from the project's own privacy — private projects // restrict the reader set, while public projects are universally readable. func LabelProject(isPrivate bool) SecurityLabel { if isPrivate { return PrivateTrusted() } return PublicUntrusted() } // LabelProjectList returns the IFC label for a list_projects result, joining // the per-project labels across every returned project. // // Public-only results are untrusted and public. All-private results are trusted // and private. Mixed public/private results are untrusted and private: public // items keep the joined payload's integrity untrusted, while private items keep // the joined payload's confidentiality private. func LabelProjectList(projectVisibilities []bool) SecurityLabel { var anyPrivate, anyPublic bool for _, isPrivate := range projectVisibilities { if isPrivate { anyPrivate = true } else { anyPublic = true } } switch { case anyPrivate && anyPublic: return PrivateUntrusted() case anyPrivate: return PrivateTrusted() default: return PublicUntrusted() } } // LabelProjectContent returns the IFC label for project results that can include // item content, field values, or status update bodies. These can aggregate // content from a variety of sources, so integrity remains untrusted even when // the project is private. func LabelProjectContent(isPrivate bool) SecurityLabel { if isPrivate { return PrivateUntrusted() } return PublicUntrusted() } // LabelTeam returns the IFC label for organization team membership data // (get_teams, get_team_members). // // Integrity is trusted: team membership is maintained by GitHub and cannot be // forged by outside contributors, so it is not an attacker-controllable // instruction source. // // Confidentiality is private. Organization team rosters and the teams a user // belongs to are visible only to members of the organization, not to the // public, so the reader set is restricted (the opaque "private" marker). func LabelTeam() SecurityLabel { return PrivateTrusted() } // LabelNotificationDetails returns the IFC label for the subject of a single // notification. // // Integrity is untrusted: a notification subject points at an issue, pull // request, comment, or discussion whose content is user-authored and may carry // attacker-controlled text. Confidentiality is private because notifications // are delivered to a specific recipient and may reference private // repositories; the result cannot be assumed to be publicly readable. func LabelNotificationDetails() SecurityLabel { return PrivateUntrusted() }