Skip to content

Fix template-injection in MCP config heredocs#15066

Merged
pelikhan merged 4 commits intomainfrom
copilot/fix-template-injection-risk
Feb 12, 2026
Merged

Fix template-injection in MCP config heredocs#15066
pelikhan merged 4 commits intomainfrom
copilot/fix-template-injection-risk

Conversation

Copy link
Contributor

Copilot AI commented Feb 12, 2026

MCP server configurations embedded GitHub Actions template expressions directly in shell heredocs, allowing potential code injection if user-controlled values flow into these expressions.

Changes

Template Expression Replacement

  • Replaced ${{ github.workspace }}\${GITHUB_WORKSPACE} in JSON configs
  • Replaced ${{ secrets.* }}\${VAR_NAME} for all secret references
  • Replaced ${{ env.* }}\${VAR_NAME} for all env var references
  • Backslash escaping prevents shell expansion in heredoc, deferring to MCP gateway

Implementation

  • Added ReplaceTemplateExpressionsWithEnvVars() in secret_extraction.go
  • Updated env value, mounts, and args rendering in MCP config builders
  • Changed DefaultWorkspaceMount constant from template to env var reference
  • Applied fix to both JSON (Copilot/Claude) and TOML (Codex) formats

Before:

cat << EOF | start_mcp_gateway.sh
{
  "env": {
    "BRAVE_API_KEY": "${{ secrets.BRAVE_API_KEY }}"
  },
  "mounts": ["${{ github.workspace }}:${{ github.workspace }}:rw"]
}
EOF

After:

env:
  BRAVE_API_KEY: ${{ secrets.BRAVE_API_KEY }}
  GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
  cat << EOF | start_mcp_gateway.sh
  {
    "env": {
      "BRAVE_API_KEY": "\${BRAVE_API_KEY}"
    },
    "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw"]
  }
  EOF

Scope

Affects 40+ workflows using MCP servers. All 148 workflows recompiled successfully.

Original prompt

This section details on the original issue you should resolve

<issue_title>[plan] Review and fix template-injection risk in mcp-inspector workflow</issue_title>
<issue_description>## Objective

Address the low severity template-injection finding in mcp-inspector.lock.yml to prevent potential code injection through template expansion.

Context

From discussion #15009 - Static analysis identified template injection risk at line 538. This has been recurring since Feb 5.

Zizmor Finding:

  • Severity: Low
  • Location: Line 538
  • Description: Potential code injection through template expansion
  • Impact: Low risk if inputs are controlled
  • Reference: docs.zizmor.sh/rules/template-injection

Approach

  1. Review mcp-inspector.lock.yml (line 538) to identify the template expansion
  2. Check the source .md workflow file for the corresponding template usage
  3. Verify that template inputs are properly sanitized:
    • Ensure no user-controlled input flows into template expansion
    • Use GitHub expression syntax safely (e.g., avoid ${{ github.event.issue.title }} in shell commands)
  4. If user input is used, add proper sanitization or use safer alternatives:
    • Use GitHub Actions inputs with validation
    • Use environment variables instead of direct expansion
    • Apply proper escaping/quoting
  5. Update the workflow to eliminate the security finding
  6. Recompile with make recompile
  7. Verify the fix with static analysis tools

Files to Modify

  • .github/workflows/mcp-inspector.md (source workflow)
  • .github/workflows/mcp-inspector.lock.yml (will be regenerated)

Acceptance Criteria

  • template-injection finding eliminated from zizmor scan
  • Template inputs are properly sanitized or validated
  • No user-controlled input flows unsafely into template expansion
  • Workflow recompiled successfully
  • Workflow still functions as intended

AI generated by Plan Command for discussion #15009

  • expires on Feb 14, 2026, 1:28 AM UTC

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.


Changeset

  • Type: patch
  • Description: Escape template expressions in MCP gateway heredocs so GitHub expressions stay literal until the MCP server processes them, preventing shell template injection.

