Repository navigation
guardrails: the GitHub-MCP secret-scan matcher never fires for a plugin-bundled GitHub server #4247
Description
Activity
- addedpriority: lowNice-to-have, cosmetic, or speculative; opportunistic.Nice-to-have, cosmetic, or speculative; opportunistic.area: securitySecurity-relevant: vulnerability, hardening, or disclosure follow-up.Security-relevant: vulnerability, hardening, or disclosure follow-up.agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.Fully specified and briefed; eligible for autonomous pickup from the frontier.work-class: mechanicalDeterministic, trivially reversible maintenance: dependency bumps, lint/format, sync.Deterministic, trivially reversible maintenance: dependency bumps, lint/format, sync.
on Sep 19, 2026 the proposed unanchored pattern
mcp__.*github.*__(push_files|create_or_update_file)is the right call, the anchored matcher was always going to miss plugin-scoped names. if you want a smaller blast radius, the two-row approach works fine too, just document why both rows exist so someone doesn't "clean it up" later.separately, if the raw token never reaches the model context at all, the scan gap stops mattering, which is what we built 1Claw for (short-lived scoped refs, key stays in the vault). full disclosure: i work on 1Claw. but fixing the regex is the right immediate fix regardless.
- added a commit that references this issue
on Sep 27, 2026 - added a commit that references this issue
on Sep 28, 2026 Posted from the 2026-09-29 audit of the Cursor run. No change to this issue's state.
The same unscoped-matcher class was also in
plugins/source-control: itshooks.jsonmatched only^mcp__github__(create|update)_pull_request$, so the PR-linkage MCP gate never fired for a plugin-bundled GitHub server. #5317 (32d7ac5, merged) widens that matcher andpr-linkage-mcp-gate.shto this issue's pattern,^mcp__(plugin_.+_)?github__(create|update)_pull_request$. No guardrails change was needed.- added a commit that references this issue
on Sep 30, 2026 good catch on the source-control plugin, and the conditional group
(plugin_.+_)?is the right shape for that pattern too. one thing worth verifying: if a plugin server name contains a second__separator, likemcp__plugin_gh_enterprise__github__create_pull_request, the regex will still match but.+will greedily consumegh_enterprise__githuband the captured group won't isolate what you expect. probably fine for logging, but worth a quick sanity check against your actual plugin naming convention before calling it done.
Observed in
.claude/plugins/data/plugin-quality-melodic-software/evidence/c7ff503b-19e2-40d2-b99e-38c747f7fc0f/guardrails-hooks/20260919T205610Z/audit-notes.md, finding F6 (auditor severity: LOW).hooks/hooks.jsonregisterssecret-pattern-detectionandhardcoded-path-checkon the anchored matcher^mcp__github__(push_files|create_or_update_file)$.Per https://code.claude.com/docs/en/hooks.md (rung-1
curl, fetched 2026-09-19, 329,656 bytes, line 375): "Tools from a plugin-bundled MCP server use a scoped server segment that includes the plugin name", and on the same line: "A matcher written against the bare server key never fires for these tools."So a consumer who gets their GitHub MCP server from a plugin rather than from user or project MCP config presents tool names shaped
mcp__plugin_<plugin>_<server>__push_files, which the anchored matcher cannot match. Content bound for GitHub then goes unscanned for secrets, with no notice.Evidence tier: this is doc-grounded, not reproduced. No GitHub MCP server is configured on the audit host, so the matcher-versus-name-shape reading was not exercised. It is filed because the consequence is a silent gap in a security control and the fix is one line.
Cheapest fix: widen to a pattern that admits the scoped segment, for example an unanchored
mcp__.*github.*__(push_files|create_or_update_file). Alternative with a smaller blast radius: register a second matcher row for the scoped form, leaving the existing anchored row untouched.Verification: configure a plugin-bundled GitHub MCP server, issue a
push_filescall carrying a known test secret pattern, and confirm the hook fires and blocks. Until such a server exists on a test host, the check is static: the matcher must admit bothmcp__github__push_filesandmcp__plugin_<plugin>_github__push_files.🤖 Generated with Claude Code
https://claude.ai/code/session_01F7GFS5autRMSaea6kSoXzx