Skip to content

fix: isolate MCP script secrets per tool - #73887

Merged
pelikhan merged 4 commits into
mainfrom
pelikhan/mcp-tool-secret-isolation
Nov 2, 2026
Merged

pelikhan merged 4 commits into
mainfrom
pelikhan/mcp-tool-secret-isolation

Conversation

@pelikhan

@pelikhan pelikhan commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

MCP scripts startup merged every tool's environment into a shared map, allowing handlers to read sibling secrets and overwriting bindings when tools declared the same variable name. This addresses HIGH CWE-200 code scanning alert 1058.

Approach

  • Give each (tool name, variable name) pair a distinct transport reference shared by the generated manifest and startup environment.
  • Build fresh JavaScript, Bash, Python, and Go child environments from an ordinary runtime allowlist plus only that tool's declared bindings. Missing references fail explicitly.
  • Keep tool credentials out of agent/gateway environments and dependency installers. Installers retain a separate allowlist for credential-free proxy, registry/index and CA configuration, rejecting unsafe settings without logging their values.
  • Explicitly bind repository context in shared query and PR-data tools to preserve omitted-repository defaults.
  • Add compiler and concurrent runtime regressions for overlapping names, sibling/server credential exclusion, ambient configuration decisions, installer compatibility and shared defaults. Update related docs, generated locks and native/WASM golden fixtures.

Compatibility and security boundary

Existing workflows require recompilation. Handlers no longer inherit ambient TLS/proxy/registry settings, PYTHONPATH or GitHub metadata; declare additional configuration in each tool's env: block. Dependency installers preserve allowlisted non-credential configuration, but do not inherit tool/server credentials or credential-file overrides. Authenticated installer URLs are unsupported; forwarded endpoint paths must also be non-secret because arbitrary path-embedded credentials cannot be detected.

Tools still execute trusted code on the runner host: this environment boundary is not a filesystem or network sandbox.

ADR: ADR-73887: Isolate MCP Script Secrets per Tool. The rationale and alternatives are complete; maintainer acceptance and actual deciders remain unrecorded, so the ADR is Proposed.

Validation

  • make build, make fmt, and make lint-cjs passed for the isolation change.
  • The latest combined review fixes passed make agent-report-progress, including lint, type-check, impacted Go/JavaScript tests and workflow drift.
  • Focused handler/installer environment suites: 53 tests passed. Shared repository-default regressions and all native WASM golden fixtures passed; the rebuilt WASM smoke fixture was verified.
  • make recompile compiled all 353 workflows and regenerated affected locks, including downstream dispatcher contract hashes.
  • Earlier complete handler/server runtime coverage passed 172 tests with 3 skipped. Canonical macOS temporary paths and serial test files avoided existing path-alias assertions and shared-temp-file count races.

No workflows were dispatched and the alert was not dismissed.

pelikhan and others added 2 commits November 1, 2026 11:28
Use distinct transport bindings for each tool and variable, build restricted environments for all four handler languages, and keep tool credentials out of agent, gateway, and dependency installer environments. Add compiler/runtime regressions and regenerate affected workflow locks.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review November 1, 2026 19:46
Copilot AI auto review requested due to automatic review settings November 1, 2026 19:46
@github-actions

github-actions Bot commented Nov 1, 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 Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Test Quality Sentinel · sonnet50 · ~16 AIC · ⌖ ~19 AIC · ◇ ~8 AIC · ⊞ 12.7K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Ponytail Reviewer

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Ponytail Reviewer · gpt56 · ~15 AIC · ⌖ ~6 AIC · ⊞ 15.1K · ◷

Generated by Ponytail Reviewer for #73887

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

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

📄 Draft ADR committed: docs/adr/73887-isolate-mcp-script-secrets-per-tool.md. Review and complete it before merging.

Inferred decision: each (tool, variable) pair gets its own collision-free transport variable (GH_AW_MCP_SCRIPTS_ENV_<hex(tool)>_<hex(name)>). Each handler then runs in a fresh environment that contains only the runtime allowlist and that tool's bindings. MCP script credentials are also removed from the agent, gateway and dependency-installer environments.

🔒 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. Fill in what the draft couldn't infer. Confirm the deciders, refine the rationale and check the alternatives: a spawn-time denylist, one server per tool, and sanitized instead of hex-encoded names.

  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-73887: Isolate MCP Script Secrets per Tool (link unavailable: invalid HTTPS URL)

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

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does.

📋 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.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · routed: opus55 medium · ~33 AIC · ⌖ ~2 AIC · ⊞ 7.7K · ◷
Comment /review to run again

@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-11-01T19:55:00Z
review_event: COMMENT
top_themes:
  - dependency installer environment compatibility (proxy, CA, private registry variables)
files_reviewed:
  - actions/setup/js/mcp_dependencies_manager.cjs
  - actions/setup/js/mcp_handler_process.cjs
  - actions/setup/js/mcp_server_core.cjs
  - pkg/workflow/mcp_scripts_generator.go
  - pkg/workflow/mcp_setup_scripts.go
