Skip to content

feat: enforce repos="public" when workflow runs in a public repository#8933

Merged
lpcox merged 13 commits into
mainfrom
copilot/enforce-repos-public-policy
Jul 8, 2026
Merged

feat: enforce repos="public" when workflow runs in a public repository#8933
lpcox merged 13 commits into
mainfrom
copilot/enforce-repos-public-policy

Conversation

Copilot AI commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

When the gateway runs in a public repository, agents could accumulate private-repo secrecy tags via allow-only policies broader than repos="public", exposing a GitLost attack vector. This adds a runtime check that auto-forces repos="public" on all server guard policies when GITHUB_REPOSITORY identifies a public repo.

Config

New forcePublicRepos gateway field (MCP_GATEWAY_FORCE_PUBLIC_REPOS env var), enabled by default. Set to false to opt out (compiler emits this for private-to-public-flows: allow):

{ "gateway": { "forcePublicRepos": false } }

Implementation

  • internal/config/ForcePublicRepos *bool added to GatewayConfig and StdinGatewayConfig; EnvForcePublicRepos constant; JSON schema updated
  • internal/server/unified.go — caching fields (repoVisibilityOnce/Cached/CacheOK, forcePublicReposOnce/Result) on UnifiedServer
  • internal/server/guard_init.go:
    • resolveWorkflowRepoVisibility() — single sync.Once API call shared by both verifySinkVisibilityAtRuntime and the new force-public check (no double API call)
    • shouldForcePublicRepos() / computeForcePublicRepos() — check config opt-out, env opt-out, GITHUB_REPOSITORY presence, token availability, then API visibility
    • overrideToPublicScope() — patches both global cfg.GuardPolicy and per-server GuardPolicies to repos="public"; skips write-sink-only policies (mutually exclusive with allow-only)
    • registerGuard() — calls override before requireGuardPolicyIfGuardEnabled, so the patched value is the one persisted

Behavior

Condition Result
Public repo + allow-only repos="all" Overridden to repos="public", warning logged
Private/internal repo No change
forcePublicRepos: false in config No API call, no override
GITHUB_REPOSITORY unset or token missing Skip silently (fail-open)
API error Skip, log warning (fail-open)
Write-sink-only policy Skipped (incompatible with allow-only)

Tests

  • 14 unit tests in internal/server/force_public_repos_test.go
  • 4 integration tests in test/integration/force_public_repos_test.go covering public override, private no-op, config opt-out, and missing GITHUB_REPOSITORY

Copilot AI added 2 commits July 8, 2026 13:59
- Add ForcePublicRepos *bool config field to GatewayConfig and StdinGatewayConfig
- Add EnvForcePublicRepos constant (MCP_GATEWAY_FORCE_PUBLIC_REPOS)
- Update JSON schema with forcePublicRepos property
- Add caching fields to UnifiedServer for shared repo visibility
- Implement resolveWorkflowRepoVisibility() shared cache for both checks
- Implement shouldForcePublicRepos(), computeForcePublicRepos(), overrideToPublicScope()
- Override allow-only policy to repos=public in registerGuard() for public repos
- Add unit tests (14 tests) in force_public_repos_test.go
- Add integration tests in test/integration/force_public_repos_test.go
- Update docs/CONFIGURATION.md with forcePublicRepos field documentation
- Add MCP_GATEWAY_FORCE_PUBLIC_REPOS to AGENTS.md environment variables
- Add commented example to config.example.toml

Closes #8932
Copilot AI changed the title [WIP] Enforce repos="public" when workflow runs in a public repository feat: enforce repos="public" when workflow runs in a public repository Jul 8, 2026
Copilot AI requested a review from lpcox July 8, 2026 14:03
Copilot finished work on behalf of lpcox July 8, 2026 14:03
…d default

