refactor: 清理未使用代码并添加未来功能标记
Some checks failed
CI / Rust Check (push) Has been cancelled
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Rust Check (push) Has been cancelled
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
style: 统一代码格式和注释风格 docs: 更新多个功能文档的完整度和状态 feat(runtime): 添加路径验证工具支持 fix(pipeline): 改进条件判断和变量解析逻辑 test(types): 为ID类型添加全面测试用例 chore: 更新依赖项和Cargo.lock文件 perf(mcp): 优化MCP协议传输和错误处理
This commit is contained in:
272
desktop/src-tauri/src/intelligence/validation.rs
Normal file
272
desktop/src-tauri/src/intelligence/validation.rs
Normal file
@@ -0,0 +1,272 @@
|
||||
//! Input validation utilities for the Intelligence Layer
|
||||
//!
|
||||
//! This module provides validation functions for common input types
|
||||
//! to prevent injection attacks, path traversal, and memory exhaustion.
|
||||
//!
|
||||
//! NOTE: Some functions are defined for future use and external API exposure.
|
||||
|
||||
#![allow(dead_code)] // Validation functions reserved for future API endpoints
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Maximum length for identifier strings (agent_id, pipeline_id, skill_id, etc.)
|
||||
pub const MAX_IDENTIFIER_LENGTH: usize = 128;
|
||||
|
||||
/// Minimum length for identifier strings
|
||||
pub const MIN_IDENTIFIER_LENGTH: usize = 1;
|
||||
|
||||
/// Allowed characters in identifiers: alphanumeric, hyphen, underscore
|
||||
const IDENTIFIER_ALLOWED_CHARS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
||||
|
||||
/// Validation error types
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ValidationError {
|
||||
/// Identifier is too long
|
||||
IdentifierTooLong { field: String, max: usize, actual: usize },
|
||||
/// Identifier is too short or empty
|
||||
IdentifierTooShort { field: String, min: usize, actual: usize },
|
||||
/// Identifier contains invalid characters
|
||||
InvalidCharacters { field: String, invalid_chars: String },
|
||||
/// String exceeds maximum length
|
||||
StringTooLong { field: String, max: usize, actual: usize },
|
||||
/// Required field is missing or empty
|
||||
RequiredFieldEmpty { field: String },
|
||||
}
|
||||
|
||||
impl fmt::Display for ValidationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::IdentifierTooLong { field, max, actual } => {
|
||||
write!(f, "Field '{}' is too long: {} characters (max: {})", field, actual, max)
|
||||
}
|
||||
Self::IdentifierTooShort { field, min, actual } => {
|
||||
write!(f, "Field '{}' is too short: {} characters (min: {})", field, actual, min)
|
||||
}
|
||||
Self::InvalidCharacters { field, invalid_chars } => {
|
||||
write!(f, "Field '{}' contains invalid characters: '{}'. Allowed: alphanumeric, '-', '_'", field, invalid_chars)
|
||||
}
|
||||
Self::StringTooLong { field, max, actual } => {
|
||||
write!(f, "Field '{}' is too long: {} characters (max: {})", field, actual, max)
|
||||
}
|
||||
Self::RequiredFieldEmpty { field } => {
|
||||
write!(f, "Required field '{}' is empty", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ValidationError {}
|
||||
|
||||
/// Validate an identifier (agent_id, pipeline_id, skill_id, etc.)
|
||||
///
|
||||
/// # Rules
|
||||
/// - Length: 1-128 characters
|
||||
/// - Characters: alphanumeric, hyphen (-), underscore (_)
|
||||
/// - Cannot start with hyphen or underscore
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// use desktop_lib::intelligence::validation::validate_identifier;
|
||||
///
|
||||
/// assert!(validate_identifier("agent-123", "agent_id").is_ok());
|
||||
/// assert!(validate_identifier("my_skill", "skill_id").is_ok());
|
||||
/// assert!(validate_identifier("", "agent_id").is_err());
|
||||
/// assert!(validate_identifier("invalid@id", "agent_id").is_err());
|
||||
/// ```
|
||||
pub fn validate_identifier(value: &str, field_name: &str) -> Result<(), ValidationError> {
|
||||
let len = value.len();
|
||||
|
||||
// Check minimum length
|
||||
if len < MIN_IDENTIFIER_LENGTH {
|
||||
return Err(ValidationError::IdentifierTooShort {
|
||||
field: field_name.to_string(),
|
||||
min: MIN_IDENTIFIER_LENGTH,
|
||||
actual: len,
|
||||
});
|
||||
}
|
||||
|
||||
// Check maximum length
|
||||
if len > MAX_IDENTIFIER_LENGTH {
|
||||
return Err(ValidationError::IdentifierTooLong {
|
||||
field: field_name.to_string(),
|
||||
max: MAX_IDENTIFIER_LENGTH,
|
||||
actual: len,
|
||||
});
|
||||
}
|
||||
|
||||
// Check for invalid characters
|
||||
let invalid_chars: String = value
|
||||
.chars()
|
||||
.filter(|c| !IDENTIFIER_ALLOWED_CHARS.contains(*c))
|
||||
.collect();
|
||||
|
||||
if !invalid_chars.is_empty() {
|
||||
return Err(ValidationError::InvalidCharacters {
|
||||
field: field_name.to_string(),
|
||||
invalid_chars,
|
||||
});
|
||||
}
|
||||
|
||||
// Cannot start with hyphen or underscore (reserved for system use)
|
||||
if value.starts_with('-') || value.starts_with('_') {
|
||||
return Err(ValidationError::InvalidCharacters {
|
||||
field: field_name.to_string(),
|
||||
invalid_chars: value.chars().next().unwrap().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate a string field with a maximum length
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `value` - The string to validate
|
||||
/// * `field_name` - Name of the field for error messages
|
||||
/// * `max_length` - Maximum allowed length
|
||||
///
|
||||
/// # Examples
|
||||
/// ```ignore
|
||||
/// use desktop_lib::intelligence::validation::validate_string_length;
|
||||
///
|
||||
/// assert!(validate_string_length("hello", "message", 100).is_ok());
|
||||
/// assert!(validate_string_length("", "message", 100).is_err());
|
||||
/// ```
|
||||
pub fn validate_string_length(value: &str, field_name: &str, max_length: usize) -> Result<(), ValidationError> {
|
||||
let len = value.len();
|
||||
|
||||
if len == 0 {
|
||||
return Err(ValidationError::RequiredFieldEmpty {
|
||||
field: field_name.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if len > max_length {
|
||||
return Err(ValidationError::StringTooLong {
|
||||
field: field_name.to_string(),
|
||||
max: max_length,
|
||||
actual: len,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate an optional identifier field
|
||||
///
|
||||
/// Returns Ok if the value is None or if it contains a valid identifier.
|
||||
pub fn validate_optional_identifier(value: Option<&str>, field_name: &str) -> Result<(), ValidationError> {
|
||||
match value {
|
||||
None => Ok(()),
|
||||
Some(v) if v.is_empty() => Ok(()), // Empty string treated as None
|
||||
Some(v) => validate_identifier(v, field_name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a list of identifiers
|
||||
pub fn validate_identifiers<'a, I>(values: I, field_name: &str) -> Result<(), ValidationError>
|
||||
where
|
||||
I: IntoIterator<Item = &'a str>,
|
||||
{
|
||||
for value in values {
|
||||
validate_identifier(value, field_name)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sanitize a string for safe logging (remove control characters, limit length)
|
||||
pub fn sanitize_for_logging(value: &str, max_len: usize) -> String {
|
||||
let sanitized: String = value
|
||||
.chars()
|
||||
.filter(|c| !c.is_control() || *c == '\n' || *c == '\t')
|
||||
.take(max_len)
|
||||
.collect();
|
||||
|
||||
if value.len() > max_len {
|
||||
format!("{}...", sanitized)
|
||||
} else {
|
||||
sanitized
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_valid_identifiers() {
|
||||
assert!(validate_identifier("agent-123", "agent_id").is_ok());
|
||||
assert!(validate_identifier("my_skill", "skill_id").is_ok());
|
||||
assert!(validate_identifier("Pipeline42", "pipeline_id").is_ok());
|
||||
assert!(validate_identifier("a", "test").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_identifiers() {
|
||||
// Too short
|
||||
assert!(matches!(
|
||||
validate_identifier("", "agent_id"),
|
||||
Err(ValidationError::IdentifierTooShort { .. })
|
||||
));
|
||||
|
||||
// Too long
|
||||
let long_id = "a".repeat(200);
|
||||
assert!(matches!(
|
||||
validate_identifier(&long_id, "agent_id"),
|
||||
Err(ValidationError::IdentifierTooLong { .. })
|
||||
));
|
||||
|
||||
// Invalid characters
|
||||
assert!(matches!(
|
||||
validate_identifier("invalid@id", "agent_id"),
|
||||
Err(ValidationError::InvalidCharacters { .. })
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
validate_identifier("invalid id", "agent_id"),
|
||||
Err(ValidationError::InvalidCharacters { .. })
|
||||
));
|
||||
|
||||
// Starts with reserved characters
|
||||
assert!(matches!(
|
||||
validate_identifier("-invalid", "agent_id"),
|
||||
Err(ValidationError::InvalidCharacters { .. })
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
validate_identifier("_invalid", "agent_id"),
|
||||
Err(ValidationError::InvalidCharacters { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_length_validation() {
|
||||
assert!(validate_string_length("hello", "message", 100).is_ok());
|
||||
assert!(matches!(
|
||||
validate_string_length("", "message", 100),
|
||||
Err(ValidationError::RequiredFieldEmpty { .. })
|
||||
));
|
||||
|
||||
let long_string = "a".repeat(200);
|
||||
assert!(matches!(
|
||||
validate_string_length(&long_string, "message", 100),
|
||||
Err(ValidationError::StringTooLong { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optional_identifier() {
|
||||
assert!(validate_optional_identifier(None, "agent_id").is_ok());
|
||||
assert!(validate_optional_identifier(Some(""), "agent_id").is_ok());
|
||||
assert!(validate_optional_identifier(Some("valid-id"), "agent_id").is_ok());
|
||||
assert!(validate_optional_identifier(Some("invalid@id"), "agent_id").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_for_logging() {
|
||||
assert_eq!(sanitize_for_logging("hello", 100), "hello");
|
||||
assert_eq!(sanitize_for_logging("hello\x00world", 100), "helloworld");
|
||||
assert_eq!(sanitize_for_logging("hello\nworld", 100), "hello\nworld");
|
||||
assert_eq!(sanitize_for_logging("hello world", 5), "hello...");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user