From 3481228aeed5d0562ccee7f5b7f5ed0f504cd89c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 02:00:41 +0000 Subject: [PATCH 1/4] Initial plan From 15d23a81f6deaf6ea6892c302b700fbc51dfd06e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 02:12:36 +0000 Subject: [PATCH 2/4] Fix template-injection by replacing template expressions with env vars in MCP config Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../agent-performance-analyzer.lock.yml | 4 +- .../workflows/agent-persona-explorer.lock.yml | 4 +- .github/workflows/archie.lock.yml | 4 +- .github/workflows/audit-workflows.lock.yml | 4 +- .github/workflows/brave.lock.yml | 2 +- .github/workflows/cloclo.lock.yml | 8 +- .../workflows/daily-cli-tools-tester.lock.yml | 4 +- .../workflows/daily-compiler-quality.lock.yml | 4 +- .github/workflows/daily-file-diet.lock.yml | 4 +- .../workflows/daily-firewall-report.lock.yml | 4 +- .../daily-mcp-concurrency-analysis.lock.yml | 4 +- .../daily-observability-report.lock.yml | 6 +- .../daily-safe-output-optimizer.lock.yml | 4 +- .../daily-testify-uber-super-expert.lock.yml | 4 +- .github/workflows/deep-report.lock.yml | 6 +- .github/workflows/dev-hawk.lock.yml | 4 +- .../developer-docs-consolidator.lock.yml | 4 +- .../duplicate-code-detector.lock.yml | 8 +- .../example-workflow-analyzer.lock.yml | 4 +- .../workflows/glossary-maintainer.lock.yml | 4 +- .github/workflows/go-fan.lock.yml | 4 +- .github/workflows/jsweep.lock.yml | 4 +- .github/workflows/mcp-inspector.lock.yml | 26 +++---- .github/workflows/metrics-collector.lock.yml | 4 +- .../workflows/notion-issue-summary.lock.yml | 2 +- .github/workflows/portfolio-analyst.lock.yml | 4 +- .../prompt-clustering-analysis.lock.yml | 4 +- .github/workflows/python-data-charts.lock.yml | 4 +- .github/workflows/q.lock.yml | 8 +- .../repository-quality-improver.lock.yml | 4 +- .github/workflows/safe-output-health.lock.yml | 4 +- .github/workflows/security-review.lock.yml | 4 +- .../semantic-function-refactor.lock.yml | 4 +- .github/workflows/sergo.lock.yml | 4 +- .github/workflows/smoke-claude.lock.yml | 8 +- .github/workflows/smoke-codex.lock.yml | 8 +- .github/workflows/smoke-copilot.lock.yml | 8 +- .../workflows/static-analysis-report.lock.yml | 4 +- .github/workflows/terminal-stylist.lock.yml | 4 +- .github/workflows/typist.lock.yml | 4 +- .../workflows/workflow-normalizer.lock.yml | 4 +- pkg/constants/constants.go | 6 +- pkg/workflow/mcp_config_builtin.go | 6 +- pkg/workflow/mcp_config_custom.go | 34 ++++++-- pkg/workflow/mcp_config_serena_renderer.go | 8 +- pkg/workflow/mcp_renderer.go | 6 +- pkg/workflow/secret_extraction.go | 77 +++++++++++++++++++ 47 files changed, 227 insertions(+), 120 deletions(-) diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index 4f67f36b094..abf0226591c 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -619,8 +619,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index ecb7b45acea..c19c923cd4f 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -512,8 +512,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml index 143b54e0bf1..0896d759136 100644 --- a/.github/workflows/archie.lock.yml +++ b/.github/workflows/archie.lock.yml @@ -489,8 +489,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index d140a98df8c..e45284580e2 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -578,8 +578,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml index 95baa3db637..b642e0c2b8b 100644 --- a/.github/workflows/brave.lock.yml +++ b/.github/workflows/brave.lock.yml @@ -461,7 +461,7 @@ jobs: "*" ], "env": { - "BRAVE_API_KEY": "${{ secrets.BRAVE_API_KEY }}" + "BRAVE_API_KEY": "\${BRAVE_API_KEY}" } }, "github": { diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index a28b05ffb4b..f3f9a5c1a97 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -628,8 +628,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" @@ -680,9 +680,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml index 653a4cef085..faa8888d1b9 100644 --- a/.github/workflows/daily-cli-tools-tester.lock.yml +++ b/.github/workflows/daily-cli-tools-tester.lock.yml @@ -519,8 +519,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml index 4dae82474de..5f5ea851749 100644 --- a/.github/workflows/daily-compiler-quality.lock.yml +++ b/.github/workflows/daily-compiler-quality.lock.yml @@ -472,8 +472,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index d4ba326885a..c3c95ab2d9e 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -486,8 +486,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 51d7b5b3d3a..c2839ef2e92 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -564,8 +564,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml index 5f1cff23cc5..1adb7c03ca7 100644 --- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml +++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml @@ -521,8 +521,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index 936551a1751..216e6e54380 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -568,7 +568,7 @@ jobs: [mcp_servers.agenticworkflows] container = "localhost/gh-aw:dev" - mounts = ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] + mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] env_vars = ["DEBUG", "GH_TOKEN", "GITHUB_TOKEN"] [mcp_servers.github] @@ -593,8 +593,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index 30c410df05d..c89e83aca27 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -545,8 +545,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml index 1e23daf9dcc..225b11f6045 100644 --- a/.github/workflows/daily-testify-uber-super-expert.lock.yml +++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml @@ -496,8 +496,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index fb9e3fd3292..40fed6f2ef2 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -640,7 +640,7 @@ jobs: [mcp_servers.agenticworkflows] container = "localhost/gh-aw:dev" - mounts = ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] + mounts = ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"] env_vars = ["DEBUG", "GH_TOKEN", "GITHUB_TOKEN"] [mcp_servers.github] @@ -665,8 +665,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml index 121d40c105f..d0aaca76f5e 100644 --- a/.github/workflows/dev-hawk.lock.yml +++ b/.github/workflows/dev-hawk.lock.yml @@ -490,8 +490,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index c3f0c8224af..33fa3e2c2bc 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -546,9 +546,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 386805ab82e..a8eb756e4ae 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -497,9 +497,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "${GITHUB_WORKSPACE}" ] - mounts = ["${{ github.workspace }}:${{ github.workspace }}:rw"] + mounts = ["${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw"] GH_AW_MCP_CONFIG_EOF # Generate JSON config for MCP gateway @@ -534,9 +534,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 7e8ac8f95a1..ae28183ad4f 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -503,8 +503,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index ae6aece4c26..205f964ae06 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -494,8 +494,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index a86cdd8ce78..6cfb1a85cf9 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -483,9 +483,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index c9dfc533c09..f99063c7717 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -491,8 +491,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 4cbe8ea1407..387d239a38c 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -580,8 +580,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" @@ -610,7 +610,7 @@ jobs: "*" ], "env": { - "BRAVE_API_KEY": "${{ secrets.BRAVE_API_KEY }}" + "BRAVE_API_KEY": "\${BRAVE_API_KEY}" } }, "context7": { @@ -621,7 +621,7 @@ jobs: "resolve-library-id" ], "env": { - "CONTEXT7_API_KEY": "${{ secrets.CONTEXT7_API_KEY }}" + "CONTEXT7_API_KEY": "\${CONTEXT7_API_KEY}" } }, "datadog": { @@ -677,9 +677,9 @@ jobs: "get_eventstream_definition" ], "env": { - "AZURE_CLIENT_ID": "${{ secrets.AZURE_CLIENT_ID }}", - "AZURE_CLIENT_SECRET": "${{ secrets.AZURE_CLIENT_SECRET }}", - "AZURE_TENANT_ID": "${{ secrets.AZURE_TENANT_ID }}" + "AZURE_CLIENT_ID": "\${AZURE_CLIENT_ID}", + "AZURE_CLIENT_SECRET": "\${AZURE_CLIENT_SECRET}", + "AZURE_TENANT_ID": "\${AZURE_TENANT_ID}" } }, "github": { @@ -730,7 +730,7 @@ jobs: "query_database" ], "env": { - "NOTION_API_TOKEN": "${{ secrets.NOTION_API_TOKEN }}" + "NOTION_API_TOKEN": "\${NOTION_API_TOKEN}" } }, "safeoutputs": { @@ -765,9 +765,9 @@ jobs: "get_doc" ], "env": { - "OPENAI_API_KEY": "${{ secrets.SENTRY_OPENAI_API_KEY }}", - "SENTRY_ACCESS_TOKEN": "${{ secrets.SENTRY_ACCESS_TOKEN }}", - "SENTRY_HOST": "${{ env.SENTRY_HOST }}" + "OPENAI_API_KEY": "\${SENTRY_OPENAI_API_KEY}", + "SENTRY_ACCESS_TOKEN": "\${SENTRY_ACCESS_TOKEN}", + "SENTRY_HOST": "\${SENTRY_HOST}" } }, "serena": { @@ -775,8 +775,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] }, "tavily": { "type": "http", diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml index 6754d8aaf1b..bd34798a07b 100644 --- a/.github/workflows/metrics-collector.lock.yml +++ b/.github/workflows/metrics-collector.lock.yml @@ -288,8 +288,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 5c51b0c9415..778ddcebcb0 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -428,7 +428,7 @@ jobs: "query_database" ], "env": { - "NOTION_API_TOKEN": "${{ secrets.NOTION_API_TOKEN }}" + "NOTION_API_TOKEN": "\${NOTION_API_TOKEN}" } }, "safeoutputs": { diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 4c4caf51b91..abb2ad1d71f 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -571,8 +571,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 066adc45f7c..9bb83bcbe78 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -568,8 +568,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index c39b8f3a8cb..597a52d6fd6 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -560,8 +560,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 3840dd879bb..5e7c5cd612f 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -613,8 +613,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" @@ -642,8 +642,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index f029d8f67b8..39e1ee3f760 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -473,8 +473,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 73d425834f0..20375d34f2f 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -521,8 +521,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index 3ff19b1b420..f19865ccd85 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -603,8 +603,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 6a7ac40fa32..d29c0c08e7e 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -532,9 +532,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index 6be1655e0c3..fafd1fd91e8 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -484,9 +484,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index a3487b4019f..39bd1d1159b 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -1201,8 +1201,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" @@ -1260,9 +1260,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] }, "tavily": { "type": "http", diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index de078844ea6..1a96f39f949 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -788,9 +788,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "${GITHUB_WORKSPACE}" ] - mounts = ["${{ github.workspace }}:${{ github.workspace }}:rw"] + mounts = ["${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw"] GH_AW_MCP_CONFIG_EOF # Generate JSON config for MCP gateway @@ -849,9 +849,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index f51c3993636..486a3388139 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1190,8 +1190,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" @@ -1233,8 +1233,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index fa2612d138a..7eacd0280eb 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -520,8 +520,8 @@ jobs: "mcpServers": { "agenticworkflows": { "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "$GITHUB_TOKEN" diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml index 5457e9f96fb..1b873caf2e2 100644 --- a/.github/workflows/terminal-stylist.lock.yml +++ b/.github/workflows/terminal-stylist.lock.yml @@ -459,8 +459,8 @@ jobs: "container": "ghcr.io/github/serena-mcp-server:latest", "args": ["--network", "host"], "entrypoint": "serena", - "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 6dedc89d51e..18139e32815 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -472,9 +472,9 @@ jobs: "--context", "codex", "--project", - "${{ github.workspace }}" + "\${GITHUB_WORKSPACE}" ], - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"] + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"] } }, "gateway": { diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml index 31a5e1b9367..8c3d8942632 100644 --- a/.github/workflows/workflow-normalizer.lock.yml +++ b/.github/workflows/workflow-normalizer.lock.yml @@ -520,8 +520,8 @@ jobs: "agenticworkflows": { "type": "stdio", "container": "localhost/gh-aw:dev", - "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], - "args": ["--network", "host", "-w", "${{ github.workspace }}"], + "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], + "args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"], "env": { "DEBUG": "*", "GITHUB_TOKEN": "\${GITHUB_TOKEN}" diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 78dbe69ef7d..850998f45ab 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -429,9 +429,9 @@ const DefaultGhBinaryMount = "/usr/bin/gh:/usr/bin/gh:ro" const DefaultTmpGhAwMount = "/tmp/gh-aw:/tmp/gh-aw:rw" // DefaultWorkspaceMount is the mount path for the GitHub workspace directory in containerized MCP servers -// Uses GitHub Actions expression syntax which expands to the actual workspace path at runtime -// This is required when MCP servers need read-write access to repository files -const DefaultWorkspaceMount = "${{ github.workspace }}:${{ github.workspace }}:rw" +// Security: Uses GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection +// The GITHUB_WORKSPACE environment variable is automatically set by GitHub Actions and passed to the MCP gateway +const DefaultWorkspaceMount = "\\${GITHUB_WORKSPACE}:\\${GITHUB_WORKSPACE}:rw" // DefaultPythonVersion is the default version of Python for runtime setup const DefaultPythonVersion Version = "3.12" diff --git a/pkg/workflow/mcp_config_builtin.go b/pkg/workflow/mcp_config_builtin.go index 5bbb262f47c..26f4669bfd8 100644 --- a/pkg/workflow/mcp_config_builtin.go +++ b/pkg/workflow/mcp_config_builtin.go @@ -232,7 +232,8 @@ func renderAgenticWorkflowsMCPConfigWithOptions(yaml *strings.Builder, isLast bo // Add Docker runtime args: // - --network host: Enables network access for GitHub API calls (gh CLI needs api.github.com) // - -w: Sets working directory to workspace for .github/workflows folder resolution - yaml.WriteString(" \"args\": [\"--network\", \"host\", \"-w\", \"${{ github.workspace }}\"],\n") + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection + yaml.WriteString(" \"args\": [\"--network\", \"host\", \"-w\", \"\\${GITHUB_WORKSPACE}\"],\n") // Note: tools field is NOT included here - the converter script adds it back // for Copilot. This keeps the gateway config compatible with the schema. @@ -341,7 +342,8 @@ func renderAgenticWorkflowsMCPConfigTOML(yaml *strings.Builder, actionMode Actio // Add Docker runtime args: // - --network host: Enables network access for GitHub API calls (gh CLI needs api.github.com) // - -w: Sets working directory to workspace for .github/workflows folder resolution - yaml.WriteString(" args = [\"--network\", \"host\", \"-w\", \"${{ github.workspace }}\"]\n") + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection + yaml.WriteString(" args = [\"--network\", \"host\", \"-w\", \"${GITHUB_WORKSPACE}\"]\n") // Use env_vars array to reference environment variables instead of embedding secrets yaml.WriteString(" env_vars = [\"DEBUG\", \"GITHUB_TOKEN\"]\n") diff --git a/pkg/workflow/mcp_config_custom.go b/pkg/workflow/mcp_config_custom.go index 4658cfcf096..8f17eaf2c67 100644 --- a/pkg/workflow/mcp_config_custom.go +++ b/pkg/workflow/mcp_config_custom.go @@ -263,7 +263,12 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma if argIndex == len(mcpConfig.EntrypointArgs)-1 { argComma = "" } - fmt.Fprintf(yaml, "%s \"%s\"%s\n", renderer.IndentLevel, arg, argComma) + // Replace template expressions with environment variable references + argValue := arg + if renderer.RequiresCopilotFields { + argValue = ReplaceTemplateExpressionsWithEnvVars(argValue) + } + fmt.Fprintf(yaml, "%s \"%s\"%s\n", renderer.IndentLevel, argValue, argComma) } fmt.Fprintf(yaml, "%s]%s\n", renderer.IndentLevel, comma) case "mounts": @@ -279,7 +284,12 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma if mountIndex == len(mcpConfig.Mounts)-1 { mountComma = "" } - fmt.Fprintf(yaml, "%s \"%s\"%s\n", renderer.IndentLevel, mount, mountComma) + // Replace template expressions with environment variable references + mountValue := mount + if renderer.RequiresCopilotFields { + mountValue = ReplaceTemplateExpressionsWithEnvVars(mountValue) + } + fmt.Fprintf(yaml, "%s \"%s\"%s\n", renderer.IndentLevel, mountValue, mountComma) } fmt.Fprintf(yaml, "%s]%s\n", renderer.IndentLevel, comma) case "command": @@ -324,7 +334,14 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma if i > 0 { yaml.WriteString(", ") } - fmt.Fprintf(yaml, "\"%s\" = \"%s\"", envKey, mcpConfig.Env[envKey]) + // Replace template expressions with environment variable references for TOML + envValue := mcpConfig.Env[envKey] + // For TOML, we use direct shell variable syntax without backslash + envValue = strings.ReplaceAll(envValue, "${{ secrets.", "${") + envValue = strings.ReplaceAll(envValue, "${{ env.", "${") + envValue = strings.ReplaceAll(envValue, "${{ github.workspace }}", "${GITHUB_WORKSPACE}") + envValue = strings.ReplaceAll(envValue, " }}", "}") + fmt.Fprintf(yaml, "\"%s\" = \"%s\"", envKey, envValue) } yaml.WriteString(" }\n") } else { @@ -363,8 +380,15 @@ func renderSharedMCPConfig(yaml *strings.Builder, toolName string, toolConfig ma // Use passthrough syntax: "VAR_NAME": "\\${VAR_NAME}" fmt.Fprintf(yaml, "%s \"%s\": \"\\${%s}\"%s\n", renderer.IndentLevel, envKey, envKey, envComma) } else { - // Use existing env value - fmt.Fprintf(yaml, "%s \"%s\": \"%s\"%s\n", renderer.IndentLevel, envKey, mcpConfig.Env[envKey], envComma) + // Replace template expressions with environment variable references + // This prevents template injection by using shell variable substitution + // instead of GitHub Actions template expansion + envValue := mcpConfig.Env[envKey] + if renderer.RequiresCopilotFields { + // For Copilot, replace all template expressions with \${VAR} syntax + envValue = ReplaceTemplateExpressionsWithEnvVars(envValue) + } + fmt.Fprintf(yaml, "%s \"%s\": \"%s\"%s\n", renderer.IndentLevel, envKey, envValue, envComma) } } fmt.Fprintf(yaml, "%s}%s\n", renderer.IndentLevel, comma) diff --git a/pkg/workflow/mcp_config_serena_renderer.go b/pkg/workflow/mcp_config_serena_renderer.go index 69507781f2d..bfa8521a1d4 100644 --- a/pkg/workflow/mcp_config_serena_renderer.go +++ b/pkg/workflow/mcp_config_serena_renderer.go @@ -145,8 +145,9 @@ func renderSerenaMCPConfigWithOptions(yaml *strings.Builder, serenaTool any, isL yaml.WriteString(" \"entrypoint\": \"serena\",\n") // Entrypoint args for Serena MCP server + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection if inlineArgs { - yaml.WriteString(" \"entrypointArgs\": [\"start-mcp-server\", \"--context\", \"codex\", \"--project\", \"${{ github.workspace }}\"") + yaml.WriteString(" \"entrypointArgs\": [\"start-mcp-server\", \"--context\", \"codex\", \"--project\", \"\\${GITHUB_WORKSPACE}\"") // Append custom args if present writeArgsToYAMLInline(yaml, customArgs) yaml.WriteString("],\n") @@ -156,7 +157,7 @@ func renderSerenaMCPConfigWithOptions(yaml *strings.Builder, serenaTool any, isL yaml.WriteString(" \"--context\",\n") yaml.WriteString(" \"codex\",\n") yaml.WriteString(" \"--project\",\n") - yaml.WriteString(" \"${{ github.workspace }}\"") + yaml.WriteString(" \"\\${GITHUB_WORKSPACE}\"") // Append custom args if present writeArgsToYAML(yaml, customArgs, " ") yaml.WriteString("\n") @@ -164,7 +165,8 @@ func renderSerenaMCPConfigWithOptions(yaml *strings.Builder, serenaTool any, isL } // Add volume mount for workspace access - yaml.WriteString(" \"mounts\": [\"${{ github.workspace }}:${{ github.workspace }}:rw\"]\n") + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection + yaml.WriteString(" \"mounts\": [\"\\${GITHUB_WORKSPACE}:\\${GITHUB_WORKSPACE}:rw\"]\n") // Note: tools field is NOT included here - the converter script adds it back // for Copilot. This keeps the gateway config compatible with the schema. diff --git a/pkg/workflow/mcp_renderer.go b/pkg/workflow/mcp_renderer.go index 10d21a27556..6418939e4ab 100644 --- a/pkg/workflow/mcp_renderer.go +++ b/pkg/workflow/mcp_renderer.go @@ -311,7 +311,8 @@ func (r *MCPConfigRendererUnified) renderSerenaTOML(yaml *strings.Builder, seren yaml.WriteString(" \"--context\",\n") yaml.WriteString(" \"codex\",\n") yaml.WriteString(" \"--project\",\n") - yaml.WriteString(" \"${{ github.workspace }}\"") + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection + yaml.WriteString(" \"${GITHUB_WORKSPACE}\"") // Append custom args if present for _, arg := range customArgs { @@ -323,7 +324,8 @@ func (r *MCPConfigRendererUnified) renderSerenaTOML(yaml *strings.Builder, seren yaml.WriteString(" ]\n") // Add volume mount for workspace access - yaml.WriteString(" mounts = [\"${{ github.workspace }}:${{ github.workspace }}:rw\"]\n") + // Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection + yaml.WriteString(" mounts = [\"${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw\"]\n") } } diff --git a/pkg/workflow/secret_extraction.go b/pkg/workflow/secret_extraction.go index 3fef1b09aac..f6d26189358 100644 --- a/pkg/workflow/secret_extraction.go +++ b/pkg/workflow/secret_extraction.go @@ -120,3 +120,80 @@ func ReplaceSecretsWithEnvVars(value string, secrets map[string]string) string { } return result } + +// ExtractEnvExpressionsFromValue extracts all GitHub Actions env expressions from a string value +// Returns a map of environment variable names to their full env expressions +// Examples: +// - "${{ env.SENTRY_HOST }}" -> {"SENTRY_HOST": "${{ env.SENTRY_HOST }}"} +// - "${{ env.DD_SITE || 'default' }}" -> {"DD_SITE": "${{ env.DD_SITE || 'default' }}"} +func ExtractEnvExpressionsFromValue(value string) map[string]string { + envExpressions := make(map[string]string) + + start := 0 + for { + // Find the start of an expression + startIdx := strings.Index(value[start:], "${{ env.") + if startIdx == -1 { + break + } + startIdx += start + + // Find the end of the expression + endIdx := strings.Index(value[startIdx:], "}}") + if endIdx == -1 { + break + } + endIdx += startIdx + 2 // Include the closing }} + + // Extract the full expression + fullExpr := value[startIdx:endIdx] + + // Extract the variable name from "env.VARIABLE_NAME" or "env.VARIABLE_NAME ||" + envPart := strings.TrimPrefix(fullExpr, "${{ env.") + envPart = strings.TrimSuffix(envPart, "}}") + envPart = strings.TrimSpace(envPart) + + // Find the variable name (everything before space, ||, or end) + varName := envPart + if spaceIdx := strings.IndexAny(varName, " |"); spaceIdx != -1 { + varName = varName[:spaceIdx] + } + + // Store the variable name and full expression + if varName != "" { + envExpressions[varName] = fullExpr + secretLog.Printf("Extracted env expression: %s", varName) + } + + start = endIdx + } + + return envExpressions +} + +// ReplaceTemplateExpressionsWithEnvVars replaces all template expressions with environment variable references +// Handles: secrets.*, env.*, and github.workspace +// Examples: +// - "${{ secrets.DD_API_KEY }}" -> "\${DD_API_KEY}" +// - "${{ env.SENTRY_HOST }}" -> "\${SENTRY_HOST}" +// - "${{ github.workspace }}" -> "\${GITHUB_WORKSPACE}" +func ReplaceTemplateExpressionsWithEnvVars(value string) string { + result := value + + // Extract and replace secrets + secrets := ExtractSecretsFromValue(value) + for varName, secretExpr := range secrets { + result = strings.ReplaceAll(result, secretExpr, "\\${"+varName+"}") + } + + // Extract and replace env vars + envVars := ExtractEnvExpressionsFromValue(value) + for varName, envExpr := range envVars { + result = strings.ReplaceAll(result, envExpr, "\\${"+varName+"}") + } + + // Replace github.workspace with GITHUB_WORKSPACE env var + result = strings.ReplaceAll(result, "${{ github.workspace }}", "\\${GITHUB_WORKSPACE}") + + return result +} From 7aa6be3b195c8da4279344807b036ae44b03bb3e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 02:15:48 +0000 Subject: [PATCH 3/4] Update tests to expect env var references instead of template expressions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/importable_tools_test.go | 2 +- pkg/workflow/mcp_config_compilation_test.go | 2 +- pkg/workflow/mcp_config_comprehensive_test.go | 2 +- pkg/workflow/mcp_config_refactor_test.go | 46 +++++++++---------- pkg/workflow/mcp_renderer_test.go | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pkg/workflow/importable_tools_test.go b/pkg/workflow/importable_tools_test.go index 879083f91cd..1a24610ddbd 100644 --- a/pkg/workflow/importable_tools_test.go +++ b/pkg/workflow/importable_tools_test.go @@ -264,7 +264,7 @@ Uses imported agentic-workflows tool. } // Verify working directory args are present - if !strings.Contains(workflowData, `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`) { + if !strings.Contains(workflowData, `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`) { t.Error("Expected args with network access and working directory") } } diff --git a/pkg/workflow/mcp_config_compilation_test.go b/pkg/workflow/mcp_config_compilation_test.go index 5c53a636d75..d18a26cde11 100644 --- a/pkg/workflow/mcp_config_compilation_test.go +++ b/pkg/workflow/mcp_config_compilation_test.go @@ -325,7 +325,7 @@ This workflow tests that agentic-workflows uses the correct container in dev mod } // Verify working directory args are present - if !strings.Contains(string(lockContent), `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`) { + if !strings.Contains(string(lockContent), `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`) { t.Error("Expected args with network access and working directory in dev mode") } } diff --git a/pkg/workflow/mcp_config_comprehensive_test.go b/pkg/workflow/mcp_config_comprehensive_test.go index ebdf8414918..ef7b41f8933 100644 --- a/pkg/workflow/mcp_config_comprehensive_test.go +++ b/pkg/workflow/mcp_config_comprehensive_test.go @@ -627,7 +627,7 @@ func TestRenderSerenaMCPConfigWithOptions(t *testing.T) { `"serena": {`, `"type": "stdio"`, `"container": "ghcr.io/github/serena-mcp-server:latest"`, - `"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "${{ github.workspace }}"]`, + `"entrypointArgs": ["start-mcp-server", "--context", "codex", "--project", "\${GITHUB_WORKSPACE}"]`, }, unexpectedContent: []string{}, }, diff --git a/pkg/workflow/mcp_config_refactor_test.go b/pkg/workflow/mcp_config_refactor_test.go index 82d95a9c3e7..5dba5403a05 100644 --- a/pkg/workflow/mcp_config_refactor_test.go +++ b/pkg/workflow/mcp_config_refactor_test.go @@ -105,11 +105,11 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) { expectedContent: []string{ `"agenticworkflows": {`, `"type": "stdio"`, - `"container": "localhost/gh-aw:dev"`, // Dev mode uses locally built image - `"${{ github.workspace }}:${{ github.workspace }}:rw"`, // workspace mount (read-write) - `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) - `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`, // Network access + working directory - `"DEBUG": "*"`, // Literal value for debug logging + `"container": "localhost/gh-aw:dev"`, // Dev mode uses locally built image + `"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount (read-write) + `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) + `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`, // Network access + working directory + `"DEBUG": "*"`, // Literal value for debug logging `"GITHUB_TOKEN": "\${GITHUB_TOKEN}"`, ` },`, }, @@ -134,11 +134,11 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) { `"container": "alpine:latest"`, `"entrypoint": "/opt/gh-aw/gh-aw"`, `"entrypointArgs": ["mcp-server"]`, - `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount (read-only) - `"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount (read-only) - `"${{ github.workspace }}:${{ github.workspace }}:rw"`, // workspace mount (read-write) - `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) - `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`, // Network access + working directory + `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount (read-only) + `"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount (read-only) + `"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount (read-write) + `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) + `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`, // Network access + working directory `"DEBUG": "*"`, `"GITHUB_TOKEN": "\${GITHUB_TOKEN}"`, ` },`, @@ -156,10 +156,10 @@ func TestRenderAgenticWorkflowsMCPConfigWithOptions(t *testing.T) { actionMode: ActionModeDev, expectedContent: []string{ `"agenticworkflows": {`, - `"container": "localhost/gh-aw:dev"`, // Dev mode uses locally built image - `"${{ github.workspace }}:${{ github.workspace }}:rw"`, // workspace mount (read-write) - `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) - `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`, // Network access + working directory + `"container": "localhost/gh-aw:dev"`, // Dev mode uses locally built image + `"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount (read-write) + `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount (read-write) + `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`, // Network access + working directory // Environment variables `"DEBUG": "*"`, // Literal value for debug logging `"GITHUB_TOKEN": "$GITHUB_TOKEN"`, @@ -261,8 +261,8 @@ func TestRenderAgenticWorkflowsMCPConfigTOML(t *testing.T) { expectedContainer: `container = "localhost/gh-aw:dev"`, shouldHaveEntrypoint: false, // Dev mode uses container's default ENTRYPOINT expectedMounts: []string{ - `"${{ github.workspace }}:${{ github.workspace }}:rw"`, // workspace mount - `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount + `"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount + `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount }, unexpectedContent: []string{ `--cmd`, @@ -278,12 +278,12 @@ func TestRenderAgenticWorkflowsMCPConfigTOML(t *testing.T) { expectedContainer: `container = "alpine:latest"`, shouldHaveEntrypoint: true, expectedMounts: []string{ - `entrypoint = "/opt/gh-aw/gh-aw"`, // Entrypoint needed in release mode - `entrypointArgs = ["mcp-server"]`, // EntrypointArgs needed in release mode - `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount - `"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount - `"${{ github.workspace }}:${{ github.workspace }}:rw"`, // workspace mount - `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount + `entrypoint = "/opt/gh-aw/gh-aw"`, // Entrypoint needed in release mode + `entrypointArgs = ["mcp-server"]`, // EntrypointArgs needed in release mode + `"/opt/gh-aw:/opt/gh-aw:ro"`, // gh-aw binary mount + `"/usr/bin/gh:/usr/bin/gh:ro"`, // gh CLI binary mount + `"\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"`, // workspace mount + `"/tmp/gh-aw:/tmp/gh-aw:rw"`, // temp directory mount }, unexpectedContent: []string{ `--cmd`, @@ -302,7 +302,7 @@ func TestRenderAgenticWorkflowsMCPConfigTOML(t *testing.T) { expectedContent := []string{ `[mcp_servers.agenticworkflows]`, tt.expectedContainer, - `args = ["--network", "host", "-w", "${{ github.workspace }}"]`, // Network access + working directory + `args = ["--network", "host", "-w", "${GITHUB_WORKSPACE}"]`, // Network access + working directory `env_vars = ["DEBUG", "GITHUB_TOKEN"]`, } expectedContent = append(expectedContent, tt.expectedMounts...) diff --git a/pkg/workflow/mcp_renderer_test.go b/pkg/workflow/mcp_renderer_test.go index 6f3793a89da..a117dd2617d 100644 --- a/pkg/workflow/mcp_renderer_test.go +++ b/pkg/workflow/mcp_renderer_test.go @@ -214,7 +214,7 @@ func TestRenderAgenticWorkflowsMCP_JSON_Copilot(t *testing.T) { t.Error("Expected GITHUB_TOKEN in env vars") } // Should have network access and working directory args - if !strings.Contains(output, `"args": ["--network", "host", "-w", "${{ github.workspace }}"]`) { + if !strings.Contains(output, `"args": ["--network", "host", "-w", "\${GITHUB_WORKSPACE}"]`) { t.Error("Expected args with network access and working directory set to workspace") } } From 9d9aa673e9267fc9907577feb91e0c36c4365adf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 12 Feb 2026 02:29:31 +0000 Subject: [PATCH 4/4] Add changeset [skip-ci] --- .changeset/patch-escape-mcp-template-expressions.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/patch-escape-mcp-template-expressions.md diff --git a/.changeset/patch-escape-mcp-template-expressions.md b/.changeset/patch-escape-mcp-template-expressions.md new file mode 100644 index 00000000000..43b853dfb5e --- /dev/null +++ b/.changeset/patch-escape-mcp-template-expressions.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Escape MCP gateway template expressions in the generated heredocs so GitHub expressions are deferred to the MCP server instead of being expanded by the shell, preventing template-injection risks.