From e29d04079d4140e06c1279f50b206fc0d7766f9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:04:57 +0000 Subject: [PATCH 1/3] Initial plan From be7426ec146476e38bde6267162cbf7b337f33ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:13:43 +0000 Subject: [PATCH 2/3] Initial plan Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/cc67400c-5f09-4a60-9a5a-ade81bfb2223 --- .github/workflows/smoke-codex.lock.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 70d93a5e10..cf7fca274c 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -450,13 +450,20 @@ jobs: { "description": "Add the 'smoked' label to the current pull request (can only be called once)", "inputSchema": { - "additionalProperties": true, + "additionalProperties": false, "properties": { - "payload": { - "description": "JSON-encoded payload to pass to the action", + "labels": { + "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.", + "type": "string" + }, + "number": { + "description": "The number of the issue or pull request.", "type": "string" } }, + "required": [ + "labels" + ], "type": "object" }, "name": "add_smoked_label" @@ -1561,7 +1568,8 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} with: - payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }} + labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }} + number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }} - name: Upload safe output items if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 From fc48199fc4e5abd4d3d7f95ffba950caa5b68344 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 02:32:00 +0000 Subject: [PATCH 3/3] Fix MCP Gateway failure: default repos to all when only min-integrity is specified The MCP Gateway v0.1.19 now requires `repos` to be present in the `allow-only` guard policy. Previously, when a workflow configured only `min-integrity` without `repos`, the compiled lock file would generate: "guard-policies": { "allow-only": { "min-integrity": "approved" } } This caused the MCP Gateway to fail with: "invalid guard policy JSON: allow-only must include repos" Fix: Update getGitHubGuardPolicies() to default repos to "all" when min-integrity is set but repos is not. This is consistent with the existing documentation and tools_validation.go behavior. Also update tests and recompile daily-issues-report.lock.yml. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Agent-Logs-Url: https://github.com/github/gh-aw/sessions/cc67400c-5f09-4a60-9a5a-ade81bfb2223 --- .../workflows/daily-issues-report.lock.yml | 15 ++++++- pkg/cli/compile_guard_policy_test.go | 42 +++++++++++++++++++ pkg/workflow/mcp_github_config.go | 8 ++++ pkg/workflow/safeoutputs_guard_policy_test.go | 9 +++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 73588114ad..d1d5229708 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -642,6 +642,11 @@ jobs: [mcp_servers.safeoutputs.headers] Authorization = "$GH_AW_SAFE_OUTPUTS_API_KEY" + + [mcp_servers.safeoutputs."guard-policies"] + + [mcp_servers.safeoutputs."guard-policies".write-sink] + accept = ["*"] GH_AW_MCP_CONFIG_EOF # Generate JSON config for MCP gateway @@ -658,7 +663,8 @@ jobs: }, "guard-policies": { "allow-only": { - "min-integrity": "approved" + "min-integrity": "approved", + "repos": "all" } } }, @@ -667,6 +673,13 @@ jobs: "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", "headers": { "Authorization": "$GH_AW_SAFE_OUTPUTS_API_KEY" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } } } }, diff --git a/pkg/cli/compile_guard_policy_test.go b/pkg/cli/compile_guard_policy_test.go index ea22dd1bf3..9dc15a850e 100644 --- a/pkg/cli/compile_guard_policy_test.go +++ b/pkg/cli/compile_guard_policy_test.go @@ -146,3 +146,45 @@ This workflow specifies repos without min-integrity. }) } } + +// TestGuardPolicyMinIntegrityOnlyCompiledOutput verifies that when only min-integrity is +// specified (without repos), the compiled lock file includes repos="all" in the guard policy. +// This is a regression test for the MCP Gateway requirement that allow-only must include repos. +func TestGuardPolicyMinIntegrityOnlyCompiledOutput(t *testing.T) { + workflowContent := `--- +on: + workflow_dispatch: +permissions: + contents: read +engine: copilot +tools: + github: + min-integrity: approved +--- + +# Guard Policy Test + +This workflow uses min-integrity without specifying repos. +` + + tmpDir := t.TempDir() + workflowPath := filepath.Join(tmpDir, "test-guard-policy.md") + err := os.WriteFile(workflowPath, []byte(workflowContent), 0644) + require.NoError(t, err, "Failed to write workflow file") + + compiler := workflow.NewCompiler() + err = CompileWorkflowWithValidation(compiler, workflowPath, false, false, false, false, false, false) + require.NoError(t, err, "Expected compilation to succeed") + + // Read the compiled lock file and verify it contains the correct guard-policies JSON block. + // The MCP Gateway requires repos to be present in the allow-only policy. + lockFilePath := filepath.Join(tmpDir, "test-guard-policy.lock.yml") + lockFileBytes, err := os.ReadFile(lockFilePath) + require.NoError(t, err, "Failed to read compiled lock file") + + lockFileContent := string(lockFileBytes) + // Check that the guard-policies allow-only block contains both repos=all and min-integrity=approved + // in the correct JSON structure expected by the MCP Gateway. + assert.Contains(t, lockFileContent, `"guard-policies": {`+"\n"+` "allow-only": {`+"\n"+` "min-integrity": "approved",`+"\n"+` "repos": "all"`, + "Compiled lock file must include repos=all and min-integrity=approved in the guard-policies allow-only block") +} diff --git a/pkg/workflow/mcp_github_config.go b/pkg/workflow/mcp_github_config.go index 850b5af33a..07ff4230e3 100644 --- a/pkg/workflow/mcp_github_config.go +++ b/pkg/workflow/mcp_github_config.go @@ -235,6 +235,10 @@ func getGitHubAllowedTools(githubTool any) []string { // getGitHubGuardPolicies extracts guard policies from GitHub tool configuration. // It reads the flat repos/min-integrity fields and wraps them for MCP gateway rendering. +// When min-integrity is set but repos is not, repos defaults to "all" because the MCP +// Gateway requires repos to be present in the allow-only policy. +// Note: repos-only (without min-integrity) is rejected earlier by validateGitHubGuardPolicy, +// so this function will never be called with repos but without min-integrity in practice. // Returns nil if no guard policies are configured. func getGitHubGuardPolicies(githubTool any) map[string]any { if toolConfig, ok := githubTool.(map[string]any); ok { @@ -244,6 +248,10 @@ func getGitHubGuardPolicies(githubTool any) map[string]any { policy := map[string]any{} if hasRepos { policy["repos"] = repos + } else { + // Default repos to "all" when min-integrity is specified without repos. + // The MCP Gateway requires repos in the allow-only policy. + policy["repos"] = "all" } if hasIntegrity { policy["min-integrity"] = integrity diff --git a/pkg/workflow/safeoutputs_guard_policy_test.go b/pkg/workflow/safeoutputs_guard_policy_test.go index b912f49eb8..2f28e1281c 100644 --- a/pkg/workflow/safeoutputs_guard_policy_test.go +++ b/pkg/workflow/safeoutputs_guard_policy_test.go @@ -197,8 +197,13 @@ func TestDeriveSafeOutputsGuardPolicyFromGitHub(t *testing.T) { githubTool: map[string]any{ "min-integrity": "approved", }, - expectNil: true, - description: "No repos means no guard-policy for safeoutputs", + expectedPolicies: map[string]any{ + "write-sink": map[string]any{ + "accept": []string{"*"}, + }, + }, + expectNil: false, + description: "No repos defaults to all, which means accept=[*] for safeoutputs", }, { name: "no guard policy at all",