Skip to content

Prevent secret masking from MCP servers - #50746

Merged
pelikhan merged 6 commits into
mainfrom
pelikhan/prevent-mcp-core-set-secret
Aug 6, 2026
Merged

pelikhan merged 6 commits into
mainfrom
pelikhan/prevent-mcp-core-set-secret

Conversation

@pelikhan

@pelikhan pelikhan commented Aug 6, 2026 •

Copy link
Copy Markdown
Collaborator

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.

Overview

Prevents MCP server processes from calling core.setSecret (a GitHub Actions workflow command), which is unsafe when invoked from an MCP server context outside the github-script runtime. Secret masking logic is separated from MCP-safe git authentication environment construction.

Key Changes

  • New actions_secret_masking.cjs: introduces maskSecret, which calls core.setSecret when available or falls back to emitting a raw ::add-mask:: workflow command — without requiring the real @actions/core module.
  • New git_auth_env.cjs: introduces buildGitAuthEnv, which builds git authentication environment variables without calling core.setSecret, making it safe to use from MCP server processes.
  • git_auth_helpers.cjs / git_helpers.cjs: getGitAuthEnv now delegates environment construction to buildGitAuthEnv and applies maskSecret for credential masking only where safe (Actions context), not inside the MCP-safe path.
  • shim.cjs: setSecret now throws when called outside the github-script runtime instead of silently emitting an escaped add-mask workflow command; escapeCommandData helper was removed as no longer needed.
  • Call sites updated (action_setup_otlp.cjs, copilot_harness.cjs, dynamic_checkout.cjs) to use maskSecret instead of calling core.setSecret directly.
  • setup.sh: generate_mcp_scripts_config.cjs removed from MCP_SCRIPTS_FILES; git_auth_env.cjs added to SAFE_OUTPUTS_FILES.
  • Test coverage: new actions_secret_masking.test.cjs covering maskSecret fallback and MCP shim placeholder handling; new setup_sh_file_lists.test.cjs guard (filesCallingCoreSetSecret) ensuring MCP script files never call core.setSecret; updated tests for shim.cjs, git_auth_helpers.cjs, git_helpers.cjs, push_repo_memory.cjs, and check_workflow_recompile_needed.cjs.
  • Changeset added: patch-prevent-mcp-secret-masking.md.

Impact

  • Behavioral change: calling setSecret from the MCP shim (outside github-script) now throws instead of no-op/masking, surfacing misuse early.
  • No breaking changes to public APIs; internal refactor of git auth/secret-masking module boundaries.

Commits

  • ecebecd27 fix: prevent secret masking from MCP servers
  • 3999b0ba8 fix: keep Actions secret masking outside MCP shim
  • 02ee8c199 fix: bind Actions setSecret masking call
  • 6d6760709 docs: clarify shim setSecret sentinel
  • c6cf2e272 test: cover shim setSecret fallback masking> Generated by PR Description Updater for Prevent secret masking from MCP servers #50746 · auto · 43.3 AIC · ⊞ 6.8K · ◷

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

Copilot-Session: 95fbf928-8f60-4b41-8aae-175460945b91
@pelikhan
pelikhan marked this pull request as ready for review August 6, 2026 04:16
Copilot AI balanced review requested due to automatic review settings August 6, 2026 04:16
@github-actions

github-actions Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

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

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.

No ADR enforcement needed: PR #50746 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

@github-actions

github-actions Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based 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.

@github-actions

github-actions Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

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.

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

Separates MCP-safe Git authentication from GitHub Actions secret masking and blocks accidental masking in MCP bundles.

Changes:

  • Adds an MCP-safe Git authentication environment builder.
  • Moves Actions-only masking into git_auth_helpers.cjs.
  • Adds deployment guards against direct core.setSecret calls.
