Skip to content

fix: add write-sink guard policies for non-GitHub MCP servers on auto-lockdown#21342

Merged
lpcox merged 6 commits intomainfrom
copilot/fix-write-sink-guard-policies
Mar 17, 2026
Merged

fix: add write-sink guard policies for non-GitHub MCP servers on auto-lockdown#21342
lpcox merged 6 commits intomainfrom
copilot/fix-write-sink-guard-policies

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

When the github tool is configured without explicit repos/min-integrity, the auto-lockdown step always sets repos=all at runtime — but non-GitHub MCP servers (playwright, serena, mcp-scripts, safeoutputs, agentic-workflows, web-fetch, custom) had no corresponding write-sink: { accept: ["*"] } guard policies compiled in, causing the MCP gateway to block writes.

Changes

  • mcp_github_config.goderiveWriteSinkGuardPolicyFromWorkflow now returns write-sink: { accept: ["*"] } when the GitHub tool is present without explicit guard policies and without a GitHub App configured (the exact conditions that generate the auto-lockdown step)

  • mcp_config_builtin.go / mcp_renderer_builtin.go — safeoutputs rendering updated to use deriveWriteSinkGuardPolicyFromWorkflow instead of calling deriveSafeOutputsGuardPolicyFromGitHub directly, so it benefits from the same auto-lockdown detection

  • Tests — updated to expect guard policies in the auto-lockdown case; added TestNonGitHubMCPServersNoGuardPoliciesWithGitHubApp to cover the GitHub App exclusion; added TestAllNonGitHubMCPServersGetWriteSinkWhenGitHubHasAllowOnly to verify that when GitHub has an explicit allow-only guard policy configured (all repos variants and all min-integrity levels), every non-GitHub MCP server type (playwright, serena, agentic-workflows, mcp-scripts, safe-outputs, web-fetch) receives a correctly derived write-sink guard-policy; tightened unexpectedInServer checks in compilation tests to target the tools array wildcard specifically rather than any "*" in the server block

  • Lock files — all 172 workflow files recompiled; non-GitHub MCP servers now include write-sink: { accept: ["*"] } in the generated JSON config when auto-lockdown applies

Before (smoke-copilot, no explicit min-integrity):

"playwright": { "type": "stdio", ... }
// no guard-policies → gateway blocks writes

After:

"playwright": {
  "type": "stdio", ...,
  "guard-policies": { "write-sink": { "accept": ["*"] } }
}

[!WARNING]


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.



✨ PR Review Safe Output Test - Run 23180654460

💥 [THE END] — Illustrated by Smoke Claude ·

Copilot AI linked an issue Mar 17, 2026 that may be closed by this pull request
…to-lockdown is used

When a workflow has the github tool configured without explicit repos/min-integrity
guard policies, the auto-lockdown detection step runs at runtime and always sets
repos=all. Non-GitHub MCP servers (playwright, serena, mcpscripts, safeoutputs,
agentic-workflows, web-fetch, custom) were missing the corresponding write-sink
guard policies needed for the MCP gateway to allow writes to these servers.

- Modify deriveWriteSinkGuardPolicyFromWorkflow to return accept=["*"] write-sink
  policy when GitHub tool is present, no explicit guard policies configured, and no
  GitHub App configured (same conditions that trigger auto-lockdown)
- Update renderSafeOutputsMCPConfigWithOptions and renderSafeOutputsTOML to use
  deriveWriteSinkGuardPolicyFromWorkflow instead of deriveSafeOutputsGuardPolicyFromGitHub
- Update tests to reflect new behavior and add GitHub App exclusion test
- Recompile all 172 workflow lock files with the fix applied

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix missing write-sink guard policies for non-github mcp servers fix: add write-sink guard policies for non-GitHub MCP servers on auto-lockdown Mar 17, 2026
Copilot AI requested a review from lpcox March 17, 2026 05:51
@pelikhan pelikhan marked this pull request as ready for review March 17, 2026 05:56
Copilot AI review requested due to automatic review settings March 17, 2026 05:56
@pelikhan pelikhan added the smoke label Mar 17, 2026
@github-actions
Copy link
Contributor

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

@github-actions github-actions bot removed the smoke label Mar 17, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

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

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

✅ All tools validated successfully! Agent Container Smoke Test confirms agent container is ready.

@lpcox
Copy link
Collaborator

lpcox commented Mar 17, 2026

@copilot also add new tests to ensure that if the github-mcp server has a configured allow-only guard-policy that all non-github mcp servers have a write-sink guard-policy.

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

Adds a missing “write-sink” guard policy for non-GitHub MCP servers when GitHub auto-lockdown is inferred (GitHub tool present but repos/min-integrity not explicitly configured), preventing the MCP gateway from blocking writes in that runtime scenario.

Changes:

  • Extend deriveWriteSinkGuardPolicyFromWorkflow to emit write-sink: { accept: ["*"] } in the auto-lockdown detection case (with a GitHub App exclusion).
  • Route safeoutputs guard-policy rendering through deriveWriteSinkGuardPolicyFromWorkflow so it matches the same auto-lockdown behavior.
  • Update/extend tests and regenerate workflow lockfiles / golden fixtures to reflect the newly compiled guard-policies.

Reviewed changes

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

Show a summary per file
File Description
pkg/workflow/mcp_github_config.go Derives non-GitHub write-sink guard policies from GitHub config, including new auto-lockdown fallback.
pkg/workflow/mcp_config_builtin.go Updates safeoutputs JSON config rendering to use the new workflow-level guard policy derivation.
pkg/workflow/mcp_renderer_builtin.go Updates safeoutputs TOML rendering to use the new workflow-level guard policy derivation.
pkg/workflow/non_github_mcp_guard_policy_test.go Updates/adds tests for auto-lockdown guard-policy propagation and GitHub App exclusion.
pkg/workflow/mcp_config_compilation_test.go Tightens compilation assertions to avoid false positives from accept: ["*"] in guard-policies.
pkg/workflow/testdata/wasm_golden/TestWasmGolden_CompileFixtures/smoke-copilot.golden Updates wasm golden fixture with new compiled guard-policies blocks.
.github/workflows/*.lock.yml (regenerated set) Recompiled workflow lockfiles now include guard-policies.write-sink.accept=["*"] for non-GitHub MCP servers when auto-lockdown applies.
Comments suppressed due to low confidence (1)

pkg/workflow/mcp_config_builtin.go:153

  • The log message says guard-policies are derived from the GitHub guard-policy, but deriveWriteSinkGuardPolicyFromWorkflow can now also return a default policy from auto-lockdown detection (no explicit repos/min-integrity). Consider adjusting the message to avoid implying explicit guard policies were set.

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

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 351 to 356
// When no explicit guard policy is configured but automatic lockdown detection would run
// (GitHub tool present and not disabled, no GitHub App configured), a write-sink policy with
// accept=["*"] is returned because automatic lockdown always sets repos=all at runtime.
//
// Returns nil when no GitHub guard policies are configured or when workflowData is nil.
func deriveWriteSinkGuardPolicyFromWorkflow(workflowData *WorkflowData) map[string]any {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 78f43de. The docstring now enumerates both non-nil cases (explicit guard policy and auto-lockdown) and clarifies the two nil-return conditions (no GitHub tool, or GitHub App configured). The log messages in mcp_config_builtin.go and mcp_renderer_builtin.go were also updated to mention auto-lockdown alongside guard-policy derivation.

Comment on lines +220 to +224
serverName: `"my-api"`,
expectedContent: []string{`"get_data"`, `"list_items"`},
// Check that the tools array specifically does not have the wildcard
// (guard-policies accept may still contain "*" at deeper indentation)
unexpectedInServer: []string{"\"tools\": [\n \"*\""},
Copy link
Collaborator

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 2543fd9. The unexpectedInServer loop now uses regexp.MatchString instead of strings.Contains, and the three brittle patterns have been changed from whitespace-sensitive literals like "\"tools\": [\n \"*\"" to a whitespace-agnostic regex: "tools"\s*:\s*\[\s*"\*". This matches "tools": ["*"] in any formatting without false-positives from guard-policies's "accept": ["*"] (different key name).

Comment on lines +175 to +179
guardPolicies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
if len(guardPolicies) > 0 {
mcpRendererLog.Print("Adding guard-policies to safeoutputs TOML (derived from GitHub guard-policy)")
// Render guard-policies in TOML format
renderGuardPoliciesToml(yaml, guardPolicies, constants.SafeOutputsMCPServerID.String())
Copy link
Contributor

Choose a reason for hiding this comment

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

✅ Agreed — the log message could be more precise to distinguish the auto-lockdown-derived case from the explicit guard-policy case.

📰 BREAKING: Report filed by Smoke Copilot

Copy link
Collaborator

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already fixed in 78f43de — the log message was updated to "derived from GitHub guard-policy or auto-lockdown detection" in both mcp_config_builtin.go and mcp_renderer_builtin.go.

@github-actions
Copy link
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21
sh available
git 2.53.0
jq 1.7
yq v4.52.4
curl 8.5.0
gh 2.87.3
node v20.20.1
python3 3.12.3
go 1.24.13
java 10.0.102
dotnet 10.0.102

Result: 12/12 tools available ✅

Overall Status: PASS

🔧 Tool validation by Agent Container Smoke Test ·

@github-actions
Copy link
Contributor

Smoke Test (Codex)

PRs: #21338 [smoke-multi-pr] PR 1: Documentation smoke test; #21311 [docs] docs: condense MultiRepoOps best practices and remove redundant sections
GitHub MCP: ✅
Serena MCP: ✅
Playwright: ✅
Web-fetch: ✅
File write: ✅
Bash cat verify: ✅
Build (make build): ✅
Overall: PASS

🔮 The oracle has spoken through Smoke Codex ·

Warning

⚠️ Firewall blocked 1 domain

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

  • ab.chatgpt.com

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

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

…y for explicit allow-only guard policies

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

Copilot AI commented Mar 17, 2026

@copilot also add new tests to ensure that if the github-mcp server has a configured allow-only guard-policy that all non-github mcp servers have a write-sink guard-policy.

Added TestAllNonGitHubMCPServersGetWriteSinkWhenGitHubHasAllowOnly in 485e699. The test is table-driven across 4 explicit allow-only configurations (repos=all, repos=public, repos=myorg/myrepo, repos=myorg/*) and verifies that all 6 non-GitHub MCP server types — playwright, serena, agentic-workflows, mcp-scripts, safe-outputs, and web-fetch — each produce a write-sink guard-policy with the correctly derived accept list.

@github-actions
Copy link
Contributor

Smoke Test Results§23180654468

Test Status
GitHub MCP
MCP Scripts GH CLI
Serena MCP
Playwright
Web Fetch
File Writing + Bash
Discussion Interaction
Build gh-aw
Discussion Creation
Workflow Dispatch
PR Review

Overall: ⚠️ PARTIAL PASS (11/12) — Serena MCP tools unavailable.

cc @pelikhan @lpcox

📰 BREAKING: Report filed by Smoke Copilot ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Smoke test review on PR #21342. This fix correctly extends write-sink guard policies to all non-GitHub MCP servers during auto-lockdown. The refactoring is clean and the mass-recompilation of 172 lock files ensures the fix is applied consistently across all workflows.

📰 BREAKING: Report filed by Smoke Copilot

renderGuardPoliciesToml(yaml, guardPolicies, constants.SafeOutputsMCPServerID.String())
}
}
guardPolicies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
Copy link
Contributor

Choose a reason for hiding this comment

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

Consistent with the change in mcp_config_builtin.go. The simplification from nested if-blocks to a single function call improves readability. Consider adding a test case that verifies this TOML path also gets guard policies when auto-lockdown conditions are met.

guardPolicies = deriveSafeOutputsGuardPolicyFromGitHub(githubTool)
}
}
guardPolicies := deriveWriteSinkGuardPolicyFromWorkflow(workflowData)
Copy link
Contributor

Choose a reason for hiding this comment

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

Good refactor — centralizing the guard policy derivation through deriveWriteSinkGuardPolicyFromWorkflow ensures non-GitHub MCP servers get consistent write-sink policies when auto-lockdown triggers. This is cleaner than the previous per-server checks.

@github-actions
Copy link
Contributor

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

@github-actions
Copy link
Contributor

Smoke Test Results — Run §23180654460

Overall: PARTIAL (2 skipped, rest passed)

Test Result
1 GitHub MCP
2 GH CLI
3 Serena MCP
4 Make Build
5 Playwright
6 Tavily Search
7 File Write
8 Bash
9 Discussion
10 AW Status
11 Update PR
12 Review Comments
13 Submit Review
14 Resolve Thread ⚠️ skipped
15 Add Reviewer
16 Push Branch
17 Close PR ⚠️ skipped

💥 [THE END] — Illustrated by Smoke Claude ·

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

💥 Automated smoke test review - all systems nominal!

💥 [THE END] — Illustrated by Smoke Claude

"GITHUB_REPOSITORY": "$GITHUB_REPOSITORY"
},
"guard-policies": {
"write-sink": {
Copy link
Contributor

Choose a reason for hiding this comment

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

The write-sink guard policy with accept: ["*"] here correctly applies the write-sink restriction for non-GitHub MCP servers on auto-lockdown. This ensures that only trusted write destinations are permitted.

"mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"]
"mounts": ["/tmp/gh-aw/mcp-logs:/tmp/gh-aw/mcp-logs:rw"],
"guard-policies": {
"write-sink": {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good addition of write-sink guard policy for the Playwright MCP server. Consistent pattern with other non-GitHub MCP servers in this workflow.

…FromWorkflow auto-lockdown case

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

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

Copilot AI commented Mar 17, 2026

@copilot also add new tests to ensure that if the github-mcp server has a configured allow-only guard-policy that all non-github mcp servers have a write-sink guard-policy.

Already added TestAllNonGitHubMCPServersGetWriteSinkWhenGitHubHasAllowOnly in 485e699. The test is table-driven across repos=all, repos=public, repos=myorg/myrepo, and repos=myorg/*, verifying that playwright, serena, agentic-workflows, mcp-scripts, safe-outputs, and web-fetch all get the correct write-sink guard-policy.

@lpcox lpcox merged commit b28dccb into main Mar 17, 2026
53 checks passed
@lpcox lpcox deleted the copilot/fix-write-sink-guard-policies branch March 17, 2026 06:29
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.

Missing write-sink guard policies

4 participants