This is an automated email from the ASF dual-hosted git repository.
Gargi-jais11 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new f19f2c14afb HDDS-15958. Return InvalidURI for unreadable S3 object
keys on GetObject (#10860).
f19f2c14afb is described below
commit f19f2c14afb5aed59e0960542e9e703179964e92
Author: Gargi Jaiswal <[email protected]>
AuthorDate: Wed Jul 29 10:07:12 2026 +0530
HDDS-15958. Return InvalidURI for unreadable S3 object keys on GetObject
(#10860).
---
.../ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java | 20 ++++++++++++++++++++
.../ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java | 19 +++++++++++++++++++
.../hadoop/ozone/s3/endpoint/EndpointBase.java | 17 +++++++++++++++++
.../hadoop/ozone/s3/endpoint/ObjectEndpoint.java | 1 +
.../hadoop/ozone/s3/endpoint/TestObjectGet.java | 21 +++++++++++++++++++++
5 files changed, 78 insertions(+)
diff --git
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
index 7dc5c8c5ad2..fc634a44f78 100644
---
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
+++
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v1/AbstractS3SDKV1Tests.java
@@ -1368,6 +1368,26 @@ public void testGetObject() throws Exception {
}
}
+ /**
+ * Adapted from ceph s3-tests test_object_read_unreadable.
+ */
+ @Test
+ public void testGetObjectUnreadableKey() {
+ final String bucketName = getBucketName();
+ s3Client.createBucket(bucketName);
+
+ String unreadableKey = new String(new byte[] {(byte) 0xae, (byte) 0x8a,
'-'},
+ StandardCharsets.ISO_8859_1);
+
+ AmazonServiceException ase = assertThrows(AmazonServiceException.class,
+ () -> s3Client.getObject(bucketName, unreadableKey));
+
+ assertEquals(ErrorType.Client, ase.getErrorType());
+ assertEquals(400, ase.getStatusCode());
+ assertEquals(S3ErrorTable.INVALID_URI.getCode(), ase.getErrorCode());
+ assertEquals(S3ErrorTable.INVALID_URI.getErrorMessage(),
ase.getErrorMessage());
+ }
+
static Stream<Arguments> onlyTagKeyCasesV1() {
Map<String, String> fooBarEmptyBar = new HashMap<>();
fooBarEmptyBar.put("foo", "bar");
diff --git
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
index 750e5742156..2fe78577576 100644
---
a/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
+++
b/hadoop-ozone/integration-test-s3/src/test/java/org/apache/hadoop/ozone/s3/awssdk/v2/AbstractS3SDKV2Tests.java
@@ -619,6 +619,25 @@ public void
testGetObjectIfModifiedSinceReturnsNotModified() {
assertEquals(304, exception.statusCode());
}
+ /**
+ * Adapted from ceph s3-tests test_object_read_unreadable.
+ */
+ @Test
+ public void testGetObjectUnreadableKey() {
+ final String bucketName = getBucketName();
+ s3Client.createBucket(b -> b.bucket(bucketName));
+
+ String unreadableKey = new String(new byte[] {(byte) 0xae, (byte) 0x8a,
'-'},
+ StandardCharsets.ISO_8859_1);
+
+ S3Exception exception = assertThrows(S3Exception.class,
+ () -> s3Client.getObjectAsBytes(b ->
b.bucket(bucketName).key(unreadableKey)));
+
+ assertEquals(400, exception.statusCode());
+ assertEquals(S3ErrorTable.INVALID_URI.getCode(),
exception.awsErrorDetails().errorCode());
+ assertEquals(S3ErrorTable.INVALID_URI.getErrorMessage(),
exception.awsErrorDetails().errorMessage());
+ }
+
@Test
public void testHeadObjectIfMatch() {
final String bucketName = getBucketName();
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
index 60ed3a29fa7..3f960cf0e67 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/EndpointBase.java
@@ -33,6 +33,7 @@
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_ARGUMENT;
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_REQUEST;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_TAG;
+import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_URI;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError;
import static org.apache.hadoop.ozone.s3.util.S3Consts.AWS_TAG_PREFIX;
import static
org.apache.hadoop.ozone.s3.util.S3Consts.CUSTOM_METADATA_HEADER_PREFIX;
@@ -649,6 +650,22 @@ protected static boolean isAccessDenied(OMException ex) {
|| result == ResultCodes.INVALID_TOKEN;
}
+ /**
+ * Reject object keys that cannot be represented in a valid URI. AWS S3
returns
+ * InvalidURI for keys containing malformed UTF-8 or ISO control characters.
+ */
+ protected void validateObjectKeyUri(String keyPath) throws OS3Exception {
+ if (keyPath == null || keyPath.indexOf('\uFFFD') >= 0) {
+ throw newError(INVALID_URI, keyPath);
+ }
+
+ for (int i = 0; i < keyPath.length(); i++) {
+ if (Character.isISOControl(keyPath.charAt(i))) {
+ throw newError(INVALID_URI, keyPath);
+ }
+ }
+ }
+
protected ReplicationConfig getReplicationConfig(OzoneBucket ozoneBucket)
throws OS3Exception {
String storageType = getHeaders().getHeaderString(STORAGE_CLASS_HEADER);
String storageConfig =
getHeaders().getHeaderString(CUSTOM_METADATA_HEADER_PREFIX +
STORAGE_CONFIG_HEADER);
diff --git
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
index e1687672eaf..ed9602d0fd0 100644
---
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
+++
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/ObjectEndpoint.java
@@ -364,6 +364,7 @@ public Response get(
) throws IOException, OS3Exception {
ObjectRequestContext context = new ObjectRequestContext(S3GAction.GET_KEY,
bucketName);
try {
+ validateObjectKeyUri(keyPath);
return handler.handleGetRequest(context, keyPath);
} catch (OMException ex) {
throw newError(bucketName, keyPath, ex);
diff --git
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
index e8a8473a815..741cd766739 100644
---
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
+++
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestObjectGet.java
@@ -17,12 +17,15 @@
package org.apache.hadoop.ozone.s3.endpoint;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static
org.apache.hadoop.ozone.s3.S3GatewayConfigKeys.OZONE_S3G_FSO_DIRECTORY_CREATION_ENABLED;
import static
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.assertErrorResponse;
import static
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.assertSucceeds;
import static org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.get;
import static org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.put;
import static
org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_ARGUMENT;
+import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.INVALID_URI;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NO_SUCH_KEY;
import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.PRECOND_FAILED;
import static
org.apache.hadoop.ozone.s3.util.S3Consts.CUSTOM_METADATA_HEADER_PREFIX;
@@ -118,6 +121,24 @@ public void testGetWithNegativePartNumber() throws
Exception {
() -> get(rest, BUCKET_NAME, KEY_NAME));
}
+ @Test
+ public void testGetUnreadableKey() {
+ String unreadableKey = new String(new byte[] {(byte) 0xae, (byte) 0x8a,
'-'}, ISO_8859_1);
+ assertErrorResponse(INVALID_URI, () -> get(rest, BUCKET_NAME,
unreadableKey));
+
+ String malformedUtf8Key = new String(new byte[] {(byte) 0xff}, UTF_8);
+ assertErrorResponse(INVALID_URI, () -> get(rest, BUCKET_NAME,
malformedUtf8Key));
+ }
+
+ @Test
+ public void testGetValidUnicodeKey() throws Exception {
+ String unicodeKey = "café.txt";
+ assertSucceeds(() -> put(rest, BUCKET_NAME, unicodeKey, CONTENT));
+ Response response = get(rest, BUCKET_NAME, unicodeKey);
+ assertEquals(String.valueOf(CONTENT.length()),
+ response.getHeaderString("Content-Length"));
+ }
+
@Test
public void testGet() throws IOException, OS3Exception {
//WHEN
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]