use do_memory_core::{
ComplexityLevel, Episode, Evidence, ExecutionResult, ExecutionStep, Heuristic, Pattern,
TaskContext, TaskType,
};
use do_memory_storage_redb::{MAX_EPISODE_SIZE, MAX_HEURISTIC_SIZE, MAX_PATTERN_SIZE, RedbStorage};
use serde_json::json;
use tempfile::TempDir;
async fn create_test_storage() -> anyhow::Result<(RedbStorage, TempDir)> {
let dir = TempDir::new()?;
let db_path = dir.path().join("test_security.redb");
let storage = RedbStorage::new(&db_path).await?;
Ok((storage, dir))
}
fn create_large_valid_episode(target_size_bytes: usize) -> Episode {
let mut episode = Episode::new(
"Large valid episode for size limit testing".to_string(),
TaskContext {
language: Some("rust".to_string()),
framework: Some("tokio".to_string()),
complexity: ComplexityLevel::Complex,
domain: "security-testing".to_string(),
tags: vec!["bincode".to_string(), "serialization".to_string()],
},
TaskType::Testing,
);
let step_overhead = 500;
let observation_size = 400_000; let steps_needed = (target_size_bytes / (observation_size + step_overhead)).max(1);
for i in 0..steps_needed {
let mut step = ExecutionStep::new(
i + 1,
"test_tool".to_string(),
format!("Large step {}", i + 1),
);
step.result = Some(ExecutionResult::Success {
output: "x".repeat(observation_size.min(10_000)), });
step.latency_ms = 100;
step.tokens_used = Some(1000);
step.parameters = json!({
"step": i,
});
episode.add_step(step);
}
episode
}
fn create_oversized_episode() -> Episode {
let mut episode = Episode::new(
"Oversized episode exceeding 10MB limit".to_string(),
TaskContext::default(),
TaskType::Testing,
);
for i in 0..1100 {
let mut step = ExecutionStep::new(
i + 1,
"test_tool".to_string(),
format!("Oversized step {}", i + 1),
);
step.parameters = json!({
"step": i,
});
step.result = Some(ExecutionResult::Success {
output: "x".repeat(10_000), });
episode.add_step(step);
}
episode
}
fn create_large_pattern() -> Pattern {
let large_condition = "x".repeat(400_000); let large_action = "y".repeat(400_000);
Pattern::DecisionPoint {
id: uuid::Uuid::new_v4(),
condition: format!("if {} then", large_condition),
action: format!("do {}", large_action),
context: TaskContext::default(),
outcome_stats: do_memory_core::OutcomeStats {
success_count: 9,
failure_count: 1,
total_count: 10,
avg_duration_secs: 1.5,
},
effectiveness: do_memory_core::PatternEffectiveness::new(),
}
}
fn create_oversized_pattern() -> Pattern {
let oversized_condition = "x".repeat(600_000); let oversized_action = "y".repeat(600_000);
Pattern::DecisionPoint {
id: uuid::Uuid::new_v4(),
condition: format!("if {} then", oversized_condition),
action: format!("do {}", oversized_action),
context: TaskContext::default(),
outcome_stats: do_memory_core::OutcomeStats {
success_count: 9,
failure_count: 1,
total_count: 10,
avg_duration_secs: 1.5,
},
effectiveness: do_memory_core::PatternEffectiveness::new(),
}
}
fn create_large_heuristic() -> Heuristic {
let large_condition = "x".repeat(40_000); let large_action = "y".repeat(40_000);
Heuristic {
heuristic_id: uuid::Uuid::new_v4(),
condition: format!("if {} then", large_condition),
action: format!("do {}", large_action),
confidence: 0.85,
evidence: Evidence {
episode_ids: vec![uuid::Uuid::new_v4()],
success_rate: 0.9,
sample_size: 10,
},
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
}
}
fn create_oversized_heuristic() -> Heuristic {
let oversized_condition = "x".repeat(120_000);
Heuristic {
heuristic_id: uuid::Uuid::new_v4(),
condition: format!("if {} then", oversized_condition),
action: "do something".to_string(),
confidence: 0.85,
evidence: Evidence {
episode_ids: vec![uuid::Uuid::new_v4()],
success_rate: 0.9,
sample_size: 10,
},
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
}
}
#[tokio::test]
async fn test_deserialize_valid_episode_at_max_size() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let large_episode = create_large_valid_episode(9_500_000);
let serialized = postcard::to_allocvec(&large_episode).expect("Failed to serialize");
println!(
"Large valid episode serialized size: {} bytes ({:.2} MB)",
serialized.len(),
serialized.len() as f64 / 1_000_000.0
);
assert!(
serialized.len() < MAX_EPISODE_SIZE as usize,
"Episode should be under MAX_EPISODE_SIZE"
);
println!(
"Postcard size limit validated: {} bytes < {} limit",
serialized.len(),
MAX_EPISODE_SIZE
);
}
#[tokio::test]
async fn test_deserialize_oversized_episode_fails() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let oversized_episode = create_oversized_episode();
let serialized = postcard::to_allocvec(&oversized_episode).expect("Failed to serialize");
println!(
"Oversized episode serialized size: {} bytes ({:.2} MB)",
serialized.len(),
serialized.len() as f64 / 1_000_000.0
);
assert!(
serialized.len() > MAX_EPISODE_SIZE as usize,
"Episode should exceed MAX_EPISODE_SIZE"
);
println!("Size check passed: Episode exceeds limit and would be rejected");
}
#[tokio::test]
async fn test_malicious_oversized_postcard_payload() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let malicious_payload = vec![0xFF; 1024];
let result: Result<Vec<u8>, _> = postcard::from_bytes(&malicious_payload);
assert!(
result.is_err(),
"Malicious payload should fail to deserialize"
);
let error = result.unwrap_err();
println!("Malicious payload error: {:?}", error);
println!("Postcard safely rejected malicious payload");
}
#[tokio::test]
async fn test_pattern_deserialization_at_limit() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let large_pattern = create_large_pattern();
let serialized = postcard::to_allocvec(&large_pattern).expect("Failed to serialize pattern");
println!(
"Large pattern serialized size: {} bytes ({:.2} KB)",
serialized.len(),
serialized.len() as f64 / 1_000.0
);
assert!(
serialized.len() < MAX_PATTERN_SIZE as usize,
"Pattern should be under MAX_PATTERN_SIZE"
);
println!(
"Pattern size validated: {} bytes < {} limit",
serialized.len(),
MAX_PATTERN_SIZE
);
}
#[tokio::test]
async fn test_pattern_exceeding_limit_fails() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let oversized_pattern = create_oversized_pattern();
let serialized = postcard::to_allocvec(&oversized_pattern).expect("Failed to serialize");
println!(
"Oversized pattern serialized size: {} bytes ({:.2} MB)",
serialized.len(),
serialized.len() as f64 / 1_000_000.0
);
assert!(
serialized.len() > MAX_PATTERN_SIZE as usize,
"Pattern should exceed MAX_PATTERN_SIZE"
);
println!(
"Size check passed: Pattern {} bytes exceeds {} byte limit and would be rejected before storage",
serialized.len(),
MAX_PATTERN_SIZE
);
}
#[tokio::test]
async fn test_heuristic_deserialization_at_limit() {
let (storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let large_heuristic = create_large_heuristic();
let serialized =
postcard::to_allocvec(&large_heuristic).expect("Failed to serialize heuristic");
println!(
"Large heuristic serialized size: {} bytes ({:.2} KB)",
serialized.len(),
serialized.len() as f64 / 1_000.0
);
assert!(
serialized.len() < MAX_HEURISTIC_SIZE as usize,
"Heuristic should be under MAX_HEURISTIC_SIZE"
);
let result = storage.store_heuristic(&large_heuristic).await;
assert!(
result.is_ok(),
"Should successfully store large valid heuristic: {:?}",
result.err()
);
let retrieved = storage
.get_heuristic(large_heuristic.heuristic_id)
.await
.expect("Failed to retrieve heuristic");
assert!(
retrieved.is_some(),
"Should successfully retrieve large valid heuristic"
);
}
#[tokio::test]
async fn test_heuristic_exceeding_limit_fails() {
let (_storage, _dir) = create_test_storage()
.await
.expect("Failed to create storage");
let oversized_heuristic = create_oversized_heuristic();
let serialized = postcard::to_allocvec(&oversized_heuristic).expect("Failed to serialize");
println!(
"Oversized heuristic serialized size: {} bytes ({:.2} KB)",
serialized.len(),
serialized.len() as f64 / 1_000.0
);
assert!(
serialized.len() > MAX_HEURISTIC_SIZE as usize,
"Heuristic should exceed MAX_HEURISTIC_SIZE"
);
println!(
"Size check passed: Heuristic {} bytes exceeds {} byte limit and would be rejected before storage",
serialized.len(),
MAX_HEURISTIC_SIZE
);
}
#[test]
fn test_security_constants_are_correct() {
assert_eq!(
MAX_EPISODE_SIZE, 10_000_000,
"MAX_EPISODE_SIZE should be 10MB"
);
assert_eq!(
MAX_PATTERN_SIZE, 1_000_000,
"MAX_PATTERN_SIZE should be 1MB"
);
assert_eq!(
MAX_HEURISTIC_SIZE, 100_000,
"MAX_HEURISTIC_SIZE should be 100KB"
);
const _: () = assert!(
MAX_HEURISTIC_SIZE < MAX_PATTERN_SIZE,
"Heuristic limit should be less than pattern limit"
);
const _: () = assert!(
MAX_PATTERN_SIZE < MAX_EPISODE_SIZE,
"Pattern limit should be less than episode limit"
);
}