Skip to content

feat: add force-public-repos runtime check to proxy mode#8948

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

feat: add force-public-repos runtime check to proxy mode#8948
lpcox merged 2 commits into
mainfrom
copilot/enforce-repos-public-policy

Conversation

@lpcox

@lpcox lpcox commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the same forcePublicRepos defense-in-depth runtime check to the CLI proxy (awmg proxy) that already exists in the MCP gateway (unified mode).

Problem

The proxy receives its allow-only policy as a pre-built --policy JSON string from the compiler. If the compiler misconfigures the policy (e.g., repos: "all" when the workflow repo is public), the proxy would trust it and allow the agent to read private repos through the gh CLI.

Solution

Before passing the policy to the WASM guard, the proxy now:

  1. Reads GITHUB_REPOSITORY
  2. Calls GET /repos/{owner}/{repo} to verify visibility
  3. If the repo is public, overrides repos to "public" in the allow-only policy

This prevents agents in public-repo workflows from reading private repos, regardless of what policy the compiler passed.

Opt-out

Set MCP_GATEWAY_FORCE_PUBLIC_REPOS=false to disable (set by the compiler when private-to-public-flows: allow is declared in workflow frontmatter).

Fail-open semantics

The override is skipped when:

  • MCP_GATEWAY_FORCE_PUBLIC_REPOS=false
  • GITHUB_REPOSITORY is not set
  • No GitHub token is available
  • The API call fails

In these cases, the compiled policy is used as-is.

Companion PR

Copilot AI review requested due to automatic review settings July 8, 2026 16:40

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

Adds a defense-in-depth “force public repos” runtime check to awmg proxy, mirroring the unified MCP gateway behavior, so a miscompiled allow-only policy can’t accidentally permit private-repo reads in public-repo workflows.

Changes:

  • Resolve an “effective” proxy guard policy by optionally rewriting the allow-only repos scope to "public" when the workflow repo is confirmed public via the GitHub REST API.
  • Add proxyForcePublicReposIfNeeded helper implementing opt-out + fail-open semantics and handling canonical/legacy allow-only keys.
Show a summary per file
File Description
internal/cmd/proxy.go Adds runtime repo-visibility verification and rewrites the proxy’s guard policy to force repos="public" when appropriate.

Review details

Tip

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

  • Files reviewed: 1/1 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread internal/cmd/proxy.go
Comment on lines +402 to +406
vis, err := githubhttp.FetchRepoVisibility(ctx, apiURL, nwo, "token "+authToken)
if err != nil {
logger.LogWarn("difc", "forcePublicRepos: failed to determine visibility for %s (fail-open): %v", nwo, err)
return policyJSON
}
Comment thread internal/cmd/proxy.go
Comment on lines +428 to +435
if allowOnly == nil {
policyMap["allow-only"] = map[string]interface{}{
"repos": "public",
"min-integrity": "none",
}
} else {
allowOnly["repos"] = "public"
}
Comment thread internal/cmd/proxy.go
Comment on lines +225 to +231
// Defense-in-depth: force repos="public" when running in a public repository.
// This overrides the compiled policy to prevent agents from reading private
// repos, even if the compiler misconfigured the allow-only scope.
effectivePolicy := proxyPolicy
if effectivePolicy != "" {
effectivePolicy = proxyForcePublicReposIfNeeded(ctx, effectivePolicy, token, apiURL)
}
When the workflow runs in a public repo (GITHUB_REPOSITORY is public),
the proxy now overrides the allow-only policy to repos="public" before
passing it to the WASM guard. This prevents agents from reading private
repos through the gh CLI proxy, even if the compiler passed a permissive
policy.

The override can be explicitly disabled via:
- MCP_GATEWAY_FORCE_PUBLIC_REPOS=false environment variable

Fail-open semantics: if GITHUB_REPOSITORY is unset, no token is
available, or the API call fails, the compiled policy is used as-is.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lpcox lpcox force-pushed the copilot/enforce-repos-public-policy branch from f07dde0 to e9b4156 Compare July 8, 2026 16:46
@lpcox

lpcox commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

@copilot address review feedback

Copilot AI commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

@copilot address review feedback

Addressed all three review comments in commit $(git -C /home/runner/work/gh-aw-mcpg/gh-aw-mcpg rev-parse --short HEAD):

  1. 10s timeoutproxyForcePublicReposIfNeeded now wraps the FetchRepoVisibility call with context.WithTimeout(ctx, 10*time.Second), matching the unified gateway's approach in guard_init.go.

  2. config.IntegrityNone constant – replaced the raw "none" string literal when injecting a minimal allow-only policy.

  3. Unit tests – added 8 tests in TestProxyForcePublicReposIfNeeded covering: public repo override, private repo passthrough, opt-out via flag, missing GITHUB_REPOSITORY, missing token, API failure fail-open, legacy allowonly key, and no allow-only key injection.

Copilot finished work on behalf of lpcox July 8, 2026 16:54
Five integration tests covering the --force-public-repos flag:
- Public workflow repo → forces repos=public override
- --force-public-repos=false flag → disables override
- MCP_GATEWAY_FORCE_PUBLIC_REPOS=false env var → disables override
- No GITHUB_REPOSITORY → no override applied
- Private workflow repo → no override applied

Tests require WASM guard (skipped gracefully when unavailable).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

3 participants