Skip to content

Fix MCP gateway mount policy rejection for safeoutputs backend server - #51870

Merged
pelikhan merged 4 commits into
mainfrom
copilot/fix-gateway-configuration
Aug 11, 2026
Merged

Fix MCP gateway mount policy rejection for safeoutputs backend server#51870
pelikhan merged 4 commits into
mainfrom
copilot/fix-gateway-configuration

Conversation

Copilot AI commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

The agent job in run 31410242707 failed because gh-aw-mcpg v0.4.9 introduced a trusted host-path mount policy that, by default, only permits read-only access to $GITHUB_WORKSPACE and read-write access to the system temp dir. Our compiler mounts ${GITHUB_WORKSPACE} read-write into the built-in safeoutputs MCP server, so the gateway now rejects it at startup:

Mount policy violation: mount "/home/runner/work/gh-aw/gh-aw" rejected: read-write access to this host root is not permitted by mount policy

This breaks safeoutputs tool registration entirely, failing every workflow run that uses it.

Gateway configuration

  • Added buildMCPGatewayAllowedMountRoots() (pkg/workflow/mcp_setup_gateway.go) which computes an explicit allowlist granting read-write access to the paths our built-in servers mount (${GITHUB_WORKSPACE}, ${RUNNER_TEMP}/gh-aw, /tmp) and read-only access to /usr/bin/gh, merged with any user-configured sandbox.mcp.mounts
  • Export the computed value as MCP_GATEWAY_ALLOWED_MOUNT_ROOTS before gateway startup, and forward it into the gateway container via a new -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS flag
export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:rw,/tmp:rw,/usr/bin/gh:ro"

Tests & generated artifacts

  • Added unit tests covering the allowlist builder (dedup/merge behavior, determinism) and the new env flag/export
  • Recompiled all .lock.yml workflows and regenerated wasm golden fixtures to reflect the new exported env var

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix gateway configuration issue Fix MCP gateway mount policy rejection for safeoutputs backend server Aug 10, 2026
Copilot AI requested a review from pelikhan August 10, 2026 21:25
@lpcox

lpcox commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Thanks for addressing the mount-policy regression. I think this needs one compiler-side change before merge:

Requested change (blocking): include every compiler-supported per-server host mount in MCP_GATEWAY_ALLOWED_MOUNT_ROOTS. The current buildMCPGatewayAllowedMountRoots implementation appears to include gatewayConfig.Mounts (sandbox.mcp.mounts) only. It should also account for host sources declared through mcp-servers.<name>.mounts, volume mounts supplied through supported server args, and tools.github.mounts.

This belongs in the gh-aw compiler rather than mcpg. Once MCP_GATEWAY_ALLOWED_MOUNT_ROOTS is set, mcpg intentionally treats it as the complete policy. Having mcpg automatically trust mounts requested by an MCP server would make validation circular and weaken the security boundary. The compiler has the trusted workflow configuration and should derive the allowlist from all supported mount surfaces. Please add coverage using a custom containerized MCP server mount so the generated allowlist and resulting launch behavior are exercised.

While updating this, please preserve Docker semantics for mode-less mounts: they are writable, so they must not be added to the allowlist as ro.

Security tightening: ${RUNNER_TEMP}/gh-aw should remain ro; only the more specific ${RUNNER_TEMP}/gh-aw/safeoutputs path needs rw. Granting the full runtime tree rw unnecessarily exposes generated action scripts to backend MCP containers.

@lpcox

lpcox commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

@copilot apply the recommended change in this comment #51870 (comment)

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

Copilot AI commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for addressing the mount-policy regression. I think this needs one compiler-side change before merge:

