pub const MAX_LOOP_ITERATIONS: usize = 100_000;
#[allow(dead_code)]
pub const MAX_SEQUENCE_ITEMS: usize = 50_000;
#[allow(dead_code)]
pub const MAX_MAPPING_PAIRS: usize = 50_000;
#[macro_export]
macro_rules! loop_guard_init {
($counter:ident) => {
let mut $counter: usize = 0;
};
}
#[macro_export]
macro_rules! loop_guard_check {
($counter:ident, $max:expr, $context:expr) => {{
$counter += 1;
#[allow(unused_comparisons)]
if $counter >= $max {
return Err(crate::parser::utils::error_builder::limit_error(
$context,
$max,
"loop iterations",
));
}
}};
}
#[macro_export]
macro_rules! collection_size_check {
($collection:expr, $max:expr, $type_name:expr) => {
#[allow(unused_comparisons)]
if $collection.len() >= $max {
return Err(crate::parser::utils::error_builder::limit_error(
$type_name, $max, "items",
));
}
};
}
#[macro_export]
macro_rules! combined_loop_guard {
($counter:ident, $collection:expr, $max_iter:expr, $max_size:expr, $context:expr) => {{
$counter += 1;
#[allow(unused_comparisons)]
if $counter >= $max_iter {
return Err(crate::parser::utils::error_builder::limit_error(
&format!("{} parsing", $context),
$max_iter,
"loop iterations",
));
}
#[allow(unused_comparisons)]
if $collection.len() >= $max_size {
return Err(crate::parser::utils::error_builder::limit_error(
$context, $max_size, "items",
));
}
Ok(()) as Result<(), crate::error::YamlError>
}};
}
#[cfg(test)]
mod tests {
#[test]
fn test_loop_guard_allows_normal_iterations() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
let mut items = Vec::new();
for i in 0..100 {
loop_guard_check!(counter, 1000, "Test");
items.push(i);
}
Ok(())
}
assert!(test_function().is_ok());
}
#[test]
fn test_loop_guard_catches_infinite_loop() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
loop {
loop_guard_check!(counter, 100, "Test");
if counter > 200 {
break; }
}
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("exceeded") || err.contains("loop iterations"),
"Error: {}",
err
);
}
#[test]
fn test_collection_size_check_normal() {
fn test_function() -> Result<(), crate::error::YamlError> {
let items = vec![1, 2, 3, 4, 5];
collection_size_check!(items, 100, "Test collection");
Ok(())
}
assert!(test_function().is_ok());
}
#[test]
fn test_collection_size_check_exceeds_limit() {
fn test_function() -> Result<(), crate::error::YamlError> {
let items = vec![1; 150];
collection_size_check!(items, 100, "Test collection");
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("exceeded") || err.contains("items"),
"Error: {}",
err
);
}
#[test]
fn test_combined_guard_normal() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
let mut items = Vec::new();
for i in 0..50 {
combined_loop_guard!(counter, items, 1000, 100, "Test")?;
items.push(i);
}
Ok(())
}
assert!(test_function().is_ok());
}
#[test]
fn test_combined_guard_catches_iteration_limit() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
let mut items = Vec::new();
for i in 0..150 {
combined_loop_guard!(counter, items, 100, 1000, "Test")?;
items.push(i);
}
Ok(())
}
let result = test_function();
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("exceeded maximum loop iterations")
);
}
#[test]
fn test_combined_guard_catches_size_limit() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
let mut items = Vec::new();
for i in 0..150 {
combined_loop_guard!(counter, items, 1000, 100, "Test")?;
items.push(i);
}
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(
err.contains("exceeded") || err.contains("items"),
"Error: {}",
err
);
}
#[test]
fn test_loop_guard_zero_limit() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
for _ in 0..1 {
loop_guard_check!(counter, 0, "Zero limit");
}
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("exceeded") || err.contains("Zero limit"));
}
#[test]
fn test_collection_size_check_empty_collection() {
fn test_function() -> Result<(), crate::error::YamlError> {
let items: Vec<u32> = Vec::new();
collection_size_check!(items, 0, "Empty collection");
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("exceeded") || err.contains("Empty collection"));
}
#[test]
fn test_loop_guard_custom_context_message() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(counter);
for _ in 0..2 {
loop_guard_check!(counter, 1, "Custom context message");
}
Ok(())
}
let result = test_function();
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("Custom context message"));
}
#[test]
fn test_nested_loop_guards() {
fn test_function() -> Result<(), crate::error::YamlError> {
loop_guard_init!(outer);
for _ in 0..9 {
loop_guard_check!(outer, 10, "Outer loop");
loop_guard_init!(inner);
for _ in 0..4 {
loop_guard_check!(inner, 5, "Inner loop");
}
}
Ok(())
}
assert!(test_function().is_ok());
}
}