use crate::error::FusekiResult;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct InjectionDetector {
patterns: Vec<String>,
whitelist: Vec<String>,
pub enabled: bool,
}
impl Default for InjectionDetector {
fn default() -> Self {
Self::new()
}
}
impl InjectionDetector {
pub fn new() -> Self {
Self {
patterns: vec![
"DROP".to_string(),
"DELETE WHERE".to_string(),
"INSERT DATA".to_string(),
"CLEAR".to_string(),
"LOAD".to_string(),
],
whitelist: vec![
"SELECT".to_string(),
"CONSTRUCT".to_string(),
"ASK".to_string(),
"DESCRIBE".to_string(),
],
enabled: true,
}
}
pub fn detect_injection(&self, query: &str) -> FusekiResult<bool> {
let upper_query = query.to_uppercase();
for pattern in &self.patterns {
if upper_query.contains(pattern) {
return Ok(true);
}
}
let has_whitelist = self.whitelist.iter().any(|op| upper_query.contains(op));
if !has_whitelist {
return Ok(true); }
Ok(false)
}
pub fn sanitize_query(&self, query: &str) -> FusekiResult<String> {
let mut sanitized = query.to_string();
for pattern in &self.patterns {
sanitized = sanitized.replace(pattern, "");
}
Ok(sanitized)
}
}
#[derive(Debug, Clone)]
pub struct ComplexityAnalyzer {
max_depth: usize,
max_joins: usize,
max_unions: usize,
}
impl Default for ComplexityAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl ComplexityAnalyzer {
pub fn new() -> Self {
Self {
max_depth: 10,
max_joins: 20,
max_unions: 10,
}
}
pub fn analyze_complexity(&self, query: &str) -> FusekiResult<QueryComplexity> {
let depth = self.calculate_nesting_depth(query);
let joins = self.count_joins(query);
let unions = self.count_unions(query);
Ok(QueryComplexity {
nesting_depth: depth,
join_count: joins,
union_count: unions,
is_complex: depth > self.max_depth
|| joins > self.max_joins
|| unions > self.max_unions,
})
}
fn calculate_nesting_depth(&self, query: &str) -> usize {
let mut max_depth: usize = 0;
let mut current_depth: usize = 0;
for ch in query.chars() {
match ch {
'{' => {
current_depth += 1;
max_depth = max_depth.max(current_depth);
}
'}' => {
current_depth = current_depth.saturating_sub(1);
}
_ => {}
}
}
max_depth
}
fn count_joins(&self, query: &str) -> usize {
let upper = query.to_uppercase();
let explicit_joins = upper.matches("JOIN").count();
let triple_patterns = upper.matches(" . ").count() + 1;
explicit_joins + triple_patterns.saturating_sub(1_usize)
}
fn count_unions(&self, query: &str) -> usize {
query.to_uppercase().matches("UNION").count()
}
}
#[derive(Debug, Clone)]
pub struct QueryComplexity {
pub nesting_depth: usize,
pub join_count: usize,
pub union_count: usize,
pub is_complex: bool,
}
#[derive(Debug, Clone)]
pub struct PerformanceOptimizer {
cache: HashMap<String, OptimizedQuery>,
}
impl Default for PerformanceOptimizer {
fn default() -> Self {
Self::new()
}
}
impl PerformanceOptimizer {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
pub fn optimize(&mut self, query: &str) -> FusekiResult<String> {
if let Some(cached) = self.cache.get(query) {
return Ok(cached.optimized_query.clone());
}
let mut optimized = query.to_string();
optimized = self.optimize_filter_placement(&optimized)?;
optimized = self.optimize_join_order(&optimized)?;
optimized = self.optimize_projection(&optimized)?;
self.cache.insert(
query.to_string(),
OptimizedQuery {
original_query: query.to_string(),
optimized_query: optimized.clone(),
optimization_applied: vec![
"filter_placement".to_string(),
"join_order".to_string(),
],
},
);
Ok(optimized)
}
fn optimize_filter_placement(&self, query: &str) -> FusekiResult<String> {
Ok(query.to_string())
}
fn optimize_join_order(&self, query: &str) -> FusekiResult<String> {
Ok(query.to_string())
}
fn optimize_projection(&self, query: &str) -> FusekiResult<String> {
Ok(query.to_string())
}
}
#[derive(Debug, Clone)]
pub struct OptimizedQuery {
pub original_query: String,
pub optimized_query: String,
pub optimization_applied: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_injection_detection() {
let detector = InjectionDetector::new();
let safe_query = "SELECT ?s WHERE { ?s ?p ?o }";
assert!(!detector.detect_injection(safe_query).unwrap());
let dangerous_query = "DROP GRAPH <http://example.org/graph>";
assert!(detector.detect_injection(dangerous_query).unwrap());
}
#[test]
fn test_complexity_analysis() {
let analyzer = ComplexityAnalyzer::new();
let simple_query = "SELECT ?s WHERE { ?s ?p ?o }";
let complexity = analyzer.analyze_complexity(simple_query).unwrap();
assert!(!complexity.is_complex);
let complex_query = "SELECT ?s WHERE { ?s ?p { ?x ?y { ?z ?a ?b } } }";
let complexity = analyzer.analyze_complexity(complex_query).unwrap();
assert!(complexity.nesting_depth > 1);
}
#[test]
fn test_performance_optimization() {
let mut optimizer = PerformanceOptimizer::new();
let query =
"SELECT ?s ?p ?o WHERE { ?s ?p ?o . FILTER(?s = <http://example.org/resource>) }";
let optimized = optimizer.optimize(query).unwrap();
assert!(!optimized.is_empty());
let cached_result = optimizer.optimize(query).unwrap();
assert_eq!(optimized, cached_result);
}
}