**Requested change (blocking): include every compiler-supported per-server host mount in `...

Addressed in 17d1465: buildMCPGatewayAllowedMountRoots now also scans mcp-servers.<name>.mounts, tools.github.mounts, and -v/--volume flags in server args/entrypointArgs in addition to sandbox.mcp.mounts. Mode-less mounts are treated as rw per Docker semantics (fixed a pre-existing inversion). ${RUNNER_TEMP}/gh-aw is now ro, with a dedicated ${RUNNER_TEMP}/gh-aw/safeoutputs:rw entry. Also normalized backslash-escaped \${VAR} mount sources (used by imported partials like shared/mcp/serena.md) so they map to the same allowlist root as the real substituted path. Added unit test coverage for all of these, including a custom containerized MCP server mount, and recompiled all workflows/golden fixtures.

@copilot apply the recommended change in this comment #51870 (comment)

Done in 17d1465 — see my reply on the review comment above for details.

Copilot AI requested a review from lpcox August 11, 2026 00:08
@pelikhan
pelikhan marked this pull request as ready for review August 11, 2026 00:09
Copilot AI balanced review requested due to automatic review settings August 11, 2026 00:09
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.individual.githubcopilot.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.individual.githubcopilot.com"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Pre-fetched pr-diff.patch contains only compiled .lock.yml workflow files (generated artifacts), which are explicitly out of scope for ponytail over-engineering review. The actual source change (pkg/workflow/mcp_setup_gateway.go) is not present in the fetched diff, so there is no reviewable code to assess for complexity/over-engineering.

Generated by Ponytail Reviewer for #51870

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes MCP gateway startup failures by explicitly allowing compiler-supported host mounts.

Changes:

  • Builds and forwards MCP_GATEWAY_ALLOWED_MOUNT_ROOTS.
  • Merges built-in and user-configured mounts with deterministic permissions.
  • Adds tests and regenerates compiled artifacts.
Show a summary per file
File Description
pkg/workflow/mcp_setup_gateway.go Implements and forwards the mount allowlist.
pkg/workflow/mcp_gateway_mount_policy_test.go Tests mount collection, merging, and forwarding.
.github/workflows/*.lock.yml (284 files) Regenerates compiled workflows with the new environment variable.
pkg/workflow/testdata/TestWasmGolden_AllEngines/claude.golden Updates Claude golden output.
pkg/workflow/testdata/TestWasmGolden_AllEngines/codex.golden Updates Codex golden output.
pkg/workflow/testdata/TestWasmGolden_AllEngines/copilot.golden Updates Copilot golden output.
pkg/workflow/testdata/TestWasmGolden_AllEngines/gemini.golden Updates Gemini golden output.
pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden Updates basic compilation output.
pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden Updates Playwright compilation output.
pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden Updates smoke-test compilation output.
pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden Updates imported-workflow compilation output.

Review details

Tip

Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 228/294 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +463 to +465
entries = append(entries, root+":"+rootModes[root])
}
return strings.Join(entries, ",")

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The changes are consistent and correct across all lock files. Each file receives two coordinated changes: (1) a new MCP_GATEWAY_ALLOWED_MOUNT_ROOTS env export with a correctly structured path:mode list matching existing volume mounts, and (2) -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS passed to the docker run command so the gateway container receives the policy. Mount roots match the existing -v volume mounts. No correctness, security, or reliability concerns found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 45.9 AIC · ⌖ 9.04 AIC · ⊞ 5.4K

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report — PR #51870

Repository: github/gh-aw
PR: Fix MCP gateway mount policy rejection for safeoutputs backend server
Actor: pelikhan


Score: 100/100Excellent

Implementation Test Ratio: 0% (threshold: 30%) ✅
Test Inflation Ratio: ~1.5:1 (threshold: 2:1) ✅
Violations: None ✅


Summary

This PR adds 13 new/modified behavioral tests to pkg/workflow/mcp_gateway_mount_policy_test.go. All tests are design tests that verify critical behavioral contracts for the MCP gateway mount allowlist policy — no implementation details, no mock library violations, no build-tag violations.

Key strengths:

  • ✅ 100% design-test coverage (verifies user-visible behavior and policy invariants)
  • ✅ Comprehensive edge-case coverage: Docker defaults, variable escaping, custom server integration, mount deduplication
  • ✅ Determinism test ensures idempotent allowlist builder
  • ✅ Proper use of testify/assert (no gomock, testify/mock, or .EXPECT())
  • ✅ Correct (go/redacted):build !integration tag on test file
  • ✅ High assertion density (1–6 focused assertions per test)
  • ✅ Balanced positive/negative assertions (Contains/NotContains)

Test Classification Table (13 tests, 5 new)
Test Type Value Design Assertions Coverage
nil tools and gateway config... unit high design_test 5 Builtin roots always included
default gateway mounts merged unit high design_test 6 Config merge behavior
custom rw mount widens ro root unit high design_test 2 Mount override semantics
rw root not downgraded by ro mount unit high design_test 2 Never-downgrade policy
mode-less gateway mount defaults rw unit high design_test 2 Docker semantics (NEW)
output is deterministic unit high design_test 1 Idempotency guarantee
mcp-servers.(name).mounts included unit high design_test 2 Custom server discovery (NEW)
tools.github.mounts included unit high design_test 1 GitHub tool mount discovery (NEW)
backslash-escaped mount vars unit high design_test 2 Import partial escaping (NEW)
volume mounts in args included unit high design_test 4 Docker -v/--volume discovery (NEW)
Container command includes env flag unit high design_test 1 Env flag presence
Write exports includes mounts unit high design_test 1 Export script generation
Write exports includes custom mounts unit high design_test 1 Custom server integration (NEW)

All 13 tests are design tests verifying behavioral contracts.


Quality Signals

Edge-case & error coverage:

  • ✅ Mode-less mounts default to read-write (Docker semantics)
  • ✅ Backslash-escaped variables in imported partials normalize correctly
  • ✅ Volume discovery in multiple Docker formats (-v, --volume, --volume=)
  • ✅ Mount policy enforcement: never downgrade from rw to ro
  • ✅ Deduplication: identical roots merged once
  • ✅ Determinism: same input produces same output (idempotency)

Code quality:

  • ✅ Correct build tag ((go/redacted):build !integration on line 1)
  • ✅ No mock libraries (gomock, testify/mock) — only testify/assert
  • ✅ All assertions use descriptive t.Run() subtests
  • ✅ Balanced positive and negative assertions
  • ✅ No test inflation (93 test lines added vs. ~50–70 prod lines ≈ 1.5:1)
  • ✅ Clear, focused test names describing the scenario

Verdict:APPROVED — Excellent test quality with comprehensive behavioral coverage and no violations.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 19.8 AIC · ⌖ 3.64 AIC · ⊞ 7.6K ·
Comment /review to run again

Documents the decision to introduce buildMCPGatewayAllowedMountRoots
in the compiler to resolve gh-aw-mcpg v0.4.9 mount policy violations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (366 new lines in pkg/) but did not have a linked Architecture Decision Record (ADR).

Draft ADR committed: docs/adr/51870-mcp-gateway-explicit-mount-roots-allowlist.md — review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-51870: Compiler-Computed MCP Gateway Mount-Roots Allowlist

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 51870-mcp-gateway-explicit-mount-roots-allowlist.md for PR #51870).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 83.6 AIC · ⌖ 22.4 AIC · ⊞ 8.7K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 100/100. 0% implementation tests (threshold: 30%). All 13 tests are behavioral design tests with comprehensive edge-case coverage and no code quality violations.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — two minor suggestions on test robustness; no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Hardcoded ordered assertion (test line 158): the TestWriteMCPGatewayExportsIncludesAllowedMountRoots assertion embeds the full sorted comma-separated value; this will produce an opaque failure if a new built-in root is added. Individual Contains checks (as used elsewhere in the same file) are more resilient.
  • Untested scalar-tool-entry path (collectMCPServerConfiguredMounts): the early-exit continue for non-map[string]any values is correct Go, but a small table test would document and lock in the no-panic guarantee.

Positive Highlights

  • ✅ Root cause addressed, not just the symptom: exports the allowlist before gateway startup so mcpg never sees an unlisted mount.
  • addRoot dedup logic correctly prevents rw → ro downgrade while allowing ro → rw upgrade.
  • ✅ Backslash-escape normalization of \${VAR} in parseMCPGatewayAllowlistMount closes a subtle injection/mismatch path from imported partials.
  • ✅ Determinism guaranteed via sliceutil.SortedKeys, with an explicit determinism test.
  • ✅ Both args and entrypointArgs volume-flag patterns (-v, --volume, --volume=, -v=) are covered, including a test for mode-less mounts defaulting to rw.
  • ✅ Golden fixture updates included — reviewers can see the exported env var in compiled output.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 70.6 AIC · ⌖ 6.82 AIC · ⊞ 7.1K
Comment /matt to run again

payloadDir: "/tmp/payloads",
})

assert.Contains(t, runScript.String(), `export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/tmp:rw,/usr/bin/gh:ro"`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/tdd] This assertion hard-codes the full sorted comma-separated string, coupling the test to insertion order and the exact set of built-in roots.

