fix(lifecycle): allow multiple filter predicates without And wrapper (#7298)

This commit is contained in:
Zhengchao An
2026-09-06 21:09:11 +08:00
committed by GitHub
parent c130d00d4b
commit cc15eae479
+7 -12
View File
@@ -66,8 +66,6 @@ const ERR_LIFECYCLE_EXPIRED_OBJECT_DELETE_MARKER_WITH_TAGS: &str =
const ERR_LIFECYCLE_RULE_MUST_HAVE_ACTION: &str = "Rule must have at least one of Expiration, Transition, NoncurrentVersionExpiration, NoncurrentVersionTransition, or DelMarkerExpiration"; const ERR_LIFECYCLE_RULE_MUST_HAVE_ACTION: &str = "Rule must have at least one of Expiration, Transition, NoncurrentVersionExpiration, NoncurrentVersionTransition, or DelMarkerExpiration";
const ERR_LIFECYCLE_PREFIX_FILTER_CONFLICT: &str = "Legacy Prefix and Filter cannot both be present in a lifecycle rule. Use Filter.Prefix instead of the top-level Prefix element."; const ERR_LIFECYCLE_PREFIX_FILTER_CONFLICT: &str = "Legacy Prefix and Filter cannot both be present in a lifecycle rule. Use Filter.Prefix instead of the top-level Prefix element.";
const ERR_LIFECYCLE_INVALID_NEWER_NONCURRENT_VERSIONS: &str = "'NewerNoncurrentVersions' must be a non-negative integer"; const ERR_LIFECYCLE_INVALID_NEWER_NONCURRENT_VERSIONS: &str = "'NewerNoncurrentVersions' must be a non-negative integer";
const ERR_LIFECYCLE_FILTER_TOO_MANY_PREDICATES: &str =
"Filter must have at most one of Prefix, Tag, ObjectSizeGreaterThan, ObjectSizeLessThan or And; combine predicates with And";
const ERR_LIFECYCLE_FILTER_AND_TOO_FEW_PREDICATES: &str = "Filter And must contain at least two predicates"; const ERR_LIFECYCLE_FILTER_AND_TOO_FEW_PREDICATES: &str = "Filter And must contain at least two predicates";
const ERR_LIFECYCLE_FILTER_DUPLICATE_TAG_KEY: &str = "Filter must not repeat a tag key"; const ERR_LIFECYCLE_FILTER_DUPLICATE_TAG_KEY: &str = "Filter must not repeat a tag key";
const ERR_LIFECYCLE_FILTER_INVALID_TAG: &str = "Tag key must be 1-128 characters and tag value must be at most 256 characters"; const ERR_LIFECYCLE_FILTER_INVALID_TAG: &str = "Tag key must be 1-128 characters and tag value must be at most 256 characters";
@@ -288,14 +286,11 @@ impl RuleValidate for LifecycleRule {
/// `Filter` as "applies to every object in the bucket", and rejecting it would /// `Filter` as "applies to every object in the bucket", and rejecting it would
/// break the most common way to write an unconditional rule. /// break the most common way to write an unconditional rule.
fn validate_lifecycle_filter(filter: &LifecycleRuleFilter) -> Result<(), std::io::Error> { fn validate_lifecycle_filter(filter: &LifecycleRuleFilter) -> Result<(), std::io::Error> {
let top_level_predicates = usize::from(filter.prefix.is_some()) // AWS S3 allows multiple top-level predicates (Prefix, Tag, ObjectSize*)
+ usize::from(filter.tag.is_some()) // as siblings in Filter without an explicit And wrapper — botocore sends
+ usize::from(filter.object_size_greater_than.is_some()) // this layout when a rule combines Prefix with Tag. The evaluation code
+ usize::from(filter.object_size_less_than.is_some()) // already treats sibling predicates as implicit AND, so we validate each
+ usize::from(filter.and.is_some()); // predicate individually rather than enforcing a single-predicate limit.
if top_level_predicates > 1 {
return Err(malformed_xml_error(ERR_LIFECYCLE_FILTER_TOO_MANY_PREDICATES));
}
if let Some(tag) = filter.tag.as_ref() { if let Some(tag) = filter.tag.as_ref() {
validate_lifecycle_tag(tag)?; validate_lifecycle_tag(tag)?;
@@ -4718,7 +4713,7 @@ mod tests {
tag: Some(tag("env", "prod")), tag: Some(tag("env", "prod")),
..Default::default() ..Default::default()
}, },
expected: Some((ERR_LIFECYCLE_FILTER_TOO_MANY_PREDICATES, LIFECYCLE_MALFORMED_XML_ERROR_KIND)), expected: None,
}, },
Case { Case {
name: "prefix alongside And", name: "prefix alongside And",
@@ -4731,7 +4726,7 @@ mod tests {
}), }),
..Default::default() ..Default::default()
}, },
expected: Some((ERR_LIFECYCLE_FILTER_TOO_MANY_PREDICATES, LIFECYCLE_MALFORMED_XML_ERROR_KIND)), expected: None,
}, },
Case { Case {
name: "And with a single member", name: "And with a single member",