comment_count: 1

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · ~7 AIC · ⌖ ~1 AIC · ⊞ 1.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.

Review

One compatibility issue needs attention: dependency installers now receive a narrowly filtered environment and lose proxy, CA, and private-registry configuration required on enterprise/firewalled runners. I left an inline comment with the impact and suggested remediation. No blocking correctness or security issue was identified in the changed code.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · ~7 AIC · ⌖ ~1 AIC · ⊞ 1.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.

Skills-Based Review 🧠

Applied /codebase-design and /tdd. The isolation approach is sound: tool secrets are removed from the agent and gateway env, and each handler gets only its own bindings. Recommending a decision on the allowlist before merge.

📋 Key Themes & Highlights

Key Themes

  • Allowlist compatibility: createToolEnvironment drops CA, proxy, registry, and GITHUB_* context variables. A Node probe confirmed this (inline comment on mcp_handler_process.cjs). The change may be intended, but it is a silent behaviour change for tools that rely on those variables.
  • Test gap: the new isolation test covers overlapping names and sibling exclusion, but does not pin which ambient names are forwarded (inline comment on the test file).
  • Docs contradiction: docs/src/content/docs/specs/mcp-scripts-specification.md line 458 still says "Shell/Python/Go tools execute in separate containers". The reference doc and the spec's own boundary note say they run on the runner host. Correct line 458.
  • Cosmetic: pkg/workflow/mcp_environment.go around lines 200–206 leaves a blank line inside the IsMCPScriptsEnabled block after the removed maps.Copy. Remove it.

Positive Highlights

  • ✅ Secret-validation behaviour is unchanged: the base never listed tool secrets in collectCommonMCPSecrets, so no fail-fast regression.
  • ✅ applyMCPScriptsSecretEnv has no remaining callers and GH_AW_GH_TOKEN has no remaining users.
  • ✅ Transport names are hex-encoded, so a-b/a_b collisions are prevented and tested.

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · routed: haiku55 xhigh · ~8 AIC · ⌖ ~1 AIC · ⊞ 5.4K
Comment /matt to run again

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.

🟡 Changes recommended

The environment allowlist removes required repository variables without rebinding them in shared tools, breaking discussion queries and issue/PR queries with since when repo is omitted.

1 open issue
What changed in this PR

This PR addresses MCP-script secret exposure and environment-variable collisions by introducing per-tool bindings and restricting credential propagation across the compiler and runtime.

Changes:

  • Generate distinct transport references for each tool/variable pair; stop forwarding tool credentials to agents and gateways.
  • Build allowlisted environments for JavaScript, Bash, Python, Go, and dependency installers.
  • Add isolation regressions, document migration and security boundaries, and regenerate affected workflow locks.

This pull request requires changes. Two moderate regressions remain:

  • actions/setup/js/mcp_dependencies_manager.cjs:71: The allowlist drops supported proxy variables, including lowercase equivalents, breaking registry access on proxied runners. Installers cannot recover through tool env: declarations; handlers also lose proxy configuration unless explicitly rebound. Preserve these settings in the shared runtime allowlist and documentation.
  • actions/setup/js/mcp_handler_process.cjs:22: Filtering out GITHUB_REPOSITORY and GITHUB_REPOSITORY_OWNER breaks bundled query tools’ default-repository behavior. Explicitly bind the required repository context in the shared tools’ env: declarations and regenerate their locks.
