use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rule {
pub id: String,
pub name: String,
pub description: String,
pub category: String,
pub severity: String,
pub references: Vec<String>,
}
pub struct RuleRegistry {
rules: Vec<Rule>,
}
impl Default for RuleRegistry {
fn default() -> Self {
Self::new()
}
}
impl RuleRegistry {
pub fn new() -> Self {
let rules = vec![
Rule {
id: "MCP-INJ-001".to_string(),
name: "Command Injection via Tool Arguments".to_string(),
description:
"Detects potential command injection vulnerabilities in tool argument handling. \
Tools that execute shell commands with user-provided input may allow attackers \
to execute arbitrary commands on the server."
.to_string(),
category: "injection".to_string(),
severity: "critical".to_string(),
references: vec!["CWE-78".to_string()],
},
Rule {
id: "MCP-INJ-002".to_string(),
name: "SQL Injection in Database Tools".to_string(),
description: "Detects SQL injection risks in database-related MCP tools. \
User input concatenated into SQL queries can allow data theft or modification."
.to_string(),
category: "injection".to_string(),
severity: "critical".to_string(),
references: vec!["CWE-89".to_string()],
},
Rule {
id: "MCP-INJ-003".to_string(),
name: "Path Traversal in File Operations".to_string(),
description: "Detects path traversal vulnerabilities in file system tools. \
Attackers may use ../ sequences to access files outside intended directories."
.to_string(),
category: "injection".to_string(),
severity: "high".to_string(),
references: vec!["CWE-22".to_string()],
},
Rule {
id: "MCP-INJ-004".to_string(),
name: "SSRF via URL Parameters".to_string(),
description: "Detects server-side request forgery risks in URL handling. \
Attackers may access internal services or cloud metadata endpoints."
.to_string(),
category: "injection".to_string(),
severity: "high".to_string(),
references: vec!["CWE-918".to_string()],
},
Rule {
id: "MCP-AUTH-001".to_string(),
name: "Missing Authentication".to_string(),
description: "Server accepts connections without authentication. \
Remote MCP servers should require authentication to prevent unauthorized access."
.to_string(),
category: "auth".to_string(),
severity: "high".to_string(),
references: vec!["CWE-306".to_string()],
},
Rule {
id: "MCP-AUTH-002".to_string(),
name: "Weak Token Validation".to_string(),
description: "OAuth tokens or API keys not properly validated. \
Weak validation may allow token forgery or replay attacks."
.to_string(),
category: "auth".to_string(),
severity: "high".to_string(),
references: vec!["CWE-287".to_string()],
},
Rule {
id: "MCP-AUTH-003".to_string(),
name: "Credential Exposure in Logs".to_string(),
description: "Credentials or tokens may be logged or exposed in output. \
Sensitive data in logs can be accessed by unauthorized parties."
.to_string(),
category: "auth".to_string(),
severity: "medium".to_string(),
references: vec!["CWE-532".to_string()],
},
Rule {
id: "MCP-TRANS-001".to_string(),
name: "Unencrypted HTTP Transport".to_string(),
description: "Server uses HTTP instead of HTTPS for SSE transport. \
All communications can be intercepted and modified by attackers."
.to_string(),
category: "transport".to_string(),
severity: "high".to_string(),
references: vec!["CWE-319".to_string()],
},
Rule {
id: "MCP-TRANS-002".to_string(),
name: "Missing TLS Certificate Validation".to_string(),
description: "TLS certificates not properly validated. \
Man-in-the-middle attacks possible with invalid certificates."
.to_string(),
category: "transport".to_string(),
severity: "medium".to_string(),
references: vec!["CWE-295".to_string()],
},
Rule {
id: "MCP-PROTO-001".to_string(),
name: "Tool Poisoning Vulnerability".to_string(),
description: "Tool descriptions may contain malicious instructions. \
Prompt injection in tool metadata can manipulate AI behavior."
.to_string(),
category: "protocol".to_string(),
severity: "high".to_string(),
references: vec!["MCP-Security-Advisory-2025-01".to_string()],
},
Rule {
id: "MCP-PROTO-002".to_string(),
name: "Invalid JSON-RPC Response".to_string(),
description: "Server returns malformed JSON-RPC responses. \
Protocol violations may cause client crashes or undefined behavior."
.to_string(),
category: "protocol".to_string(),
severity: "medium".to_string(),
references: vec!["JSON-RPC-2.0-Spec".to_string()],
},
Rule {
id: "MCP-PROTO-003".to_string(),
name: "Missing Error Handling".to_string(),
description: "Server does not properly handle error conditions. \
Poor error handling may expose sensitive information or cause instability."
.to_string(),
category: "protocol".to_string(),
severity: "low".to_string(),
references: vec!["CWE-755".to_string()],
},
Rule {
id: "MCP-DATA-001".to_string(),
name: "Sensitive Data in Tool Output".to_string(),
description: "Tool responses may contain sensitive information. \
PII, credentials, or internal data may be exposed through tool outputs."
.to_string(),
category: "data".to_string(),
severity: "medium".to_string(),
references: vec!["CWE-200".to_string()],
},
Rule {
id: "MCP-DATA-002".to_string(),
name: "Excessive Data Exposure".to_string(),
description: "Tools return more data than necessary. \
Over-fetching data increases attack surface and privacy risks."
.to_string(),
category: "data".to_string(),
severity: "low".to_string(),
references: vec!["CWE-213".to_string()],
},
Rule {
id: "MCP-DOS-001".to_string(),
name: "Unbounded Resource Consumption".to_string(),
description: "No limits on resource consumption in tool execution. \
Attackers may cause denial of service by exhausting server resources."
.to_string(),
category: "dos".to_string(),
severity: "medium".to_string(),
references: vec!["CWE-400".to_string()],
},
Rule {
id: "MCP-DOS-002".to_string(),
name: "Missing Rate Limiting".to_string(),
description: "Server does not implement rate limiting. \
Excessive requests may overwhelm the server or enable brute-force attacks."
.to_string(),
category: "dos".to_string(),
severity: "low".to_string(),
references: vec!["CWE-770".to_string()],
},
Rule {
id: "MCP-SEC-040".to_string(),
name: "Tool Description Injection".to_string(),
description: "Tool descriptions contain suspicious patterns that could manipulate AI behavior. \
Hidden instructions like 'ignore previous instructions' or encoded directives in tool \
descriptions may trick AI assistants into performing unintended actions."
.to_string(),
category: "injection".to_string(),
severity: "critical".to_string(),
references: vec![
"CWE-94".to_string(),
"OWASP-LLM-PI".to_string(),
"MCP-Security-Advisory-2025-02".to_string(),
],
},
Rule {
id: "MCP-SEC-041".to_string(),
name: "Cross-Server Tool Shadowing".to_string(),
description: "Server registers tools with names that shadow commonly-used tools from other servers. \
Tool shadowing can intercept calls intended for legitimate tools, enabling data theft or \
manipulation of AI assistant behavior."
.to_string(),
category: "protocol".to_string(),
severity: "high".to_string(),
references: vec![
"CWE-706".to_string(),
"MCP-Security-Advisory-2025-03".to_string(),
],
},
Rule {
id: "MCP-SEC-042".to_string(),
name: "Rug Pull Detection".to_string(),
description: "Server tool definitions have changed significantly since baseline scan. \
Changes to tool names, descriptions, or schemas after initial trust establishment \
may indicate a supply-chain attack where a previously trusted server becomes malicious."
.to_string(),
category: "protocol".to_string(),
severity: "critical".to_string(),
references: vec![
"CWE-494".to_string(),
"MCP-Security-Advisory-2025-04".to_string(),
],
},
Rule {
id: "MCP-SEC-043".to_string(),
name: "OAuth Scope Abuse".to_string(),
description: "Server requests OAuth scopes that exceed its stated functionality. \
Excessive permission requests may indicate malicious intent or poor security hygiene. \
Tools should request minimum necessary scopes."
.to_string(),
category: "auth".to_string(),
severity: "high".to_string(),
references: vec![
"CWE-250".to_string(),
"CWE-269".to_string(),
"OAuth-Best-Practice".to_string(),
],
},
Rule {
id: "MCP-SEC-044".to_string(),
name: "Unicode Hidden Instructions".to_string(),
description: "Tool descriptions or responses contain hidden Unicode characters. \
Zero-width characters, RTL overrides, or homoglyphs may hide malicious instructions \
that are invisible to users but processed by AI assistants."
.to_string(),
category: "injection".to_string(),
severity: "high".to_string(),
references: vec![
"CWE-116".to_string(),
"Unicode-Security-TR36".to_string(),
],
},
Rule {
id: "MCP-SEC-045".to_string(),
name: "Full-Schema Poisoning".to_string(),
description: "Tool input schema contains suspicious default values or enum constraints. \
Malicious schemas may include default values that execute commands, or restrict \
valid inputs to dangerous values disguised as safe options."
.to_string(),
category: "injection".to_string(),
severity: "high".to_string(),
references: vec![
"CWE-1321".to_string(),
"JSON-Schema-Security".to_string(),
],
},
];
Self { rules }
}
pub fn list_rules(&self, category: Option<&str>) -> Vec<&Rule> {
match category {
Some(cat) => self
.rules
.iter()
.filter(|r| r.category.eq_ignore_ascii_case(cat))
.collect(),
None => self.rules.iter().collect(),
}
}
#[allow(dead_code)]
pub fn get_rule(&self, id: &str) -> Option<&Rule> {
self.rules.iter().find(|r| r.id == id)
}
#[allow(dead_code)]
pub fn categories(&self) -> Vec<&str> {
vec![
"injection",
"auth",
"transport",
"protocol",
"data",
"dos",
"security",
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_has_rules() {
let registry = RuleRegistry::new();
assert!(!registry.list_rules(None).is_empty());
}
#[test]
fn filter_by_category() {
let registry = RuleRegistry::new();
let injection = registry.list_rules(Some("injection"));
assert!(injection.iter().all(|r| r.category == "injection"));
assert!(!injection.is_empty());
}
#[test]
fn get_rule_by_id() {
let registry = RuleRegistry::new();
let rule = registry.get_rule("MCP-INJ-001");
assert!(rule.is_some());
assert_eq!(rule.unwrap().name, "Command Injection via Tool Arguments");
}
#[test]
fn m6_security_rules_exist() {
let registry = RuleRegistry::new();
let rule = registry.get_rule("MCP-SEC-040");
assert!(rule.is_some());
assert_eq!(rule.unwrap().severity, "critical");
let rule = registry.get_rule("MCP-SEC-041");
assert!(rule.is_some());
assert_eq!(rule.unwrap().category, "protocol");
let rule = registry.get_rule("MCP-SEC-042");
assert!(rule.is_some());
assert_eq!(rule.unwrap().severity, "critical");
let rule = registry.get_rule("MCP-SEC-043");
assert!(rule.is_some());
assert_eq!(rule.unwrap().category, "auth");
let rule = registry.get_rule("MCP-SEC-044");
assert!(rule.is_some());
let rule = registry.get_rule("MCP-SEC-045");
assert!(rule.is_some());
}
}