Generated by Changeset Generator

Copilot AI and others added 2 commits February 12, 2026 02:12
…s in MCP config

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ions

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Review and fix template-injection risk in mcp-inspector Fix template-injection in MCP config heredocs Feb 12, 2026
Copilot AI requested a review from pelikhan February 12, 2026 02:19
@pelikhan pelikhan added the smoke label Feb 12, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

@github-actions
Copy link
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions
Copy link
Contributor

🧪 Smoke Project is now testing project operations...

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

🎬 THE ENDSmoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨

@github-actions
Copy link
Contributor

github-actions bot commented Feb 12, 2026

Changeset Generator completed successfully!

@github-actions
Copy link
Contributor

Agent Container Tool Check ✅

Tool Status Version
bash 5.2.21
sh available
git 2.52.0
jq 1.7
yq 4.52.2
curl 8.5.0
gh 2.86.0
node 20.20.0
python3 3.12.3
go 1.24.12
java 21.0.10
dotnet 10.0.102

Result: 12/12 tools available ✅

All required development tools are present and functional in the agent container environment.

AI generated by Agent Container Smoke Test

@github-actions
Copy link
Contributor

Smoke Project completed successfully. All project operations validated.

@pelikhan pelikhan marked this pull request as ready for review February 12, 2026 02:32
Copilot AI review requested due to automatic review settings February 12, 2026 02:32
@github-actions
Copy link
Contributor

Smoke Test Results

PRs Reviewed:

Test Results:

  • ✅ GitHub MCP
  • ✅ Safe Inputs GH CLI
  • ✅ Serena MCP
  • ✅ Playwright
  • ✅ File Writing
  • ✅ Bash Tool
  • ✅ Discussion Interaction
  • ✅ Build gh-aw
  • ✅ Workflow Dispatch

Overall Status: PASS

@pelikhan @Copilot - All tests passed on §21931213564

AI generated by Smoke Copilot

@github-actions
Copy link
Contributor

Smoke test results
GitHub MCP (merged PR titles): ✅ “Update developer-docs-consolidator to write to scratchpad/dev.md”; “Consolidate security-guard into bot-detection workflow”
Serena MCP (activate + find_symbol>=3): ✅
Playwright (title contains "GitHub"): ✅
File write (/tmp/gh-aw/agent/...): ✅
Bash cat verify: ✅
Build (make build): ✅
Overall: PASS

AI generated by Smoke Codex

@github-actions
Copy link
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@pelikhan pelikhan merged commit 745e36e into main Feb 12, 2026
5 checks passed
@pelikhan pelikhan deleted the copilot/fix-template-injection-risk branch February 12, 2026 02:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR mitigates a template-injection risk in generated MCP gateway heredocs by removing direct GitHub Actions template expressions from JSON/TOML config bodies and replacing them with environment-variable references.

Changes:

  • Added helpers to detect ${{ env.* }} expressions and replace ${{ secrets.* }}, ${{ env.* }}, and ${{ github.workspace }} with env-var placeholders for heredoc-safe rendering.
  • Updated MCP config renderers/builders to emit workspace/secret references via ${GITHUB_WORKSPACE} / ${VAR_NAME} patterns (and \${...} where needed).
  • Recompiled affected workflows and updated tests/fixtures accordingly.

Reviewed changes

