diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml index 3fb51d0b972..bf3623f1398 100644 --- a/.github/workflows/auto-triage-issues.lock.yml +++ b/.github/workflows/auto-triage-issues.lock.yml @@ -852,7 +852,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 1bdda4c2e50..c2e52dd2419 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -869,7 +869,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 5cb2f8b2425..da06c25a9c9 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -1074,7 +1074,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/dataflow-pr-discussion-dataset.lock.yml b/.github/workflows/dataflow-pr-discussion-dataset.lock.yml index 0e0543bcb55..4afe398821d 100644 --- a/.github/workflows/dataflow-pr-discussion-dataset.lock.yml +++ b/.github/workflows/dataflow-pr-discussion-dataset.lock.yml @@ -1105,7 +1105,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml index 63a291061e6..f300b82cf27 100644 --- a/.github/workflows/discussion-task-miner.lock.yml +++ b/.github/workflows/discussion-task-miner.lock.yml @@ -848,7 +848,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/impeccable-skills-reviewer.lock.yml b/.github/workflows/impeccable-skills-reviewer.lock.yml index 6242b74f13c..d428df75cbd 100644 --- a/.github/workflows/impeccable-skills-reviewer.lock.yml +++ b/.github/workflows/impeccable-skills-reviewer.lock.yml @@ -890,7 +890,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index a79af5e7b81..50cfcda9967 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -994,7 +994,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 97627dafc61..3ae8bce1168 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -1206,7 +1206,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/mattpocock-skills-reviewer.lock.yml b/.github/workflows/mattpocock-skills-reviewer.lock.yml index fe6440c4027..b0e1ae3f29f 100644 --- a/.github/workflows/mattpocock-skills-reviewer.lock.yml +++ b/.github/workflows/mattpocock-skills-reviewer.lock.yml @@ -1037,7 +1037,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 9bba4beb061..d8b8b265187 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -862,7 +862,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/pr-code-quality-reviewer.lock.yml b/.github/workflows/pr-code-quality-reviewer.lock.yml index 117270bd36a..408273f9b3b 100644 --- a/.github/workflows/pr-code-quality-reviewer.lock.yml +++ b/.github/workflows/pr-code-quality-reviewer.lock.yml @@ -896,7 +896,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/pr-sous-chef.lock.yml b/.github/workflows/pr-sous-chef.lock.yml index 8a5ff9b80f8..c7251ad8afb 100644 --- a/.github/workflows/pr-sous-chef.lock.yml +++ b/.github/workflows/pr-sous-chef.lock.yml @@ -1201,7 +1201,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index 8d0f894e8dc..39ce8a7f07a 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -937,7 +937,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 00bfca09a53..037999ca4da 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1031,7 +1031,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"none","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"none","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml index 9ce48c31c54..3251069cadb 100644 --- a/.github/workflows/refiner.lock.yml +++ b/.github/workflows/refiner.lock.yml @@ -935,7 +935,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/skillet.lock.yml b/.github/workflows/skillet.lock.yml index c67004ee50f..c886d575109 100644 --- a/.github/workflows/skillet.lock.yml +++ b/.github/workflows/skillet.lock.yml @@ -905,7 +905,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml index b258db167f2..c969902ffc6 100644 --- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml @@ -1886,7 +1886,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/smoke-copilot-aoai-entra.lock.yml b/.github/workflows/smoke-copilot-aoai-entra.lock.yml index 5ee03b322b5..941dc7266ce 100644 --- a/.github/workflows/smoke-copilot-aoai-entra.lock.yml +++ b/.github/workflows/smoke-copilot-aoai-entra.lock.yml @@ -1887,7 +1887,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index f0ac42fe1f9..555b0b0ec9e 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1902,7 +1902,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 8bb571dfce6..bf848e7dce2 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -1003,7 +1003,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 01bdc9e7046..b6e46a918b5 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -838,7 +838,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml index 6ff1b7ef815..aa72789e685 100644 --- a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml +++ b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml @@ -805,7 +805,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index af8ea67ae50..b1f5fcb6259 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -876,7 +876,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"all"}}' + CLI_PROXY_POLICY: '{"allow-only":{"min-integrity":"approved","repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh" diff --git a/actions/setup/js/determine_automatic_lockdown.cjs b/actions/setup/js/determine_automatic_lockdown.cjs index 2bde6ffc068..b7ac6af06af 100644 --- a/actions/setup/js/determine_automatic_lockdown.cjs +++ b/actions/setup/js/determine_automatic_lockdown.cjs @@ -8,9 +8,14 @@ const { getErrorMessage } = require("./error_helpers.cjs"); * This step always sets `min_integrity` and `repos` outputs so that the GitHub MCP * `guard-policies` block is never populated with empty values: * - * - Public repositories: defaults to `min_integrity=approved`, `repos=all` + * - Public repositories: defaults to `min_integrity=approved`, `repos=public` * - Private/internal repositories: defaults to `min_integrity=none`, `repos=all` * + * When `GH_AW_PRIVATE_TO_PUBLIC_FLOWS=allow` is set (from `tools.github.private-to-public-flows: + * allow` in the workflow frontmatter), the `repos` default for public repositories is overridden + * to `all`, matching the behavior of private repositories, because the workflow author has + * explicitly opted in to cross-visibility data flows. + * * Whether a field is "already configured" is determined by the environment variables * GH_AW_GITHUB_MIN_INTEGRITY and GH_AW_GITHUB_REPOS, which are set at compile time * from the workflow's tools.github guard policy configuration. Pre-configured values @@ -25,6 +30,12 @@ const { getErrorMessage } = require("./error_helpers.cjs"); * @returns {Promise} */ async function determineAutomaticLockdown(github, context, core) { + const privateToPublicFlows = process.env.GH_AW_PRIVATE_TO_PUBLIC_FLOWS || ""; + const privateToPublicFlowsAllow = privateToPublicFlows === "allow"; + if (privateToPublicFlows && !privateToPublicFlowsAllow) { + core.warning(`GH_AW_PRIVATE_TO_PUBLIC_FLOWS='${privateToPublicFlows}' is not recognized; expected 'allow'. Treating as unset.`); + } + try { core.info("Determining automatic guard policy for GitHub MCP server"); @@ -55,7 +66,10 @@ async function determineAutomaticLockdown(github, context, core) { // Private/internal repos default to min_integrity=none; public repos to approved. // Either way, always emit outputs so guard-policies values are never empty. const defaultMinIntegrity = isPrivate ? "none" : "approved"; - const defaultRepos = "all"; + // Public repos default to repos=public to block access to private repos unless the + // workflow author has explicitly opted in via private-to-public-flows: allow. + // Private/internal repos default to repos=all (no cross-visibility restriction). + const defaultRepos = isPrivate || privateToPublicFlowsAllow ? "all" : "public"; // Set min_integrity if not already configured const resolvedMinIntegrity = configuredMinIntegrity || defaultMinIntegrity; @@ -79,7 +93,7 @@ async function determineAutomaticLockdown(github, context, core) { core.info("Automatic guard policy determination complete for private/internal repository"); } else { core.info("Automatic guard policy determination complete for public repository"); - core.info("GitHub MCP guard policy automatically applied for public repository. " + "min-integrity='approved' and repos='all' ensure only approved-integrity content is accessible."); + core.info(`GitHub MCP guard policy automatically applied for public repository. min-integrity='${resolvedMinIntegrity}' and repos='${resolvedRepos}'.`); } // Write resolved guard policy values to the step summary @@ -105,12 +119,13 @@ async function determineAutomaticLockdown(github, context, core) { await core.summary.addRaw(details).write(); } catch (error) { const errorMessage = getErrorMessage(error); + const fallbackRepos = privateToPublicFlowsAllow ? "all" : "public"; core.error(`Failed to determine automatic guard policy: ${errorMessage}`); // Default to safe guard policy for public repos on error core.setOutput("min_integrity", "approved"); - core.setOutput("repos", "all"); + core.setOutput("repos", fallbackRepos); core.setOutput("visibility", "public"); - core.warning("Failed to determine repository visibility. Defaulting to visibility='public' (conservative), min-integrity='approved', repos='all' for security."); + core.warning(`Failed to determine repository visibility. Defaulting to visibility='public' (conservative), min-integrity='approved', repos='${fallbackRepos}'.`); } } diff --git a/actions/setup/js/determine_automatic_lockdown.test.cjs b/actions/setup/js/determine_automatic_lockdown.test.cjs index 1e5bbb3402e..1159daee7de 100644 --- a/actions/setup/js/determine_automatic_lockdown.test.cjs +++ b/actions/setup/js/determine_automatic_lockdown.test.cjs @@ -44,12 +44,13 @@ describe("determine_automatic_lockdown", () => { delete process.env.CUSTOM_GITHUB_TOKEN; delete process.env.GH_AW_GITHUB_MIN_INTEGRITY; delete process.env.GH_AW_GITHUB_REPOS; + delete process.env.GH_AW_PRIVATE_TO_PUBLIC_FLOWS; // Import the module determineAutomaticLockdown = (await import("./determine_automatic_lockdown.cjs")).default; }); - it("should set min_integrity=approved and repos=all for public repository (no guard policy configured)", async () => { + it("should set min_integrity=approved and repos=public for public repository (no guard policy configured)", async () => { mockGithub.rest.repos.get.mockResolvedValue({ data: { private: false, @@ -64,7 +65,7 @@ describe("determine_automatic_lockdown", () => { repo: "test-repo", }); expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved"); - expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public"); expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); expect(mockCore.setOutput).not.toHaveBeenCalledWith("lockdown", expect.anything()); }); @@ -82,7 +83,7 @@ describe("determine_automatic_lockdown", () => { await determineAutomaticLockdown(mockGithub, mockContext, mockCore); expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "merged"); - expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public"); expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("min-integrity already configured as 'merged'")); }); @@ -148,12 +149,24 @@ describe("determine_automatic_lockdown", () => { expect(mockCore.error).toHaveBeenCalledWith("Failed to determine automatic guard policy: API request failed"); expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved"); - expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public"); expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); expect(mockCore.setOutput).not.toHaveBeenCalledWith("lockdown", expect.anything()); expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("Failed to determine repository visibility")); }); + it("should set repos=all on API failure when GH_AW_PRIVATE_TO_PUBLIC_FLOWS=allow", async () => { + process.env.GH_AW_PRIVATE_TO_PUBLIC_FLOWS = "allow"; + const error = new Error("API request failed"); + mockGithub.rest.repos.get.mockRejectedValue(error); + + await determineAutomaticLockdown(mockGithub, mockContext, mockCore); + + expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved"); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); + }); + it("should infer visibility from private field when visibility field is missing", async () => { mockGithub.rest.repos.get.mockResolvedValue({ data: { @@ -165,7 +178,7 @@ describe("determine_automatic_lockdown", () => { await determineAutomaticLockdown(mockGithub, mockContext, mockCore); expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved"); - expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public"); expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); }); @@ -231,7 +244,7 @@ describe("determine_automatic_lockdown", () => { expect(publicSummaryArg).toContain("approved"); expect(publicSummaryArg).toContain("automatic (public repo)"); expect(publicSummaryArg).toContain("repos"); - expect(publicSummaryArg).toContain("all"); + expect(publicSummaryArg).toContain("public"); expect(mockCore.summary.write).toHaveBeenCalled(); }); @@ -287,4 +300,38 @@ describe("determine_automatic_lockdown", () => { expect(privateSummaryArg).toContain("all"); expect(mockCore.summary.write).toHaveBeenCalled(); }); + + it("should set repos=all for public repository when GH_AW_PRIVATE_TO_PUBLIC_FLOWS=allow (private-to-public opt-in)", async () => { + process.env.GH_AW_PRIVATE_TO_PUBLIC_FLOWS = "allow"; + + mockGithub.rest.repos.get.mockResolvedValue({ + data: { + private: false, + visibility: "public", + }, + }); + + await determineAutomaticLockdown(mockGithub, mockContext, mockCore); + + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "all"); + expect(mockCore.setOutput).toHaveBeenCalledWith("min_integrity", "approved"); + expect(mockCore.setOutput).toHaveBeenCalledWith("visibility", "public"); + expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("repos not configured")); + }); + + it("should warn for unrecognized GH_AW_PRIVATE_TO_PUBLIC_FLOWS value", async () => { + process.env.GH_AW_PRIVATE_TO_PUBLIC_FLOWS = "Allow"; + + mockGithub.rest.repos.get.mockResolvedValue({ + data: { + private: false, + visibility: "public", + }, + }); + + await determineAutomaticLockdown(mockGithub, mockContext, mockCore); + + expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining("GH_AW_PRIVATE_TO_PUBLIC_FLOWS='Allow'")); + expect(mockCore.setOutput).toHaveBeenCalledWith("repos", "public"); + }); }); diff --git a/pkg/workflow/compiler_difc_proxy.go b/pkg/workflow/compiler_difc_proxy.go index e5dea577e7a..ceb42ba3480 100644 --- a/pkg/workflow/compiler_difc_proxy.go +++ b/pkg/workflow/compiler_difc_proxy.go @@ -498,13 +498,61 @@ func (c *Compiler) generateStartCliProxyStep(yaml *strings.Builder, data *Workfl } } -// defaultCliProxyPolicyJSON is the fallback guard policy for the CLI proxy when no -// guard policy is explicitly configured in the workflow frontmatter. +// defaultCliProxyPolicyJSONTemplate is the fallback guard policy template for the CLI proxy +// when no guard policy is explicitly configured in the workflow frontmatter. // The DIFC proxy requires a --policy flag to forward requests; without it, all API // calls return HTTP 503 with body "proxy enforcement not configured". -// This default allows all repos with no integrity filtering — the most permissive -// policy that still satisfies the proxy's requirement. -const defaultCliProxyPolicyJSON = `{"allow-only":{"repos":"all","min-integrity":"none"}}` +// This template references the determine-automatic-lockdown step outputs so that the +// repos restriction matches the repository visibility at runtime: +// - Public repos → repos="public" (blocks access to private repos) +// - Private repos → repos="all" (no cross-visibility restriction) +// +// This mirrors how the MCP Gateway references $GITHUB_MCP_GUARD_REPOS. +const defaultCliProxyPolicyJSONTemplate = `{"allow-only":{"repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}","min-integrity":"${{ steps.determine-automatic-lockdown.outputs.min_integrity }}"}}` + +const defaultReposStepOutputTemplate = "${{ steps.determine-automatic-lockdown.outputs.repos }}" +const defaultMinIntegrityStepOutputTemplate = "${{ steps.determine-automatic-lockdown.outputs.min_integrity }}" + +// buildCLIProxyPolicyJSON applies runtime defaults for any guard-policy fields omitted +// in workflow frontmatter while preserving explicitly configured fields. +func buildCLIProxyPolicyJSON(githubToolConfig map[string]any, data *WorkflowData) string { + policyJSON := getDIFCProxyPolicyJSON(githubToolConfig, data, data.SandboxConfig.MCP) + if policyJSON == "" { + return defaultCliProxyPolicyJSONTemplate + } + + repos, hasRepos := githubToolConfig["allowed-repos"] + if !hasRepos { + repos, hasRepos = githubToolConfig["repos"] + } + _, hasIntegrity := githubToolConfig["min-integrity"] + + var policy map[string]any + if err := json.Unmarshal([]byte(policyJSON), &policy); err != nil { + difcProxyLog.Printf("Failed to unmarshal CLI proxy policy JSON for runtime defaults: %v", err) + return policyJSON + } + allowOnly, ok := policy["allow-only"].(map[string]any) + if !ok { + return policyJSON + } + + if !hasRepos { + allowOnly["repos"] = defaultReposStepOutputTemplate + } else { + allowOnly["repos"] = normalizeGitHubRepositoryInReposScope(repos) + } + if !hasIntegrity { + allowOnly["min-integrity"] = defaultMinIntegrityStepOutputTemplate + } + + jsonBytes, err := json.Marshal(policy) + if err != nil { + difcProxyLog.Printf("Failed to marshal CLI proxy policy JSON with runtime defaults: %v", err) + return policyJSON + } + return string(jsonBytes) +} // buildStartCliProxyStepYAML returns the YAML for the "Start CLI proxy" step, // or an empty string if the proxy cannot be configured. @@ -519,14 +567,12 @@ func (c *Compiler) buildStartCliProxyStepYAML(data *WorkflowData) string { // Build the guard policy JSON (static fields only, plus reaction fields when enabled). // The CLI proxy requires a policy to forward requests — without one, all API - // calls return HTTP 503 ("proxy enforcement not configured"). Use the default - // permissive policy when no guard policy is configured in the frontmatter. + // calls return HTTP 503 ("proxy enforcement not configured"). When no guard policy + // is explicitly configured, reference the determine-automatic-lockdown step outputs + // so the repos restriction reflects repo visibility at runtime, matching the MCP + // Gateway's behavior. ensureDefaultMCPGatewayConfig(data) - policyJSON := getDIFCProxyPolicyJSON(githubToolConfig, data, data.SandboxConfig.MCP) - if policyJSON == "" { - policyJSON = defaultCliProxyPolicyJSON - difcProxyLog.Print("No guard policy configured, using default CLI proxy policy") - } + policyJSON := buildCLIProxyPolicyJSON(githubToolConfig, data) // Resolve the container image from the MCP gateway configuration containerImage := resolveProxyContainerImage(data.SandboxConfig.MCP) @@ -539,7 +585,7 @@ func (c *Compiler) buildStartCliProxyStepYAML(data *WorkflowData) string { if isAWFNetworkIsolationEnabled(data) { sb.WriteString(" GH_AW_NETWORK_ISOLATION: 'true'\n") } - fmt.Fprintf(&sb, " CLI_PROXY_POLICY: '%s'\n", policyJSON) + fmt.Fprintf(&sb, " CLI_PROXY_POLICY: %s\n", quoteYAMLEnvValue(policyJSON)) fmt.Fprintf(&sb, " CLI_PROXY_IMAGE: '%s'\n", containerImage) sb.WriteString(" run: |\n") sb.WriteString(" bash \"${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh\"\n") diff --git a/pkg/workflow/compiler_difc_proxy_test.go b/pkg/workflow/compiler_difc_proxy_test.go index aa3f6510c5a..9b4c9b78c11 100644 --- a/pkg/workflow/compiler_difc_proxy_test.go +++ b/pkg/workflow/compiler_difc_proxy_test.go @@ -870,12 +870,12 @@ func TestInjectProxyEnvIntoCustomSteps(t *testing.T) { } // TestBuildStartCliProxyStepYAML verifies that the CLI proxy step always emits -// CLI_PROXY_POLICY, using the default permissive policy when no guard policy is +// CLI_PROXY_POLICY, using a visibility-aware default when no guard policy is // configured in the frontmatter. func TestBuildStartCliProxyStepYAML(t *testing.T) { c := &Compiler{} - t.Run("emits default policy when no guard policy is configured", func(t *testing.T) { + t.Run("emits visibility-aware default policy when no guard policy is configured", func(t *testing.T) { data := &WorkflowData{ Tools: map[string]any{ "github": map[string]any{"toolsets": []string{"default"}}, @@ -886,11 +886,14 @@ func TestBuildStartCliProxyStepYAML(t *testing.T) { require.NotEmpty(t, result, "should emit CLI proxy step even without guard policy") assert.Contains(t, result, "CLI_PROXY_POLICY", "should always emit CLI_PROXY_POLICY") assert.Contains(t, result, `"allow-only"`, "default policy should contain allow-only") - assert.Contains(t, result, `"repos":"all"`, "default policy should allow all repos") - assert.Contains(t, result, `"min-integrity":"none"`, "default policy should have min-integrity none") + // Default policy references step output for repos (visibility-aware at runtime) + assert.Contains(t, result, `steps.determine-automatic-lockdown.outputs.repos`, "default policy should reference step output for repos") + assert.Contains(t, result, `steps.determine-automatic-lockdown.outputs.min_integrity`, "default policy should reference step output for min-integrity") + // Should NOT contain hardcoded static repos value + assert.NotContains(t, result, `"repos":"all"`, "default policy should not have hardcoded repos") }) - t.Run("emits default policy when github tool is nil", func(t *testing.T) { + t.Run("emits visibility-aware default policy when github tool is nil", func(t *testing.T) { data := &WorkflowData{ Tools: map[string]any{}, } @@ -898,7 +901,8 @@ func TestBuildStartCliProxyStepYAML(t *testing.T) { result := c.buildStartCliProxyStepYAML(data) require.NotEmpty(t, result, "should emit CLI proxy step even without github tool") assert.Contains(t, result, "CLI_PROXY_POLICY", "should always emit CLI_PROXY_POLICY") - assert.Contains(t, result, `"min-integrity":"none"`, "should use default min-integrity") + assert.Contains(t, result, `steps.determine-automatic-lockdown.outputs.repos`, "default policy should reference step output for repos") + assert.Contains(t, result, `steps.determine-automatic-lockdown.outputs.min_integrity`, "default policy should reference step output for min-integrity") }) t.Run("uses configured guard policy when present", func(t *testing.T) { @@ -918,6 +922,22 @@ func TestBuildStartCliProxyStepYAML(t *testing.T) { assert.Contains(t, result, `"repos":"owner/*"`, "should use configured repos") }) + t.Run("uses runtime repos default when repos is omitted but min-integrity is configured", func(t *testing.T) { + data := &WorkflowData{ + Tools: map[string]any{ + "github": map[string]any{ + "min-integrity": "approved", + }, + }, + } + + result := c.buildStartCliProxyStepYAML(data) + require.NotEmpty(t, result, "should emit CLI proxy step") + assert.Contains(t, result, `"min-integrity":"approved"`, "should preserve configured min-integrity") + assert.Contains(t, result, `steps.determine-automatic-lockdown.outputs.repos`, "should use runtime repos output when repos is omitted") + assert.NotContains(t, result, `"repos":"all"`, "should not hardcode repos=all when repos is omitted") + }) + t.Run("emits correct step structure", func(t *testing.T) { data := &WorkflowData{ Tools: map[string]any{ diff --git a/pkg/workflow/compiler_github_mcp_steps.go b/pkg/workflow/compiler_github_mcp_steps.go index d9e0915dc66..605f10d9326 100644 --- a/pkg/workflow/compiler_github_mcp_steps.go +++ b/pkg/workflow/compiler_github_mcp_steps.go @@ -51,6 +51,7 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder, // detect whether each field is already configured and avoid overriding it. configuredMinIntegrity := "" configuredRepos := "" + privateToPublicFlowsAllow := false if toolConfig, ok := githubTool.(map[string]any); ok { if v, exists := toolConfig["min-integrity"]; exists { configuredMinIntegrity = serializeEnvStringValue(v) @@ -61,6 +62,13 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder, } else if v, exists := toolConfig["repos"]; exists { configuredRepos = serializeEnvStringValue(v) } + // Detect private-to-public-flows: allow to inform the default repos value. + // When set to "allow", the user has explicitly opted in to cross-visibility data + // flows, so the repos default should be "all" rather than "public" even for + // public repositories. + if ptpFlows, _ := toolConfig["private-to-public-flows"].(string); ptpFlows == "allow" { + privateToPublicFlowsAllow = true + } } // Generate the step using the determine_automatic_lockdown.cjs action @@ -76,6 +84,9 @@ func (c *Compiler) generateGitHubMCPLockdownDetectionStep(yaml *strings.Builder, if configuredRepos != "" { fmt.Fprintf(yaml, " GH_AW_GITHUB_REPOS: %s\n", quoteYAMLEnvValue(configuredRepos)) } + if privateToPublicFlowsAllow { + yaml.WriteString(" GH_AW_PRIVATE_TO_PUBLIC_FLOWS: " + quoteYAMLEnvValue("allow") + "\n") + } yaml.WriteString(" with:\n") yaml.WriteString(" script: |\n") yaml.WriteString(" const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');\n") diff --git a/pkg/workflow/compiler_github_mcp_steps_test.go b/pkg/workflow/compiler_github_mcp_steps_test.go index 687bdf4d6fc..7449f7214fb 100644 --- a/pkg/workflow/compiler_github_mcp_steps_test.go +++ b/pkg/workflow/compiler_github_mcp_steps_test.go @@ -83,4 +83,26 @@ func TestGenerateGitHubMCPLockdownDetectionStepGeneratedWhenNoGuardPolicy(t *tes assert.Contains(t, output, "determine-automatic-lockdown", "detection step should be generated when no explicit guard policy") assert.NotContains(t, output, "GH_AW_GITHUB_MIN_INTEGRITY", "env var should not be present when min-integrity is not set") assert.NotContains(t, output, "GH_AW_GITHUB_REPOS", "env var should not be present when repos is not set") + assert.NotContains(t, output, "GH_AW_PRIVATE_TO_PUBLIC_FLOWS", "env var should not be present when private-to-public-flows is not set") +} + +func TestGenerateGitHubMCPLockdownDetectionStepEmitsPrivateToPublicFlowsEnv(t *testing.T) { + t.Parallel() + + var yaml strings.Builder + data := &WorkflowData{ + Tools: map[string]any{ + "github": map[string]any{ + "private-to-public-flows": "allow", + }, + }, + } + + NewCompiler().generateGitHubMCPLockdownDetectionStep(&yaml, data) + output := yaml.String() + + assert.Contains(t, output, "determine-automatic-lockdown", "detection step should be generated") + // When private-to-public-flows: allow is set, the step must receive the env var so the + // determine_automatic_lockdown.cjs script can output repos=all instead of repos=public. + assert.Contains(t, output, "GH_AW_PRIVATE_TO_PUBLIC_FLOWS: 'allow'", "env var should be emitted when private-to-public-flows is allow") } diff --git a/pkg/workflow/compiler_validators.go b/pkg/workflow/compiler_validators.go index 32c75b3e2b4..effcca7d659 100644 --- a/pkg/workflow/compiler_validators.go +++ b/pkg/workflow/compiler_validators.go @@ -182,6 +182,7 @@ func (c *Compiler) validateCoreToolConfiguration(workflowData *WorkflowData, mar {logMessage: "Validating labels", validateFn: func() error { return validateLabels(workflowData) }}, {logMessage: "Validating workflow_dispatch input requirements for command triggers", validateFn: func() error { return validateCommandWorkflowDispatchInputs(workflowData) }}, {logMessage: "Validating max-daily-ai-credits frontmatter", validateFn: func() error { return validateMaxDailyAICFrontmatter(workflowData) }}, + {logMessage: "Validating private-to-public-flows string value", validateFn: func() error { return validatePrivateToPublicFlowsStringValue(workflowData) }}, {logMessage: "Validating private-to-public-flows server IDs", validateFn: func() error { return validatePrivateToPublicFlowsServerIDs(workflowData) }}, } // This validation is intentionally outside the table below because strict mode diff --git a/pkg/workflow/private_to_public_flows_test.go b/pkg/workflow/private_to_public_flows_test.go index 0c2e3c316c2..195ac9366f2 100644 --- a/pkg/workflow/private_to_public_flows_test.go +++ b/pkg/workflow/private_to_public_flows_test.go @@ -389,6 +389,38 @@ func TestValidatePrivateToPublicFlowsServerIDs(t *testing.T) { }) } +func TestValidatePrivateToPublicFlowsStringValue(t *testing.T) { + t.Run("allow string accepted", func(t *testing.T) { + wd := &WorkflowData{ + ParsedTools: &Tools{ + GitHub: &GitHubToolConfig{PrivateToPublicFlows: "allow"}, + }, + } + assert.NoError(t, validatePrivateToPublicFlowsStringValue(wd)) + }) + + t.Run("invalid string rejected", func(t *testing.T) { + wd := &WorkflowData{ + ParsedTools: &Tools{ + GitHub: &GitHubToolConfig{PrivateToPublicFlows: "Allow"}, + }, + } + err := validatePrivateToPublicFlowsStringValue(wd) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid value") + assert.Contains(t, err.Error(), "Allow") + }) + + t.Run("list form skipped", func(t *testing.T) { + wd := &WorkflowData{ + ParsedTools: &Tools{ + GitHub: &GitHubToolConfig{PrivateToPublicFlows: []string{"github"}}, + }, + } + assert.NoError(t, validatePrivateToPublicFlowsStringValue(wd)) + }) +} + // TestIsSafeMCPServerID tests the shell-safety identifier check. func TestIsSafeMCPServerID(t *testing.T) { safe := []string{"github", "my-server", "my_server", "server123", "UPPER-CASE"} diff --git a/pkg/workflow/strict_mode_network_validation.go b/pkg/workflow/strict_mode_network_validation.go index 94e743e2bc1..7623cf9838e 100644 --- a/pkg/workflow/strict_mode_network_validation.go +++ b/pkg/workflow/strict_mode_network_validation.go @@ -173,6 +173,25 @@ func (c *Compiler) validateStrictTools(frontmatter map[string]any) error { return nil } +// validatePrivateToPublicFlowsStringValue validates that the string form of +// private-to-public-flows uses the only supported value: "allow". +func validatePrivateToPublicFlowsStringValue(workflowData *WorkflowData) error { + if workflowData == nil || workflowData.ParsedTools == nil || workflowData.ParsedTools.GitHub == nil { + return nil + } + value, ok := workflowData.ParsedTools.GitHub.PrivateToPublicFlows.(string) + if !ok || value == "" || value == "allow" { + return nil + } + + return NewValidationError( + "tools.github.private-to-public-flows", + value, + fmt.Sprintf("invalid value %q; expected \"allow\" or a list of MCP server IDs", value), + "Set tools.github.private-to-public-flows to \"allow\" for blanket opt-in, or use a list of MCP server IDs for targeted exemptions.", + ) +} + // validatePrivateToPublicFlowsServerIDs validates that every server ID in the list form of // private-to-public-flows matches a declared MCP server in the workflow's tools list. // Per MCP Gateway Specification Section 10.9.2, unknown IDs must be rejected at compile time. @@ -180,6 +199,7 @@ func validatePrivateToPublicFlowsServerIDs(workflowData *WorkflowData) error { if workflowData == nil || workflowData.ParsedTools == nil || workflowData.ParsedTools.GitHub == nil { return nil } + servers, ok := workflowData.ParsedTools.GitHub.PrivateToPublicFlows.([]string) if !ok || len(servers) == 0 { return nil diff --git a/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden b/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden index 6aba3150c70..8a4b05e943d 100644 --- a/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden +++ b/pkg/workflow/testdata/TestWasmGolden_AllEngines/pi.golden @@ -451,7 +451,7 @@ jobs: GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }} GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }} GH_AW_NETWORK_ISOLATION: 'true' - CLI_PROXY_POLICY: '{"allow-only":{"repos":"all","min-integrity":"none"}}' + CLI_PROXY_POLICY: '{"allow-only":{"repos":"${{ steps.determine-automatic-lockdown.outputs.repos }}","min-integrity":"${{ steps.determine-automatic-lockdown.outputs.min_integrity }}"}}' CLI_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.1' run: | bash "${RUNNER_TEMP}/gh-aw/actions/start_cli_proxy.sh"