If a new built-in root is added to buildMCPGatewayAllowedMountRoots, this test fails with an opaque string mismatch instead of pointing to the new entry. The determinism test on line 68 already covers ordering stability.

💡 Suggested approach

Replace with individual Contains assertions (consistent with how TestBuildMCPGatewayAllowedMountRoots already tests individual entries):

assert.Contains(t, runScript.String(), `export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS=`)
assert.Contains(t, runScript.String(), `${GITHUB_WORKSPACE}:rw`)
assert.Contains(t, runScript.String(), `${RUNNER_TEMP}/gh-aw:ro`)
assert.Contains(t, runScript.String(), `${RUNNER_TEMP}/gh-aw/safeoutputs:rw`)
assert.Contains(t, runScript.String(), `/tmp:rw`)
assert.Contains(t, runScript.String(), `/usr/bin/gh:ro`)

@copilot please address this.

// can be reflected in the gateway's trusted mount allowlist. This covers
// explicit "mounts" arrays as well as "-v"/"--volume" flags embedded in
// "args"/"entrypointArgs" for containerized servers.
func collectMCPServerConfiguredMounts(tools map[string]any) []string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/diagnosing-bugs] collectMCPServerConfiguredMounts iterates a tools map[string]any with no test for the nil path, and silently skips non-map[string]any tool entries. Both are safe in Go, but there is no test that verifies mount entries from a tool whose value is not a map are skipped without panic.