Copilot reviewed 53 out of 53 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/workflow/secret_extraction.go Adds env-expression extraction and template→env-var replacement helper.
pkg/workflow/mcp_renderer_test.go Updates expectations for workspace arg escaping in generated JSON.
pkg/workflow/mcp_renderer.go Switches Serena TOML rendering from ${{ github.workspace }} to ${GITHUB_WORKSPACE}.
pkg/workflow/mcp_config_serena_renderer.go Escapes workspace in Serena JSON config rendering.
pkg/workflow/mcp_config_refactor_test.go Updates expected mounts/args strings to new workspace placeholder patterns.
pkg/workflow/mcp_config_custom.go Applies template→env-var replacement for args/mounts/env rendering (currently Copilot-gated) and adjusts TOML env rendering.
pkg/workflow/mcp_config_comprehensive_test.go Updates expected Serena entrypoint args to new workspace placeholder pattern.
pkg/workflow/mcp_config_compilation_test.go Updates expected compiled workflow content for workspace arg escaping.
pkg/workflow/mcp_config_builtin.go Updates built-in MCP JSON/TOML renderers to use ${GITHUB_WORKSPACE} patterns.
pkg/workflow/importable_tools_test.go Updates expected workspace arg escaping.
pkg/constants/constants.go Updates default workspace mount constant to use an env-var placeholder form.
.github/workflows/workflow-normalizer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/typist.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/terminal-stylist.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/static-analysis-report.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/smoke-copilot.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/smoke-codex.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/smoke-claude.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/sergo.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/semantic-function-refactor.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/security-review.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/safe-output-health.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/repository-quality-improver.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/q.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/python-data-charts.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/prompt-clustering-analysis.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/portfolio-analyst.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/notion-issue-summary.lock.yml Recompiled: replaces secret template usage in MCP configs with env-var placeholders.
.github/workflows/metrics-collector.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/mcp-inspector.lock.yml Recompiled: replaces workspace/secrets/env templates in MCP configs with env-var placeholders.
.github/workflows/jsweep.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/go-fan.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/glossary-maintainer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/example-workflow-analyzer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/duplicate-code-detector.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/developer-docs-consolidator.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/dev-hawk.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/deep-report.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-testify-uber-super-expert.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-safe-output-optimizer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-observability-report.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-mcp-concurrency-analysis.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-firewall-report.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-file-diet.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-compiler-quality.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/daily-cli-tools-tester.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/cloclo.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/brave.lock.yml Recompiled: replaces secret template usage in MCP configs with env-var placeholders.
.github/workflows/audit-workflows.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/archie.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/agent-persona-explorer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.github/workflows/agent-performance-analyzer.lock.yml Recompiled: replaces workspace template usage in MCP configs with env-var placeholders.
.changeset/patch-escape-mcp-template-expressions.md Adds a patch changeset describing the security fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +266 to +271
// 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)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Template-expression escaping is currently gated on renderer.RequiresCopilotFields. The template-injection issue is about ${{ ... }} being embedded in heredoc content, which can also happen for non-Copilot JSON renderers (e.g., Claude/custom) since they also write JSON via cat << .... Consider applying ReplaceTemplateExpressionsWithEnvVars (or an equivalent) based on whether the output is going into a heredoc (or on renderer.Format == "json"), not on whether Copilot-specific fields are required.

Copilot uses AI. Check for mistakes.
Comment on lines 431 to +434
// 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"
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultWorkspaceMount now embeds backslash-escaped shell syntax (\\${GITHUB_WORKSPACE}), which is a rendering-context concern (heredoc escaping) rather than an invariant mount value. Since this constant is reused by both JSON and TOML renderers, consider keeping the constant as the unescaped mount expression and applying the appropriate escaping at render time (or splitting into format-specific constants) to avoid accidental behavior changes across formats.

Copilot uses AI. Check for mistakes.
Comment on lines +314 to 316
// Security: Use GITHUB_WORKSPACE environment variable instead of template expansion to prevent template injection
yaml.WriteString(" \"${GITHUB_WORKSPACE}\"")

Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says backslash escaping is used to prevent shell expansion inside heredocs (deferring substitution to the MCP gateway), but this TOML renderer writes ${GITHUB_WORKSPACE} without escaping. In the unquoted heredocs used by the compiled workflows, that will be expanded by the shell before the config is consumed. Either escape it consistently (e.g., \${GITHUB_WORKSPACE}) / quote the heredoc delimiter, or adjust the PR description to reflect that TOML uses shell-time expansion.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[plan] Review and fix template-injection risk in mcp-inspector workflow

2 participants