use rust_yaml::{Limits, LoaderType, Yaml, YamlConfig};
#[test]
fn test_max_depth_limit() {
let mut yaml_str = String::new();
for _ in 0..60 {
yaml_str.push_str("- ");
}
yaml_str.push_str("value");
let config = YamlConfig {
limits: Limits::strict(), loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(&yaml_str);
assert!(result.is_err());
if let Err(e) = result {
let error_str = e.to_string();
assert!(
error_str.contains("depth") || error_str.contains("limit"),
"Expected depth limit error, got: {}",
error_str
);
}
}
#[test]
fn test_max_string_length_limit() {
let long_string = "x".repeat(70_000); let yaml_str = format!("key: \"{}\"", long_string);
let config = YamlConfig {
limits: Limits::strict(),
loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(&yaml_str);
assert!(result.is_err());
if let Err(e) = result {
let error_str = e.to_string();
assert!(
error_str.contains("string")
|| error_str.contains("length")
|| error_str.contains("limit"),
"Expected string length limit error, got: {}",
error_str
);
}
}
#[test]
fn test_max_anchor_limit() {
let mut yaml_str = String::new();
for i in 0..150 {
yaml_str.push_str(&format!("item{}: &anchor{} value{}\n", i, i, i));
}
let config = YamlConfig {
limits: Limits::strict(),
loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(&yaml_str);
assert!(result.is_err());
if let Err(e) = result {
let error_str = e.to_string();
assert!(
error_str.contains("anchor") || error_str.contains("limit"),
"Expected anchor limit error, got: {}",
error_str
);
}
}
#[test]
fn test_max_document_size_limit() {
let large_doc = "x: ".to_string() + &"y".repeat(2_000_000);
let config = YamlConfig {
limits: Limits::strict(),
loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(&large_doc);
assert!(result.is_err());
if let Err(e) = result {
let error_str = e.to_string();
assert!(
error_str.contains("document")
|| error_str.contains("size")
|| error_str.contains("limit"),
"Expected document size limit error, got: {}",
error_str
);
}
}
#[test]
fn test_max_collection_size_limit() {
let mut yaml_str = String::new();
for i in 0..200 {
yaml_str.push_str(&format!("- item{}\n", i));
}
let mut limits = Limits::strict();
limits.max_collection_size = 100;
let config = YamlConfig {
limits,
loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(&yaml_str);
if let Err(e) = result {
let error_str = e.to_string();
assert!(
error_str.contains("collection")
|| error_str.contains("sequence")
|| error_str.contains("limit"),
"Expected collection size limit error, got: {}",
error_str
);
}
}
#[test]
fn test_secure_config() {
let config = YamlConfig::secure();
let yaml = Yaml::with_config(config);
let normal_yaml = "key: value\nlist:\n - item1\n - item2";
let result = yaml.load_str(normal_yaml);
assert!(result.is_ok());
let suspicious_yaml = "x: ".to_string() + &"y".repeat(2_000_000); let result = yaml.load_str(&suspicious_yaml);
assert!(result.is_err());
}
#[test]
fn test_billion_laughs_attack() {
let yaml_bomb = r#"
a: &a ["lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol"]
b: &b [*a, *a, *a, *a, *a, *a, *a, *a, *a]
c: &c [*b, *b, *b, *b, *b, *b, *b, *b, *b]
d: &d [*c, *c, *c, *c, *c, *c, *c, *c, *c]
e: &e [*d, *d, *d, *d, *d, *d, *d, *d, *d]
f: &f [*e, *e, *e, *e, *e, *e, *e, *e, *e]
g: &g [*f, *f, *f, *f, *f, *f, *f, *f, *f]
"#;
let config = YamlConfig {
limits: Limits::strict(),
loader_type: LoaderType::Safe,
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(yaml_bomb);
assert!(result.is_err(), "Should reject billion laughs attack");
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("limit")
|| error_msg.contains("complexity")
|| error_msg.contains("alias")
|| error_msg.contains("collection"),
"Should fail with resource limit error, got: {}",
error_msg
);
}
}
#[test]
fn test_cyclic_alias_detection() {
let yaml_str = r"
a: &a
b: *b
b: &b
a: *a
";
let config = YamlConfig {
limits: Limits::strict(),
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(yaml_str);
assert!(
result.is_err(),
"Should detect and reject cyclic references"
);
}
#[test]
fn test_nested_alias_expansion_limit() {
let yaml_str = r#"
a: &a "base"
b: &b [*a]
c: &c [*b]
d: &d [*c]
e: &e [*d]
f: &f [*e]
g: [*f]
"#;
let config = YamlConfig {
limits: Limits::strict(),
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let result = yaml.load_str(yaml_str);
assert!(result.is_err(), "Should limit alias expansion depth");
}
#[test]
fn test_unlimited_config() {
let config = YamlConfig {
limits: Limits::unlimited(),
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let mut yaml_str = String::new();
for i in 0..1000 {
yaml_str.push_str(&format!("item{}: value{}\n", i, i));
}
let result = yaml.load_str(&yaml_str);
assert!(result.is_ok());
}
#[test]
fn test_permissive_config() {
let config = YamlConfig {
limits: Limits::permissive(),
..YamlConfig::default()
};
let yaml = Yaml::with_config(config);
let mut yaml_str = String::new();
for i in 0..1000 {
yaml_str.push_str(&format!("- item{}\n", i));
}
let result = yaml.load_str(&yaml_str);
let _ = result;
}