mod helpers;
use helpers::extract_bytes_document_blocking;
use xberg::core::config::ExtractionConfig;
use xberg::extractors::security::SecurityLimits;
fn tight_limits() -> SecurityLimits {
SecurityLimits {
max_archive_size: 500 * 1024 * 1024,
max_compression_ratio: 100,
max_files_in_archive: 10_000,
max_nesting_depth: 16,
max_entity_length: 32,
max_content_size: 4 * 1024,
max_iterations: 10_000,
max_xml_depth: 16,
max_table_cells: 64,
}
}
fn config_with_tight_limits() -> ExtractionConfig {
ExtractionConfig {
security_limits: Some(tight_limits()),
..ExtractionConfig::default()
}
}
#[test]
fn xml_depth_bomb_fires_security_error() {
let mut payload = String::from("<root>");
for _ in 0..1000 {
payload.push_str("<a>");
}
payload.push_str("leaf");
for _ in 0..1000 {
payload.push_str("</a>");
}
payload.push_str("</root>");
let cfg = config_with_tight_limits();
let err = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect_err("hostile XML must not extract successfully");
assert!(
matches!(err, xberg::XbergError::Security { .. }),
"expected Security error, got {:?}",
err
);
assert!(
err.to_string().to_lowercase().contains("nesting"),
"expected nesting-too-deep message, got {}",
err
);
}
#[test]
fn xml_oversize_text_fires_security_error() {
let huge_text = "x".repeat(64);
let payload = format!("<root>{}</root>", huge_text);
let cfg = config_with_tight_limits();
let err = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect_err("oversize text must not extract successfully");
assert!(
matches!(err, xberg::XbergError::Security { .. }),
"expected Security error, got {:?}",
err
);
assert!(
err.to_string().to_lowercase().contains("entity"),
"expected entity-too-long message, got {}",
err
);
}
#[test]
fn xml_oversize_attribute_fires_security_error() {
let huge_attr = "v".repeat(128);
let payload = format!("<root attr=\"{}\">ok</root>", huge_attr);
let cfg = config_with_tight_limits();
let err = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect_err("oversize attribute must not extract successfully");
assert!(
matches!(err, xberg::XbergError::Security { .. }),
"expected Security error, got {:?}",
err
);
assert!(
err.to_string().to_lowercase().contains("entity"),
"expected entity-too-long message, got {}",
err
);
}
#[test]
fn xml_string_growth_fires_security_error() {
let mut payload = String::from("<root>");
for _ in 0..256 {
payload.push_str("<n>");
payload.push_str(&"x".repeat(30));
payload.push_str("</n>");
}
payload.push_str("</root>");
let cfg = config_with_tight_limits();
let err = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect_err("oversized cumulative content must not extract successfully");
assert!(
matches!(err, xberg::XbergError::Security { .. }),
"expected Security error, got {:?}",
err
);
assert!(
err.to_string().to_lowercase().contains("content"),
"expected content-too-large message, got {}",
err
);
}
#[test]
fn xml_iteration_bomb_fires_security_error() {
let mut payload = String::from("<root>");
for _ in 0..15_000 {
payload.push_str("<n/>");
}
payload.push_str("</root>");
let cfg = config_with_tight_limits();
let err = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect_err("oversized iteration count must not extract successfully");
assert!(
matches!(err, xberg::XbergError::Security { .. }),
"expected Security error, got {:?}",
err
);
let msg = err.to_string().to_lowercase();
assert!(
msg.contains("iteration") || msg.contains("content"),
"expected iteration-or-content error, got {}",
err
);
}
#[test]
fn xml_benign_input_extracts_successfully() {
let payload = "<root><greeting>hello world</greeting></root>";
let cfg = config_with_tight_limits();
let result = extract_bytes_document_blocking(payload.as_bytes(), "application/xml", &cfg)
.expect("benign XML must extract under the same tight limits");
assert!(
result.content.contains("hello world"),
"expected greeting in content, got: {}",
result.content
);
}