Initial scaffold: GyanDoot v2.0 in Rust
Full Cargo workspace with 11 crates: - gyandoot-core: Shared types, config, error types - gyandoot-agent: Agent loop, tool execution, context compression - gyandoot-providers: LLM provider abstraction (OpenAI, Anthropic, Ollama) - gyandoot-memory: MemPalace persistent memory system - gyandoot-platform: Platform adapters (Telegram, Discord, Webhook, CLI) - gyandoot-security: Credential vault, sanitizer, threat detector - gyandoot-kanban: Task decomposition, swarm orchestration, diagnostics - gyandoot-subagents: Sub-agent spawning and orchestration - gyandoot-skills: Skills discovery, loading, preprocessing, bundles - gyandoot-profile: Multi-agent profile manager - gyandoot-gateway: Gateway sessions, routing, access control Plus: CLI entry point with clap, cross-platform installer script, KANBAN.md, .gnpl/AGENTS.md Workspace compiles clean: cargo check passes with 0 errors.
This commit is contained in:
112
crates/gyandoot-security/src/sanitizer.rs
Normal file
112
crates/gyandoot-security/src/sanitizer.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
//! Input sanitizer — prevents injection and dangerous patterns.
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
/// Sanitizes user input before it reaches the agent or LLM.
|
||||
///
|
||||
/// Blocks or escapes shell metacharacters, path traversal attempts,
|
||||
/// and other potentially dangerous input patterns.
|
||||
pub struct Sanitizer {
|
||||
blocked_patterns: Vec<String>,
|
||||
max_length: usize,
|
||||
}
|
||||
|
||||
impl Sanitizer {
|
||||
/// Create a new sanitizer with defaults.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
blocked_patterns: vec![
|
||||
"`rm -rf /".to_string(),
|
||||
"`rm -rf ~".to_string(),
|
||||
"`sudo ".to_string(),
|
||||
"| sh".to_string(),
|
||||
"| bash".to_string(),
|
||||
"| powershell".to_string(),
|
||||
"`curl ".to_string(),
|
||||
"`wget ".to_string(),
|
||||
],
|
||||
max_length: 100_000,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sanitize a string input.
|
||||
///
|
||||
/// Returns the cleaned string. Currently performs length limiting
|
||||
/// and basic pattern blocking. Extensible for more sophisticated
|
||||
/// sanitization in the future.
|
||||
pub fn sanitize(&self, input: &str) -> String {
|
||||
// Truncate excessively long input
|
||||
let mut cleaned = if input.len() > self.max_length {
|
||||
let truncated: String = input.chars().take(self.max_length).collect();
|
||||
debug!("Input truncated from {} to {} chars", input.len(), self.max_length);
|
||||
truncated
|
||||
} else {
|
||||
input.to_string()
|
||||
};
|
||||
|
||||
// Block known dangerous patterns (case-insensitive)
|
||||
for pattern in &self.blocked_patterns {
|
||||
if cleaned.to_lowercase().contains(&pattern.to_lowercase()) {
|
||||
debug!("Blocked dangerous pattern: {}", pattern);
|
||||
cleaned = cleaned.replace(pattern, "[blocked]");
|
||||
}
|
||||
}
|
||||
|
||||
cleaned
|
||||
}
|
||||
|
||||
/// Check if input contains any blocked patterns.
|
||||
pub fn is_dangerous(&self, input: &str) -> bool {
|
||||
let lower = input.to_lowercase();
|
||||
self.blocked_patterns
|
||||
.iter()
|
||||
.any(|p| lower.contains(&p.to_lowercase()))
|
||||
}
|
||||
|
||||
/// Register an additional blocked pattern.
|
||||
pub fn block_pattern(&mut self, pattern: String) {
|
||||
self.blocked_patterns.push(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Sanitizer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_dangerous_pattern() {
|
||||
let sanitizer = Sanitizer::new();
|
||||
let result = sanitizer.sanitize("run `rm -rf /` please");
|
||||
assert!(!result.contains("rm -rf /"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitize_safe_input() {
|
||||
let sanitizer = Sanitizer::new();
|
||||
let result = sanitizer.sanitize("Hello, how are you?");
|
||||
assert_eq!(result, "Hello, how are you?");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_dangerous() {
|
||||
let sanitizer = Sanitizer::new();
|
||||
assert!(sanitizer.is_dangerous("run | bash now"));
|
||||
assert!(!sanitizer.is_dangerous("run a Python script"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_truncate_long_input() {
|
||||
let sanitizer = Sanitizer {
|
||||
max_length: 10,
|
||||
..Sanitizer::new()
|
||||
};
|
||||
let result = sanitizer.sanitize("hello world this is long");
|
||||
assert_eq!(result.len(), 10);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user