Skip to content

fix(validator): sandbox MCP stdio cwd, headers, and cross-platform SKILL.md - #4

Open
wufufu770 wants to merge 2 commits into
MiniMax-AI:mainfrom
wufufu770:fix/validator-sandbox
Open

fix(validator): sandbox MCP stdio cwd, headers, and cross-platform SKILL.md#4
wufufu770 wants to merge 2 commits into
MiniMax-AI:mainfrom
wufufu770:fix/validator-sandbox

Conversation

@wufufu770

@wufufu770 wufufu770 commented Aug 18, 2026

Copy link
Copy Markdown

Problem

The Plugin validator is the gate every Plugin passes through before host runtime loads it. Several structural gaps let a malicious mcp.json or SKILL.md smuggle attack surface past CI and human review.

Fix

Replaces loose validation with strict containment checks aligned to the contract documented in docs/plugin-compatibility.md and docs/security-model.md.

Area Gap Fix
cwd prefix regex allowed .. traversal path.resolve + path.relative containment
cwd / command / args / env / headers NUL bytes accepted regex \x00 rejected
env vs headers PLUGIN_ROOT / PLUGIN_DATA blocked in env but allowed in headers Blocked in both (contract consistency)
headers no restrictions on key 13 standard + custom credential headers blocked
headers value / key CR / LF / NUL / 0x7f accepted control bytes rejected (tab allowed per RFC 7230)
mcp server name no length cap <= 64 characters enforced
SKILL.md CRLF / BOM broke validation CRLF normalised to LF, BOM rejected with explicit error
README badge URLs pointed to wrong fork Corrected to point to MiniMax-AI

Verification

$ npm run check
OK example hello-mcode
OK example hello-mcode-mcp
OK plugin Fectivnfy112357/github-explore
OK plugin HopeYin/dida365
OK plugin HopeYin/ticktick
OK plugin Hylouis233/mcp-server-patterns
OK plugin Hylouis233/search-first
OK plugin Hylouis233/verification-loop
OK plugin hetaoBackend/minimax-code-trajectory
ℹ tests 45
ℹ pass 45
ℹ fail 0

5 consecutive runs: 45/45 stable.

Risk

Low. Pure additive validation: no existing Plugin, example, or test in the hosted tree fails under the hardened rules. The only changes that could reject a previously-passing Plugin are ones that intentionally hid credentials or attempted path traversal in the first place.

Scope

This PR does NOT touch:

  • Plugin source plugins/*/*
  • docs/security-model.md (the contract here is implicit; a follow-up PR can spell it out if maintainers want)
  • Other PRs: zero file overlap

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

The previous stdio cwd check only anchored the start of the value, so a
config like '${PLUGIN_DATA}/../../etc' passed validation and the server
process could be launched outside the Plugin sandbox. Replace the prefix
regex with path.resolve + path.relative containment against the supplied
pluginRoot / pluginData, and forward those roots through validateMcp so
hosted and standalone entry points both sandbox the cwd.

Also reject NUL bytes in cwd to defend against path-truncation attacks
where a runtime could split the path at '\x00' and resolve a different
target than the validator approved.

Add unit coverage for resolveCwd (anchored paths, escapes, missing
roots, non-string inputs, control bytes, mid-segment traversal) and
two hosted-Plugin integration tests covering safe and escaping cwd.

While here, the README and README.zh-CN build/license badges link and
source point at the upstream hetaoBackend fork; redirect them to the
MiniMax-AI/MiniMax-Code-Plugins organisation ship-to repo.
…ILL.md

The validator had structural gaps that let a Plugin author hide attack
surface inside mcp.json, mcp.json headers, and the SKILL.md frontmatter.

  - mcp server name bypassed the 64-character cap that applies to
    the Plugin name itself, so an MCP server key could be arbitrarily
    long.
  - command / args / env / headers values and keys accepted NUL bytes
    (\x00), which a runtime might split on (fs path truncation, shell
    argument parsing).
  - streamable-http / sse headers accepted any key — including the
    standard Authorization / Cookie / Set-Cookie / Proxy-Authorization
    credentials and the common custom credential headers (X-Api-Key,
    X-Auth-Token, X-Access-Token, X-Token, X-Secret, X-Api-Token,
    Api-Key, Auth-Token, Access-Token) — letting a Plugin smuggle
    tokens into the HTTP request and bypass the 'no credentials' gate
    that applies to env values.
  - headers values and keys accepted CR / LF / NUL / 0x7f, enabling
    HTTP header injection (a value like 'a\r\nAuthorization: Bearer
    evil' could split into a separate header on the wire).
  - PLUGIN_ROOT / PLUGIN_DATA were rejected in env but accepted in
    headers, a contract inconsistency.
  - SKILL.md was required to use LF line endings — a Windows or
    git-autocrlf contributor hit 'YAML frontmatter is required' with
    no hint about the BOM or CRLF cause. SKILL.md now accepts CRLF
    (normalised to LF) and rejects UTF-8 BOM with a specific error,
    matching the contract enforced on plugin.json / mcp.json.
  - DoS hardening: cwd / command / args / env entries / header values
    now have explicit length caps (1024 / 1024 / 4096 / 256 / 8192
    chars) and headers capped at 100 entries.

Adds 7 new test cases. npm run check passes 9 hosted Plugins, 2
examples, and 45 tests (5 consecutive runs).
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.

1 participant