From 212dde14a48f549840a0cfbef444f33b6ae5e357 Mon Sep 17 00:00:00 2001 From: f64116045 Date: Mon, 10 Aug 2026 08:03:19 +0800 Subject: [PATCH 1/2] HDDS-16115. Reject invalid lifecycle rule Status values --- .../s3/endpoint/S3LifecycleConfiguration.java | 8 ++++-- .../TestS3LifecycleConfigurationPut.java | 26 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java index de1de9616a74..1f24c3234779 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java @@ -312,13 +312,17 @@ public OmLifecycleConfiguration toOmLifecycleConfiguration(OzoneBucket ozoneBuck * @return OmLCRule internal rule representation */ private OmLCRule convertToOmRule(Rule rule) throws OMException, OS3Exception { - if (rule.getStatus() == null || rule.getStatus().isEmpty()) { + String status = rule.getStatus(); + if (status == null || status.isEmpty()) { throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML, "The Status element is required in LifecycleConfiguration"); } + if (!"Enabled".equals(status) && !"Disabled".equals(status)) { + throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML); + } OmLCRule.Builder builder = new OmLCRule.Builder() - .setEnabled("Enabled".equals(rule.getStatus())) + .setEnabled("Enabled".equals(status)) .setId(rule.getId()) .setPrefix(rule.getPrefix()); diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java index 818df7f5e976..0033c4fe6cbe 100644 --- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java @@ -56,6 +56,8 @@ import org.apache.hadoop.ozone.s3.util.S3Consts; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; /** @@ -137,6 +139,16 @@ public void testPutLifecycleConfigurationWithoutStatus() throws Exception { } } + @ParameterizedTest + @ValueSource(strings = {"enabled", "disabled", "invalid"}) + public void testPutLifecycleConfigurationWithInvalidStatus(String status) + throws Exception { + OS3Exception ex = assertThrows(OS3Exception.class, + () -> bucketEndpoint.put("bucket1", withStatus(status))); + assertEquals(HTTP_BAD_REQUEST, ex.getHttpCode()); + assertEquals(MALFORMED_XML.getCode(), ex.getCode()); + } + private void testInvalidLifecycleConfiguration(Supplier inputStream, int expectedHttpCode, String expectedErrorCode) throws Exception { try { @@ -171,6 +183,7 @@ public void testPutInvalidExpirationDateLCC() throws Exception { @Test public void testPutValidLifecycleConfiguration() throws Exception { assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", onePrefix()).getStatus()); + assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", withStatus("Disabled")).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", emptyPrefix()).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", oneTag()).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", twoTagsInAndOperator()).getStatus()); @@ -304,6 +317,19 @@ private static InputStream withoutStatus() { return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); } + private static InputStream withStatus(String status) { + String xml = + "" + + "" + + "remove logs after 30 days" + + "prefix/" + + "" + status + "" + + "30" + + "" + + ""; + return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); + } + private static InputStream withoutFilter() { String xml = "" + From 288c96e6107ccb52c646fc267876b5748551ef8e Mon Sep 17 00:00:00 2001 From: f64116045 Date: Tue, 11 Aug 2026 07:35:30 +0800 Subject: [PATCH 2/2] HDDS-16115. Refine lifecycle status validation --- .../ozone/s3/endpoint/S3LifecycleConfiguration.java | 10 +++++++--- .../s3/endpoint/TestS3LifecycleConfigurationPut.java | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java index 1f24c3234779..ec9ada89a9f1 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/S3LifecycleConfiguration.java @@ -25,6 +25,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.ozone.client.OzoneBucket; import org.apache.hadoop.ozone.client.OzoneLifecycleConfiguration; import org.apache.hadoop.ozone.om.exceptions.OMException; @@ -44,6 +45,9 @@ @XmlRootElement(name = "LifecycleConfiguration", namespace = "http://s3.amazonaws.com/doc/2006-03-01/") public class S3LifecycleConfiguration { + private static final String STATUS_ENABLED = "Enabled"; + private static final String STATUS_DISABLED = "Disabled"; + @XmlElement(name = "Rule") private List rules = new ArrayList<>(); @@ -313,16 +317,16 @@ public OmLifecycleConfiguration toOmLifecycleConfiguration(OzoneBucket ozoneBuck */ private OmLCRule convertToOmRule(Rule rule) throws OMException, OS3Exception { String status = rule.getStatus(); - if (status == null || status.isEmpty()) { + if (StringUtils.isEmpty(status)) { throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML, "The Status element is required in LifecycleConfiguration"); } - if (!"Enabled".equals(status) && !"Disabled".equals(status)) { + if (!STATUS_ENABLED.equals(status) && !STATUS_DISABLED.equals(status)) { throw S3ErrorTable.newError(S3ErrorTable.MALFORMED_XML); } OmLCRule.Builder builder = new OmLCRule.Builder() - .setEnabled("Enabled".equals(status)) + .setEnabled(STATUS_ENABLED.equals(status)) .setId(rule.getId()) .setPrefix(rule.getPrefix()); diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java index 0033c4fe6cbe..d76ff51b66bb 100644 --- a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestS3LifecycleConfigurationPut.java @@ -182,7 +182,7 @@ public void testPutInvalidExpirationDateLCC() throws Exception { @Test public void testPutValidLifecycleConfiguration() throws Exception { - assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", onePrefix()).getStatus()); + assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", withStatus("Enabled")).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", withStatus("Disabled")).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", emptyPrefix()).getStatus()); assertEquals(HTTP_OK, bucketEndpoint.put("bucket1", oneTag()).getStatus());