Show a summary per file
File Description
.changeset/patch-prevent-mcp-secret-masking.md Records the security fix.
actions/setup/setup.sh Updates MCP deployment file lists.
actions/setup/js/shim.cjs Makes shimmed setSecret throw.
actions/setup/js/shim.test.cjs Tests throwing behavior.
actions/setup/js/setup_sh_file_lists.test.cjs Guards MCP bundles against masking calls.
actions/setup/js/git_auth_env.cjs Adds MCP-safe auth environment construction.
actions/setup/js/git_helpers.cjs Uses the MCP-safe builder.
actions/setup/js/git_helpers.test.cjs Verifies masking is omitted.
actions/setup/js/git_auth_helpers.cjs Adds Actions-only masking wrapper.
actions/setup/js/git_auth_helpers.test.cjs Verifies credential masking.
actions/setup/js/push_repo_memory.cjs Uses the Actions-only wrapper.
actions/setup/js/push_repo_memory.test.cjs Updates import assertions.
actions/setup/js/push_experiment_state.cjs Uses the Actions-only wrapper.
actions/setup/js/check_workflow_recompile_needed.cjs Uses the Actions-only wrapper.
actions/setup/js/check_workflow_recompile_needed.test.cjs Extends the mocked core API.

Review details

Tip

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

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

Comment thread actions/setup/js/shim.cjs
Comment on lines +15 to +16
const setSecret = /** @param {string} _value */ _value => {
throw new Error("core.setSecret is unavailable outside the github-script runtime");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

setSecret should never be called from a node context.

@github-actions github-actions Bot mentioned this pull request Aug 6, 2026
@pelikhan

pelikhan commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

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

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.

The approach is clean and correct. Separating buildGitAuthEnv (MCP-safe, no secret masking) from getGitAuthEnv in git_auth_helpers.cjs (Actions context, calls core.setSecret) is a sound architectural split. The shim now throws on accidental setSecret calls outside the GitHub Actions runtime, the deploy-list test (setup_sh_file_lists.test.cjs) enforces that core.setSecret files are never bundled into MCP/safe-outputs deployments, and the test coverage validates both sides. No blocking issues found.> 🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 43.5 AIC · ⊞ 5.3K

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

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.

Skills-Based Review 🧠

Applied /codebase-design and /tdd — commenting with suggestions, no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Naming ambiguity: Both git_helpers.cjs and git_auth_helpers.cjs export getGitAuthEnv with opposite masking semantics — the wrong import is a silent security regression.
  • Optional parameter encodes a mandatory policy choice: buildGitAuthEnv(token, setSecret?) is callable without masking; making this explicit (two functions or a discriminant option) removes an invisible footgun.
  • Regex guard coverage: filesCallingCoreSetSecret misses bracket-notation calls; worth noting the assumption or extending the pattern.

Positive Highlights

  • ✅ Clean separation of MCP-safe env construction from Actions-only masking — the design intent is clear and well-executed.
  • ✅ The shim now throws instead of silently no-oping; this converts an invisible failure into an immediate, debuggable error.
  • ✅ Automated enforcement test in setup_sh_file_lists.test.cjs is an excellent regression guard.
  • ✅ Callers migrated consistently; no stale imports left behind.
  • ✅ Changeset file included.
> 🧠 *Reviewed using Matt Pocock's skills by [Matt Pocock Skills Reviewer](https://github.com/github/gh-aw/actions/runs/31070819930)* · sonnet46 · 46.4 AIC · ⊞ 7.1K > Comment /matt to run again

return buildGitAuthEnv(authToken);
}

/**

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.

[/codebase-design] Both git_helpers.cjs and git_auth_helpers.cjs export getGitAuthEnv but with opposite secret-masking semantics — the wrong import either leaks credentials silently or crashes in an MCP process. Consider renaming the git_helpers.cjs version to something like buildGitAuthEnvNoMask so the boundary is unmistakable at the call site.

💡 Suggested rename
// git_helpers.cjs - before
function getGitAuthEnv(token) { ... }
module.exports = { getGitAuthEnv };

// git_helpers.cjs - after
function buildGitAuthEnvNoMask(token) { ... }
module.exports = { buildGitAuthEnvNoMask };

The masking version in git_auth_helpers.cjs keeps the getGitAuthEnv name as the canonical Actions entry point.

@copilot please address this.

* @param {(value: string) => void} [setSecret]
* @returns {Object}
*/
function buildGitAuthEnv(authToken, setSecret) {

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.

[/codebase-design] The setSecret parameter is optional ([setSecret]), which keeps the function callable from MCP contexts — but it silently does nothing when omitted, making it easy to call without masking by accident. Since buildGitAuthEnv is always invoked either with masking (Actions path) or without (MCP path), making the intent explicit via two thin wrappers or a mandatory discriminant would remove the ambiguity.

💡 Example: explicit discriminant
// Callers pass false to be explicit about skipping masking:
function buildGitAuthEnv(authToken, { mask = false } = {}) {
  const tokenBase64 = ...;
  if (mask) setSecret(tokenBase64); // setSecret is the required Actions global here
  return { ... };
}

Or keep two exported functions: buildGitAuthEnvMasked and buildGitAuthEnvUnmasked.

@copilot please address this.

}