File Description
pkg/​workflow/​testdata/​TestWasmGolden_CompileFixtures/​smoke-copilot.golden Refreshes expected bindings and credential routing.
pkg/​workflow/​mcp_scripts_renderer.go Collects tool-scoped startup bindings.
pkg/​workflow/​mcp_scripts_renderer_test.go Tests binding separation and startup placement.
pkg/​workflow/​mcp_scripts_http_codex_test.go Updates HTTP transport binding expectations.
pkg/​workflow/​mcp_scripts_generator.go Generates collision-free per-tool transport references.
pkg/​workflow/​mcp_scripts_generator_test.go Tests tool-specific manifest references.
pkg/​workflow/​mcp_environment.go Removes tool credentials from gateway environments.
pkg/​workflow/​engine_helpers.go Removes shared script-secret propagation helpers.
pkg/​workflow/​engine_helpers_secrets_test.go Verifies server-only credential exclusion.
pkg/​workflow/​enclaves_runtime.go Removes script bindings from enclave routing.
pkg/​workflow/​copilot_engine.go Excludes script secrets from forwarding authorization.
pkg/​workflow/​copilot_engine_execution.go Stops injecting tool credentials into Copilot.
pkg/​workflow/​codex_engine.go Stops injecting tool credentials into Codex.
pkg/​workflow/​codex_engine_test.go Tests credential exclusion across engines.
pkg/​workflow/​claude_engine.go Stops injecting tool credentials into Claude.
pkg/​workflow/​behavior_defined_engine.go Removes credential injection for declarative engines.
pkg/​workflow/​agy_engine_execution.go Stops injecting tool credentials into Agy.
docs/​src/​content/​docs/​specs/​mcp-scripts-specification.md Defines per-tool bindings and environment boundaries.
docs/​src/​content/​docs/​reference/​mcp-scripts.md Documents allowlisting and recompilation requirements.
docs/​adr/​73887-isolate-mcp-script-secrets-per-tool.md Records the isolation decision and tradeoffs.
actions/​setup/​js/​mcp_server_core.cjs Passes tool bindings to language handlers.
actions/​setup/​js/​mcp_scripts_environment.test.cjs Adds concurrent, cross-language isolation regressions.
actions/​setup/​js/​mcp_scripts_config_loader.cjs Documents manifest environment references.
actions/​setup/​js/​mcp_handler_shell.cjs Builds tool-local shell environments.
actions/​setup/​js/​mcp_handler_python.cjs Builds tool-local Python environments.
actions/​setup/​js/​mcp_handler_process.cjs Centralizes allowlisting and binding resolution.
actions/​setup/​js/​mcp_handler_javascript.cjs Builds tool-local JavaScript environments.
actions/​setup/​js/​mcp_handler_go.cjs Builds tool-local Go environments.
actions/​setup/​js/​mcp_dependencies_manager.test.cjs Tests installer credential exclusion.
actions/​setup/​js/​mcp_dependencies_manager.cjs Filters dependency-installer environments.
.github/​workflows/​smoke-copilot.lock.yml Regenerates scoped query-tool bindings.
.github/​workflows/​smoke-copilot-arm.lock.yml Updates ARM smoke-test credential routing.
.github/​workflows/​smoke-copilot-aoai-entra.lock.yml Updates Entra smoke-test credential routing.
.github/​workflows/​smoke-copilot-aoai-apikey.lock.yml Updates API-key smoke-test credential routing.
.github/​workflows/​smoke-claude-copilot.lock.yml Scopes the probe’s receipt-path binding.
.github/​workflows/​github-mcp-structural-analysis.lock.yml Scopes label/workflow query credentials.
.github/​workflows/​engine-conformance-pydantic-ai.lock.yml Scopes conformance state to its tool.
.github/​workflows/​engine-conformance-pi.lock.yml Scopes conformance state to its tool.
.github/​workflows/​engine-conformance-opencode.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-kiro.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-goose.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-gemini.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-deepseek-harness.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-cursor.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-crush.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​engine-conformance-copilot.lock.yml Removes conformance state from agent environments.
.github/​workflows/​engine-conformance-codex.lock.yml Removes conformance state from agent environments.
.github/​workflows/​engine-conformance-claude.lock.yml Removes conformance state from agent environments.
.github/​workflows/​engine-conformance-aider.lock.yml Updates conformance-state routing and forwarding exclusions.
.github/​workflows/​deep-report.lock.yml Scopes reporting query credentials.
.github/​workflows/​daily-regulatory.lock.yml Regenerates scoped GitHub-query bindings.
.github/​workflows/​daily-performance-summary.lock.yml Regenerates scoped GitHub-query bindings.
.github/​workflows/​daily-mcp-concurrency-analysis.lock.yml Scopes the audit run-ID binding.
.github/​workflows/​daily-caveman-optimizer.lock.yml Scopes the optimizer run-ID binding.

Review effort: Max (auto)

Keep credential-free proxy, registry and CA settings in a dedicated installer environment; document and test the handler allowlist contract. Explicitly bind repository context for shared tools, refresh generated locks and WASM goldens, and complete the isolation ADR without asserting maintainer approval.

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

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: This is an informational completion notice, so it needs no separate code change. The actionable review feedback was addressed in 88834f0.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: The reported_no_action outcome does not establish validation, so no code change is based on this notice alone. The combined fixes passed the final validation gate, focused environment tests and shared-default/golden checks before being pushed.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: Accepted: the generated ADR was reviewed and completed in 88834f0, and it is now linked in the PR body. Its status remains Proposed because maintainer acceptance has not been recorded.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: The completion notification itself needs no code change. The review's installer compatibility finding was addressed with a dedicated non-credential configuration allowlist and focused regressions in 88834f0.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: This reported_no_action notice does not independently verify implementation or publication, so it needs no separate code change. The combined review fixes were validated and pushed in 88834f0.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: Partially accepted: the ADR's context, decision, alternatives and consequences are complete and linked in the PR body, including migration and trusted-runner limitations. Actual deciders and acceptance remain unrecorded rather than inventing maintainer approval; the ADR stays Proposed.

@pelikhan

pelikhan commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

Reply to comment: The managed metadata needs no independent code change, and its memory fence was left untouched. The referenced installer compatibility theme was addressed in 88834f0 with configuration-preservation and credential-exclusion tests.

@pelikhan
pelikhan merged commit 19002c9 into main Nov 2, 2026
86 checks passed
@pelikhan
pelikhan deleted the pelikhan/mcp-tool-secret-isolation branch November 2, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants