Skip to content

CLI proxy default policy should respect repo visibility (block private repos in public repos) #45157

Description

@lpcox

Problem

When no explicit repos / allowed-repos is configured in the workflow frontmatter, the CLI proxy falls back to defaultCliProxyPolicyJSON:

const defaultCliProxyPolicyJSON = `{"allow-only":{"repos":"all","min-integrity":"none"}}`

This means the gh CLI inside AWF can read any repo the token can access — including private repos — even when the workflow runs in a public repository.

The MCP Gateway path does not have this problem because it references $GITHUB_MCP_GUARD_REPOS (an env var set by the determine-automatic-lockdown step at runtime), which correctly restricts access based on repo visibility.

Evidence

In github/gh-aw-firewall smoke-sink-visibility-blocked, the CLI proxy allowed gh repo view github/agentic-workflows (a private repo) despite the workflow running in a public repo with no private-to-public-flows: allow opt-out. The CLI proxy logs confirm:

{"event":"exec_done","args":["repo","view","github/agentic-workflows","--json","name"],"exitCode":0}

The MCPG proxy logs show:

[difc:evaluator] Read access allowed: resource=file:github/agentic-workflows

Because agentSecrecy=[] and agentIntegrity=[none:all] — no repo-visibility constraint was applied.

Desired Behavior

Repo visibility Explicit repos in frontmatter CLI proxy repos value
Public (none) "public" — block private repos
Public "all" "all" — user explicitly opted in
Public "public" "public"
Public ["org/*", "org/repo"] ["org/*", "org/repo"] — pattern limits scope
Private (none) "all" — private repos can read own + other private repos
Private any explicit value whatever user specified
Any private-to-public-flows: allow whatever repos resolves to (forcePublicRepos=false disables extra checks)

Key principle: When no guard policy is explicitly configured, the CLI proxy should match the MCP Gateway's behavior — use the repo visibility as determined at runtime by determine-automatic-lockdown.

Step Ordering (Critical Constraint)

The agent job steps execute in this order:

1. determine-automatic-lockdown    ← outputs: visibility, (proposed) repos
2. parse-guard-vars                ← outputs: blocked_users, trusted_users, approval_labels
3. Start MCP Gateway               ← references steps.determine-automatic-lockdown.outputs.*
4. Mount MCP servers as CLIs
5. Start CLI Proxy                 ← currently uses compile-time static CLI_PROXY_POLICY
6. Execute agent (AWF)

The CLI proxy starts after determine-automatic-lockdown, so it can reference its step outputs. The code comment about "proxy starts before parse-guard-vars" refers to the fact that blocked-users/trusted-users/approval-labels are not available — but repos and visibility ARE available.

Implementation Plan

1. actions/setup/js/determine_automatic_lockdown.cjs

Add a new step output repos that reflects the default repos scope based on repo visibility:

  • Public repo + no explicit GH_AW_GITHUB_REPOS env → output repos = "public"
  • Private repo + no explicit GH_AW_GITHUB_REPOS env → output repos = "all"
  • Explicit GH_AW_GITHUB_REPOS env set → output repos = <that value> (passthrough)

2. pkg/workflow/compiler_difc_proxy.gobuildStartCliProxyStepYAML()

When no explicit guard policy is configured (the policyJSON == "" branch that falls back to defaultCliProxyPolicyJSON), instead of using the hardcoded default, emit a policy that references the step output:

CLI_PROXY_POLICY: '{"allow-only":{"repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}","min-integrity":"${{ steps.determine-automatic-lockdown.outputs.min_integrity || ''none'' }}"}}'

This mirrors how the MCP Gateway already references $GITHUB_MCP_GUARD_REPOS / $GITHUB_MCP_GUARD_MIN_INTEGRITY.

3. When explicit repos IS configured

No change needed — getDIFCProxyPolicyJSON() already emits the user's explicit value. The fix only applies to the fallback/default case.

4. private-to-public-flows: allow interaction

When private-to-public-flows: allow is set, the compiler already emits forcePublicRepos: false in the gateway config. For the CLI proxy, this means the repos value should remain whatever the user configured (or "all" if not configured). The determine-automatic-lockdown step should detect this opt-out and output repos = "all" accordingly.

Open question: Does determine_automatic_lockdown.cjs receive enough context to know about private-to-public-flows? If not, the compiler could pass it as an env var (e.g., GH_AW_PRIVATE_TO_PUBLIC_FLOWS: "allow").

5. Tests

  • Update compiler_difc_proxy_test.go — verify that when no guard policy is configured, the emitted CLI_PROXY_POLICY references the step output instead of the hardcoded default
  • Update determine_automatic_lockdown.test.cjs — verify the new repos output for public/private repos
  • The existing smoke-sink-visibility-blocked workflow in gh-aw-firewall will serve as the integration test

Files to Change

File Change
actions/setup/js/determine_automatic_lockdown.cjs Add repos output based on visibility
actions/setup/js/determine_automatic_lockdown.test.cjs Test new output
pkg/workflow/compiler_difc_proxy.go Reference step output in default CLI proxy policy
pkg/workflow/compiler_difc_proxy_test.go Update expectations

Related

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions