use std::time::Duration;
pub struct SecurityLimits {
pub max_scan_size: usize,
pub max_json_depth: usize,
pub max_pattern_length: usize,
pub max_path_length: usize,
pub max_concurrent_ops: usize,
pub operation_timeout: Duration,
}
impl Default for SecurityLimits {
fn default() -> Self {
Self {
max_scan_size: 10 * 1024 * 1024, max_json_depth: 100,
max_pattern_length: 1000,
max_path_length: 4096,
max_concurrent_ops: 100,
operation_timeout: Duration::from_secs(30),
}
}
}
pub static LIMITS: std::sync::LazyLock<SecurityLimits> =
std::sync::LazyLock::new(SecurityLimits::default);
pub fn check_size_limit(size: usize, limit: usize, name: &str) -> Result<(), String> {
if size > limit {
Err(format!("{name} exceeds maximum size: {size} > {limit}"))
} else {
Ok(())
}
}
pub fn check_json_depth(value: &serde_json::Value, max_depth: usize) -> Result<(), String> {
fn measure_depth(
value: &serde_json::Value,
current: usize,
max: usize,
) -> Result<usize, String> {
if current > max {
return Err(format!("JSON depth exceeds maximum: {current} > {max}"));
}
match value {
serde_json::Value::Object(map) => {
let mut max_child = current;
for v in map.values() {
max_child = max_child.max(measure_depth(v, current + 1, max)?);
}
Ok(max_child)
},
serde_json::Value::Array(arr) => {
let mut max_child = current;
for v in arr {
max_child = max_child.max(measure_depth(v, current + 1, max)?);
}
Ok(max_child)
},
_ => Ok(current),
}
}
measure_depth(value, 0, max_depth).map(|_| ())
}
pub struct ConcurrencyLimiter {
semaphore: tokio::sync::Semaphore,
}
impl ConcurrencyLimiter {
pub fn new(max_concurrent: usize) -> Self {
Self {
semaphore: tokio::sync::Semaphore::new(max_concurrent),
}
}
pub async fn acquire(&self) -> Result<tokio::sync::SemaphorePermit<'_>, String> {
self.semaphore
.acquire()
.await
.map_err(|_| "Failed to acquire concurrency permit".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_size_limits() {
assert!(check_size_limit(100, 1000, "test").is_ok());
assert!(check_size_limit(2000, 1000, "test").is_err());
}
#[test]
fn test_json_depth_check() {
let shallow = serde_json::json!({
"a": 1,
"b": [2, 3],
"c": {"d": 4}
});
assert!(check_json_depth(&shallow, 10).is_ok());
let mut deep = serde_json::json!({});
let mut current = &mut deep;
for i in 0..20 {
*current = serde_json::json!({
format!("level{}", i): {}
});
current = current
.as_object_mut()
.unwrap()
.values_mut()
.next()
.unwrap();
}
assert!(check_json_depth(&deep, 10).is_err());
assert!(check_json_depth(&deep, 25).is_ok());
}
#[tokio::test]
async fn test_concurrency_limiter() {
let limiter = ConcurrencyLimiter::new(2);
let _p1 = limiter.acquire().await.unwrap();
let _p2 = limiter.acquire().await.unwrap();
let result = tokio::time::timeout(Duration::from_millis(100), limiter.acquire()).await;
assert!(result.is_err()); }
}