function filesCallingCoreSetSecret(files) {
return files.filter(file => /\bcore\.setSecret\s*\(/.test(readFileSync(resolve(__dirname, file), "utf8")));

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] The regex only catches core.setSecret( and misses bracket notation (core['setSecret'](...)). A future refactor using bracket notation could silently bypass this MCP safety guard.

Consider adding a second pattern for bracket notation, or at minimum documenting this limitation as a known assumption in the test.

@copilot please address this.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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.

🧪 Test Quality Sentinel Report

✅ Test Quality Score: 86/100 — Excellent

Analyzed 7 test(s) across 6 JS test files: 6 design, 1 implementation, 0 violations.

📊 Metrics (7 tests)
Metric Value
Analyzed 7 (Go: 0, JS: 7)
✅ Design 6 (86%)
⚠️ Implementation 1 (14%)
Edge/error coverage 5 (71%)
Duplicate clusters 0
Inflation No
🚨 Violations 0
Test File Classification Issues
getGitAuthEnv — masks token and base64 auth value git_auth_helpers.test.cjs ✅ design, behavioral_contract, high_value —
getGitAuthEnv — does not call core.setSecret in MCP-safe helper git_helpers.test.cjs ✅ design, behavioral_contract, high_value —
shim — rejects setSecret outside github-script runtime (×2) shim.test.cjs ✅ design, behavioral_contract, high_value —
does not deploy files calling core.setSecret (MCP_SCRIPTS_FILES) setup_sh_file_lists.test.cjs ✅ design, behavioral_contract, high_value —
does not deploy files calling core.setSecret (SAFE_OUTPUTS_FILES) setup_sh_file_lists.test.cjs ✅ design, behavioral_contract, high_value —
import graph check (git_auth_helpers.cjs required) push_repo_memory.test.cjs ⚠️ implementation, implementation_detail, low_value Static string scan of source; no behavioral assertion

Verdict

✅ Passed. 14% implementation tests (threshold: 30%).

This PR introduces strong security-invariant tests. The setup_sh_file_lists.test.cjs deployment guards are particularly valuable — they act as design invariants that prevent any future accidental re-introduction of core.setSecret calls into MCP-deployed files. The shim.test.cjs changes correctly invert the contract (from "masks" to "throws"), enforcing the new security policy at the edge.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 58.7 AIC · ⊞ 7.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: 86/100 — Excellent. 14% implementation tests (threshold: 30%). No violations.

pelikhan and others added 3 commits August 5, 2026 21:45
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan

pelikhan commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

@copilot update setup.sh with the new file

Copilot AI and others added 2 commits August 6, 2026 04:52
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@copilot fix https://github.com/github/gh-aw/actions/runs/31070819894/job/92518508626?pr=50746

Fixed in c6cf2e2. The Actions-side plain Node callers now use an Actions masking helper instead of the shim’s unavailable core.setSecret, and the referenced extra_empty_commit.integration.test.cjs path passes in validation.

@pelikhan
pelikhan merged commit 1755de8 into main Aug 6, 2026
9 of 10 checks passed
@pelikhan
pelikhan deleted the pelikhan/prevent-mcp-core-set-secret branch August 6, 2026 05:04
Copilot stopped work on behalf of pelikhan due to an error August 6, 2026 05:04
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.85.4

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.

3 participants