Skip to content

Commit 805a35e

Browse files
authored
mcp: add case senstive tests to the parser (#43463)
<!-- !!!ATTENTION!!! If you are fixing *any* crash or *any* potential security issue, *do not* open a pull request in this repo. Please report the issue via emailing envoy-security@googlegroups.com where the issue will be triaged appropriately. Thank you in advance for helping to keep Envoy secure. !!!ATTENTION!!! For an explanation of how to fill out the fields, please see the relevant section in [PULL_REQUESTS.md](https://github.com/envoyproxy/envoy/blob/main/PULL_REQUESTS.md) !!!ATTENTION!!! Please check the [use of generative AI policy](https://github.com/envoyproxy/envoy/blob/main/CONTRIBUTING.md?plain=1#L41). You may use generative AI only if you fully understand the code. You need to disclose this usage in the PR description to ensure transparency. --> verify that a potential smuggling is not the problem. ``` { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "backend__greet", "Name": "backend__secretTool", // ← different casing "arguments": {"name": "Exploit"} } ``` #39174 Commit Message: Additional Description: Risk Level: Testing: Docs Changes: Release Notes: Platform Specific Features: [Optional Runtime guard:] [Optional Fixes #Issue] [Optional Fixes commit #PR or SHA] [Optional Deprecated:] [Optional [API Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] Signed-off-by: Boteng Yao <boteng@google.com>
1 parent 267b0a6 commit 805a35e

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

test/extensions/filters/http/mcp/mcp_json_parser_test.cc

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,90 @@ TEST(McpParserConfigTest, MethodGroupEmptyGroupFallsBackToBuiltIn) {
14981498
EXPECT_EQ(config.getMethodGroup("tools/call"), "tool");
14991499
}
15001500

1501+
// Case sensitivity tests: verify key confusion attacks (e.g., "name" vs "Name") are prevented.
1502+
// Wrong-cased top-level fields ("Jsonrpc", "Method") should not be recognized.
1503+
TEST_F(McpJsonParserTest, CaseSensitiveTopLevelFieldsAreIgnored) {
1504+
std::string json = R"({
1505+
"Jsonrpc": "2.0",
1506+
"Method": "tools/call",
1507+
"Id": 1,
1508+
"params": {"name": "calculator"}
1509+
})";
1510+
1511+
EXPECT_OK(parser_->parse(json));
1512+
auto finish_status = parser_->finishParse();
1513+
1514+
EXPECT_FALSE(parser_->isValidMcpRequest());
1515+
EXPECT_EQ(parser_->getMethod(), "");
1516+
}
1517+
1518+
// Both "method" and "Method" present — only lowercase "method" should be used.
1519+
TEST_F(McpJsonParserTest, CaseSensitiveMethodFieldOnlyLowercaseRecognized) {
1520+
std::string json = R"({
1521+
"jsonrpc": "2.0",
1522+
"method": "tools/call",
1523+
"Method": "resources/read",
1524+
"params": {"name": "calculator"},
1525+
"id": 1
1526+
})";
1527+
1528+
parseJson(json);
1529+
1530+
EXPECT_TRUE(parser_->isValidMcpRequest());
1531+
EXPECT_EQ(parser_->getMethod(), Methods::TOOLS_CALL);
1532+
}
1533+
1534+
// Both "name" and "Name" in params — only lowercase "name" should be extracted.
1535+
TEST_F(McpJsonParserTest, CaseSensitiveParamsNameKeyConfusionAttack) {
1536+
std::string json = R"({
1537+
"jsonrpc": "2.0",
1538+
"method": "tools/call",
1539+
"params": {
1540+
"name": "allowed_tool",
1541+
"Name": "restricted_secret_tool",
1542+
"arguments": {"input": "data"}
1543+
},
1544+
"id": 3
1545+
})";
1546+
1547+
parseJson(json);
1548+
1549+
EXPECT_TRUE(parser_->isValidMcpRequest());
1550+
EXPECT_EQ(parser_->getMethod(), Methods::TOOLS_CALL);
1551+
1552+
const auto* name_value = parser_->getNestedValue("params.name");
1553+
ASSERT_NE(name_value, nullptr);
1554+
EXPECT_EQ(name_value->string_value(), "allowed_tool");
1555+
1556+
// "params.Name"will not be extracted.
1557+
const auto* upper_name_value = parser_->getNestedValue("params.Name");
1558+
EXPECT_EQ(upper_name_value, nullptr);
1559+
}
1560+
1561+
// "Params" vs "params" — only the correctly-cased "params" object is used.
1562+
TEST_F(McpJsonParserTest, CaseSensitiveParamsObjectKeyConfusion) {
1563+
std::string json = R"({
1564+
"jsonrpc": "2.0",
1565+
"method": "tools/call",
1566+
"Params": {
1567+
"name": "spoofed_tool"
1568+
},
1569+
"params": {
1570+
"name": "real_tool"
1571+
},
1572+
"id": 1
1573+
})";
1574+
1575+
parseJson(json);
1576+
1577+
EXPECT_TRUE(parser_->isValidMcpRequest());
1578+
EXPECT_EQ(parser_->getMethod(), Methods::TOOLS_CALL);
1579+
1580+
const auto* name_value = parser_->getNestedValue("params.name");
1581+
ASSERT_NE(name_value, nullptr);
1582+
EXPECT_EQ(name_value->string_value(), "real_tool");
1583+
}
1584+
15011585
} // namespace
15021586
} // namespace Mcp
15031587
} // namespace HttpFilters

0 commit comments

Comments
 (0)