Skip to content

Commit f0b0d23

Browse files
Copilotlpcox
andauthored
fix: always emit CLI_PROXY_POLICY env var for CLI proxy (#25419)
* Initial plan * fix: always emit CLI_PROXY_POLICY env var with default when no guard policy configured The DIFC proxy requires a --policy flag to forward requests. Without it, all API calls return HTTP 503 with body "proxy enforcement not configured". When no guard policy is explicitly configured in the workflow frontmatter, the CLI proxy step now emits a default permissive policy: {"allow-only":{"repos":"all","min-integrity":"none"}} This ensures the CLI proxy always has a policy to forward requests, matching the behavior expected by the DIFC proxy (mcpg in proxy mode). Fixes #25373 Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2a1ff7ff-3323-425d-beb4-ab745e5fe0d1 Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
1 parent 4d8360a commit f0b0d23

2 files changed

Lines changed: 82 additions & 6 deletions

File tree

pkg/workflow/compiler_difc_proxy.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,14 @@ func (c *Compiler) generateStartCliProxyStep(yaml *strings.Builder, data *Workfl
357357
}
358358
}
359359

360+
// defaultCliProxyPolicyJSON is the fallback guard policy for the CLI proxy when no
361+
// guard policy is explicitly configured in the workflow frontmatter.
362+
// The DIFC proxy requires a --policy flag to forward requests; without it, all API
363+
// calls return HTTP 503 with body "proxy enforcement not configured".
364+
// This default allows all repos with no integrity filtering — the most permissive
365+
// policy that still satisfies the proxy's requirement.
366+
const defaultCliProxyPolicyJSON = `{"allow-only":{"repos":"all","min-integrity":"none"}}`
367+
360368
// buildStartCliProxyStepYAML returns the YAML for the "Start CLI proxy" step,
361369
// or an empty string if the proxy cannot be configured.
362370
func (c *Compiler) buildStartCliProxyStepYAML(data *WorkflowData) string {
@@ -368,10 +376,15 @@ func (c *Compiler) buildStartCliProxyStepYAML(data *WorkflowData) string {
368376
customGitHubToken := getGitHubToken(githubTool)
369377
effectiveToken := getEffectiveGitHubToken(customGitHubToken)
370378

371-
// Build the guard policy JSON (static fields only)
379+
// Build the guard policy JSON (static fields only).
380+
// The CLI proxy requires a policy to forward requests — without one, all API
381+
// calls return HTTP 503 ("proxy enforcement not configured"). Use the default
382+
// permissive policy when no guard policy is configured in the frontmatter.
372383
policyJSON := getDIFCProxyPolicyJSON(githubTool)
373-
// An empty policy is acceptable — the proxy still provides gh CLI routing
374-
// without guard filtering when no min-integrity is configured.
384+
if policyJSON == "" {
385+
policyJSON = defaultCliProxyPolicyJSON
386+
difcProxyLog.Print("No guard policy configured, using default CLI proxy policy")
387+
}
375388

376389
// Resolve the container image from the MCP gateway configuration
377390
ensureDefaultMCPGatewayConfig(data)
@@ -389,9 +402,7 @@ func (c *Compiler) buildStartCliProxyStepYAML(data *WorkflowData) string {
389402
sb.WriteString(" env:\n")
390403
fmt.Fprintf(&sb, " GH_TOKEN: %s\n", effectiveToken)
391404
sb.WriteString(" GITHUB_SERVER_URL: ${{ github.server_url }}\n")
392-
if policyJSON != "" {
393-
fmt.Fprintf(&sb, " CLI_PROXY_POLICY: '%s'\n", policyJSON)
394-
}
405+
fmt.Fprintf(&sb, " CLI_PROXY_POLICY: '%s'\n", policyJSON)
395406
fmt.Fprintf(&sb, " CLI_PROXY_IMAGE: '%s'\n", containerImage)
396407
sb.WriteString(" run: |\n")
397408
sb.WriteString(" bash \"${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh\"\n")

pkg/workflow/compiler_difc_proxy_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,3 +693,68 @@ func TestGenerateSetGHRepoAfterDIFCProxyStep(t *testing.T) {
693693
assert.NotContains(t, result, "GH_HOST", "should not touch GH_HOST")
694694
})
695695
}
696+
697+
// TestBuildStartCliProxyStepYAML verifies that the CLI proxy step always emits
698+
// CLI_PROXY_POLICY, using the default permissive policy when no guard policy is
699+
// configured in the frontmatter.
700+
func TestBuildStartCliProxyStepYAML(t *testing.T) {
701+
c := &Compiler{}
702+
703+
t.Run("emits default policy when no guard policy is configured", func(t *testing.T) {
704+
data := &WorkflowData{
705+
Tools: map[string]any{
706+
"github": map[string]any{"toolsets": []string{"default"}},
707+
},
708+
}
709+
710+
result := c.buildStartCliProxyStepYAML(data)
711+
require.NotEmpty(t, result, "should emit CLI proxy step even without guard policy")
712+
assert.Contains(t, result, "CLI_PROXY_POLICY", "should always emit CLI_PROXY_POLICY")
713+
assert.Contains(t, result, `"allow-only"`, "default policy should contain allow-only")
714+
assert.Contains(t, result, `"repos":"all"`, "default policy should allow all repos")
715+
assert.Contains(t, result, `"min-integrity":"none"`, "default policy should have min-integrity none")
716+
})
717+
718+
t.Run("emits default policy when github tool is nil", func(t *testing.T) {
719+
data := &WorkflowData{
720+
Tools: map[string]any{},
721+
}
722+
723+
result := c.buildStartCliProxyStepYAML(data)
724+
require.NotEmpty(t, result, "should emit CLI proxy step even without github tool")
725+
assert.Contains(t, result, "CLI_PROXY_POLICY", "should always emit CLI_PROXY_POLICY")
726+
assert.Contains(t, result, `"min-integrity":"none"`, "should use default min-integrity")
727+
})
728+
729+
t.Run("uses configured guard policy when present", func(t *testing.T) {
730+
data := &WorkflowData{
731+
Tools: map[string]any{
732+
"github": map[string]any{
733+
"min-integrity": "approved",
734+
"allowed-repos": "owner/*",
735+
},
736+
},
737+
}
738+
739+
result := c.buildStartCliProxyStepYAML(data)
740+
require.NotEmpty(t, result, "should emit CLI proxy step")
741+
assert.Contains(t, result, "CLI_PROXY_POLICY", "should emit CLI_PROXY_POLICY")
742+
assert.Contains(t, result, `"min-integrity":"approved"`, "should use configured min-integrity")
743+
assert.Contains(t, result, `"repos":"owner/*"`, "should use configured repos")
744+
})
745+
746+
t.Run("emits correct step structure", func(t *testing.T) {
747+
data := &WorkflowData{
748+
Tools: map[string]any{
749+
"github": map[string]any{"toolsets": []string{"default"}},
750+
},
751+
}
752+
753+
result := c.buildStartCliProxyStepYAML(data)
754+
assert.Contains(t, result, "name: Start CLI proxy", "should have correct step name")
755+
assert.Contains(t, result, "GH_TOKEN:", "should include GH_TOKEN")
756+
assert.Contains(t, result, "GITHUB_SERVER_URL:", "should include GITHUB_SERVER_URL")
757+
assert.Contains(t, result, "CLI_PROXY_IMAGE:", "should include CLI_PROXY_IMAGE")
758+
assert.Contains(t, result, "start_cli_proxy.sh", "should reference the start script")
759+
})
760+
}

0 commit comments

Comments
 (0)