- Add writeSinkGuardPolicyConfig definition with typed accept/sink-visibility
- Add typed write-sink property to guard-policies in both server configs
- Add "default": true to forcePublicRepos schema field
- Update description to match spec PR (gh-aw#44305)
- Update integration tests: invalid sink-visibility now rejected by schema
  validation (exits with error) rather than graceful fallback
- Remove case-insensitive integration test (schema enforces lowercase enum)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lpcox
lpcox marked this pull request as ready for review July 8, 2026 14:18
Copilot AI review requested due to automatic review settings July 8, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens DIFC allow-only policy behavior for workflows running in public repositories by detecting repository visibility at runtime and force-scoping reads to repos="public" (with an opt-out), closing a potential private-data read/exfiltration path.

Changes:

  • Adds gateway.forcePublicRepos / MCP_GATEWAY_FORCE_PUBLIC_REPOS configuration and schema/docs support (default enabled).
  • Implements runtime repo-visibility caching and applies an allow-only repos="public" override during guard registration when the workflow repo is public.
  • Adds unit + integration tests for the new force-public behavior, and updates sink-visibility tests to reflect stricter schema validation.
Show a summary per file
File Description
test/integration/sink_visibility_test.go Updates sink-visibility integration tests to expect schema rejection for invalid values.
test/integration/force_public_repos_test.go Adds integration coverage for force-public-repos override and opt-out scenarios.
internal/server/unified.go Adds per-server cached fields for workflow repo visibility and force-public-repos decision.
internal/server/guard_init.go Implements cached visibility lookup, sink-visibility runtime verification changes, and allow-only override to repos="public".
internal/server/force_public_repos_test.go Adds unit tests for shouldForcePublicRepos/overrideToPublicScope behaviors.
internal/config/schema/mcp-gateway-config.schema.json Adds gateway.forcePublicRepos and a stricter schema for guard-policies.write-sink including sink-visibility enum validation.
internal/config/guard_policy_parse.go Introduces EnvForcePublicRepos constant for runtime feature gating.
internal/config/config_stdin.go Adds stdin JSON support for gateway.forcePublicRepos.
internal/config/config_core.go Adds ForcePublicRepos *bool to GatewayConfig (TOML + JSON).
docs/CONFIGURATION.md Documents forcePublicRepos semantics and defaults.
config.example.toml Documents TOML force_public_repos knob and intent.
AGENTS.md Documents MCP_GATEWAY_FORCE_PUBLIC_REPOS environment variable.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 12/12 changed files
  • Comments generated: 6
  • Review effort level: Low

Comment thread internal/server/guard_init.go Outdated
Comment on lines +493 to +504
// Config opt-out: forcePublicRepos=false in gateway config disables the feature.
if us.cfg != nil && us.cfg.Gateway != nil && us.cfg.Gateway.ForcePublicRepos != nil && !*us.cfg.Gateway.ForcePublicRepos {
logGuardInit.Print("shouldForcePublicRepos: disabled by config (forcePublicRepos=false)")
return false
}

// Env opt-out: MCP_GATEWAY_FORCE_PUBLIC_REPOS=false disables the feature.
// Default is true (feature enabled), so only an explicit "false" disables it.
if !envutil.GetEnvBool(config.EnvForcePublicRepos, true) {
logGuardInit.Printf("shouldForcePublicRepos: disabled by env var %s=false", config.EnvForcePublicRepos)
return false
}
Comment thread test/integration/sink_visibility_test.go
Comment thread test/integration/force_public_repos_test.go
Comment thread internal/server/force_public_repos_test.go Outdated
Comment thread internal/server/force_public_repos_test.go
lpcox and others added 8 commits July 8, 2026 07:42
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…y net

- Non-safe-outputs write-sink servers default to sink-visibility="public"
  (security-by-default: assume external sinks release data publicly)
- Add sinkVisibilityExemptServers config field for targeted opt-out
- Add runtime safety net for safe-outputs: if compiler omits sink-visibility
  but workflow repo is public, gateway forces "public" to prevent exfiltration
- Add isSafeOutputsServer() recognizing both canonical and legacy forms
- Add validateSinkVisibilityExemptServers() with unknown-server-ID warnings
- Update JSON schema with sinkVisibilityExemptServers field
- Add integration tests for default visibility and exempt server scenarios

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lpcox
lpcox merged commit 1b109a5 into main Jul 8, 2026
26 checks passed
@lpcox
lpcox deleted the copilot/enforce-repos-public-policy branch July 8, 2026 16:18
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.

Enforce repos="public" when workflow runs in a public repository

3 participants