use gossan_cloud::common::is_xml_listing;
#[test]
fn real_bucket_listings_are_detected() {
let s3 = r#"<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>b</Name><Contents><Key>secret.txt</Key></Contents></ListBucketResult>"#;
let gcs = r#"<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://doc.s3.amazonaws.com/2006-03-01"><Name>g</Name><Contents><Key>a</Key></Contents></ListBucketResult>"#;
let spaces = r#"<?xml version="1.0"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>sp</Name></ListBucketResult>"#;
for (name, body) in [("s3", s3), ("gcs", gcs), ("spaces", spaces)] {
assert!(
is_xml_listing(body),
"{name}: a real ListBucketResult body must be detected as a listing"
);
}
assert!(
is_xml_listing(
r#"<?xml version="1.0"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>empty</Name><KeyCount>0</KeyCount></ListBucketResult>"#
),
"an empty but publicly-listable bucket is still an exposure"
);
assert!(
is_xml_listing(
r#"<?xml version="1.0"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Contents><Key>f"#
),
"a truncated listing whose root survived must still be detected"
);
}
#[test]
fn non_listing_bodies_with_contents_substring_are_not_listings() {
let cases = [
("access-denied", r#"<?xml version="1.0"?><Error><Code>AccessDenied</Code><Message>Access Denied</Message></Error>"#),
("no-such-bucket", r#"<?xml version="1.0"?><Error><Code>NoSuchBucket</Code></Error>"#),
("unrelated-xml-api", r#"<?xml version="1.0"?><Response><Status>ok</Status><Contents>some app data</Contents></Response>"#),
("html-doc", r#"<html><body><pre><Contents><Key>...</Key></Contents></pre><Contents>nav</Contents></body></html>"#),
("feed", r#"<feed><entry><Contents>post body</Contents></entry></feed>"#),
("json", r#"{"Contents": [{"Key": "a"}], "Name": "b"}"#),
("empty", ""),
("plain-404", "404 Not Found"),
];
for (name, body) in cases {
assert!(
!is_xml_listing(body),
"{name}: a non-ListBucketResult body must NOT be classified a \
public bucket listing (false Critical), body={body:?}"
);
}
}
#[test]
fn marker_injection_into_non_listing_does_not_trigger() {
let injected = r#"<?xml version="1.0"?><Error><Code>AccessDenied</Code><Message><Contents><Key>haha</Key></Contents></Message></Error>"#;
assert!(
!is_xml_listing(injected),
"a <Contents> smuggled inside an AccessDenied error is still a \
denied (secure) bucket, not an exposure"
);
let real_with_noise = r#"garbage <Contents> not really <?xml?> <ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Contents><Key>k</Key></Contents></ListBucketResult>"#;
assert!(
is_xml_listing(real_with_noise),
"a genuine ListBucketResult must still be detected amid noise"
);
}