Files
secrets/crates/secrets-mcp/src/validation.rs
agent dd24f7cc44
Some checks failed
Secrets MCP — Build & Release / 检查 / 构建 / 发版 (push) Successful in 6m7s
Secrets MCP — Build & Release / 部署 secrets-mcp (push) Failing after 6s
release: secrets-mcp 0.5.2
Bump version: secrets-mcp-0.5.1 tag already existed while crates had further changes.

Made-with: Cursor
2026-04-05 10:38:50 +08:00

150 lines
5.3 KiB
Rust

/// Validation constants for input field lengths.
pub const MAX_NAME_LENGTH: usize = 256;
pub const MAX_FOLDER_LENGTH: usize = 128;
pub const MAX_ENTRY_TYPE_LENGTH: usize = 64;
pub const MAX_NOTES_LENGTH: usize = 10000;
pub const MAX_TAG_LENGTH: usize = 64;
pub const MAX_TAG_COUNT: usize = 50;
pub const MAX_META_KEY_LENGTH: usize = 128;
pub const MAX_META_VALUE_LENGTH: usize = 4096;
pub const MAX_META_COUNT: usize = 100;
/// Validate input field lengths for MCP tools.
///
/// Returns an error if any field exceeds its maximum length.
pub fn validate_input_lengths(
name: &str,
folder: Option<&str>,
entry_type: Option<&str>,
notes: Option<&str>,
) -> Result<(), rmcp::ErrorData> {
if name.chars().count() > MAX_NAME_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!("name must be at most {} characters", MAX_NAME_LENGTH),
None,
));
}
if let Some(folder) = folder
&& folder.chars().count() > MAX_FOLDER_LENGTH
{
return Err(rmcp::ErrorData::invalid_params(
format!("folder must be at most {} characters", MAX_FOLDER_LENGTH),
None,
));
}
if let Some(entry_type) = entry_type
&& entry_type.chars().count() > MAX_ENTRY_TYPE_LENGTH
{
return Err(rmcp::ErrorData::invalid_params(
format!("type must be at most {} characters", MAX_ENTRY_TYPE_LENGTH),
None,
));
}
if let Some(notes) = notes
&& notes.chars().count() > MAX_NOTES_LENGTH
{
return Err(rmcp::ErrorData::invalid_params(
format!("notes must be at most {} characters", MAX_NOTES_LENGTH),
None,
));
}
Ok(())
}
/// Validate the tags list.
///
/// Checks total count and per-tag character length.
pub fn validate_tags(tags: &[String]) -> Result<(), rmcp::ErrorData> {
if tags.len() > MAX_TAG_COUNT {
return Err(rmcp::ErrorData::invalid_params(
format!("at most {} tags are allowed", MAX_TAG_COUNT),
None,
));
}
for tag in tags {
if tag.chars().count() > MAX_TAG_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!(
"tag '{}' exceeds the maximum length of {} characters",
tag, MAX_TAG_LENGTH
),
None,
));
}
}
Ok(())
}
/// Validate metadata KV strings (key=value / key:=json format).
///
/// Checks total count and per-key/per-value character lengths.
/// This is a best-effort check on the raw KV strings before parsing;
/// keys containing `:` path separators are checked as a whole.
pub fn validate_meta_entries(entries: &[String]) -> Result<(), rmcp::ErrorData> {
if entries.len() > MAX_META_COUNT {
return Err(rmcp::ErrorData::invalid_params(
format!("at most {} metadata entries are allowed", MAX_META_COUNT),
None,
));
}
for entry in entries {
// key:=json — check both key and JSON value length
if let Some((key, value)) = entry.split_once(":=") {
if key.chars().count() > MAX_META_KEY_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!(
"metadata key '{}' exceeds the maximum length of {} characters",
key, MAX_META_KEY_LENGTH
),
None,
));
}
if value.chars().count() > MAX_META_VALUE_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!(
"metadata JSON value for key '{}' exceeds the maximum length of {} characters",
key, MAX_META_VALUE_LENGTH
),
None,
));
}
continue;
}
// key=value or key@path
if let Some((key, value)) = entry.split_once('=') {
if key.chars().count() > MAX_META_KEY_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!(
"metadata key '{}' exceeds the maximum length of {} characters",
key, MAX_META_KEY_LENGTH
),
None,
));
}
if value.chars().count() > MAX_META_VALUE_LENGTH {
return Err(rmcp::ErrorData::invalid_params(
format!(
"metadata value for key '{}' exceeds the maximum length of {} characters",
key, MAX_META_VALUE_LENGTH
),
None,
));
}
} else {
// Fallback: entry without = or := — check total length
let max_total = MAX_META_KEY_LENGTH + MAX_META_VALUE_LENGTH;
if entry.chars().count() > max_total {
let preview = entry.chars().take(50).collect::<String>();
return Err(rmcp::ErrorData::invalid_params(
format!(
"metadata entry '{}' exceeds the maximum length of {} characters",
preview, max_total
),
None,
));
}
}
}
Ok(())
}