This is a minor gap — worth a short table-driven test case to confirm that a scalar tool entry ("my-tool": "somestring") does not cause a nil-pointer dereference.

💡 Suggested test case
t.Run("scalar tool value is silently skipped", func(t *testing.T) {
    tools := map[string]any{
        "not-a-map": "just-a-string",
        "my-server": map[string]any{
            "mounts": []any{"/srv/(redacted)
        },
    }
    roots := buildMCPGatewayAllowedMountRoots(tools, nil)
    assert.Contains(t, roots, "/srv/(redacted)
    // scalar entry should be silently ignored, not panic
})

@copilot please address this.

@pelikhan
pelikhan merged commit 67b4a88 into main Aug 11, 2026
29 checks passed
@pelikhan
pelikhan deleted the copilot/fix-gateway-configuration branch August 11, 2026 01:03
github-actions Bot added a commit that referenced this pull request Aug 11, 2026
Add two new terms based on recent commits (#51842, #51870):
- Host Service Ports (services:) — AWF sandbox access to GitHub
  Actions services containers via --allow-host-service-ports
- MCP Gateway Mount-Roots Allowlist (MCP_GATEWAY_ALLOWED_MOUNT_ROOTS) —
  compiler-computed mount policy allowlist for MCP backend containers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.86.2

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.

4 participants