Skip to content

Commit 7943433

Browse files
committed
Validate that keys containing path segments work
#2788
1 parent 14379a3 commit 7943433

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/GetPutDeleteObjectIT.kt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,46 @@ internal class GetPutDeleteObjectIT : S3TestBase() {
9797
}.isInstanceOf(NoSuchKeyException::class.java)
9898
}
9999

100+
@Test
101+
@S3VerifiedSuccess(year = 2025)
102+
fun testPutGetHeadDeleteObject_pathSegments(testInfo: TestInfo) {
103+
val key = "/.././$UPLOAD_FILE_NAME"
104+
val bucketName = givenBucket(testInfo)
105+
106+
s3Client.putObject(
107+
{
108+
it.bucket(bucketName)
109+
it.key(key)
110+
},
111+
RequestBody.fromFile(UPLOAD_FILE),
112+
)
113+
114+
s3Client.headObject {
115+
it.bucket(bucketName)
116+
it.key(key)
117+
}
118+
119+
s3Client
120+
.getObject {
121+
it.bucket(bucketName)
122+
it.key(key)
123+
}.use {
124+
assertThat(it.response().contentLength()).isEqualTo(UPLOAD_FILE_LENGTH)
125+
}
126+
127+
s3Client.deleteObject {
128+
it.bucket(bucketName)
129+
it.key(key)
130+
}
131+
132+
assertThatThrownBy {
133+
s3Client.getObject {
134+
it.bucket(bucketName)
135+
it.key(key)
136+
}
137+
}.isInstanceOf(NoSuchKeyException::class.java)
138+
}
139+
100140
@Test
101141
@S3VerifiedSuccess(year = 2025)
102142
fun testPutGetHeadDeleteObjects(testInfo: TestInfo) {

server/src/test/kotlin/com/adobe/testing/s3mock/ObjectControllerTest.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,59 @@ internal class ObjectControllerTest : BaseControllerTest() {
146146
}
147147

148148

149+
@Test
150+
@Throws(Exception::class)
151+
fun testPutObject_withPathsegments_Ok() {
152+
givenBucket()
153+
val key = ".././sampleFile.txt"
154+
155+
val testFile = File(UPLOAD_FILE_NAME)
156+
val digest = DigestUtil.hexDigest(Files.newInputStream(testFile.toPath()))
157+
val tempFile = Files.createTempFile("testPutObject_Ok", "").also {
158+
testFile.copyTo(it.toFile(), overwrite = true)
159+
}
160+
whenever(
161+
objectService.toTempFile(
162+
any(
163+
InputStream::class.java
164+
), any(HttpHeaders::class.java)
165+
)
166+
)
167+
.thenReturn(
168+
FileChecksum(
169+
tempFile,
170+
DigestUtil.checksumFor(testFile.toPath(), DefaultChecksumAlgorithm.CRC32)
171+
)
172+
)
173+
174+
whenever(
175+
objectService.putS3Object(
176+
eq(TEST_BUCKET_NAME),
177+
eq(key),
178+
contains(MediaType.TEXT_PLAIN_VALUE),
179+
anyMap(),
180+
any(Path::class.java),
181+
anyMap(),
182+
anyMap(),
183+
isNull(),
184+
isNull(),
185+
isNull(),
186+
eq(Owner.DEFAULT_OWNER),
187+
eq(StorageClass.STANDARD)
188+
)
189+
).thenReturn(s3ObjectMetadata(key, digest))
190+
191+
mockMvc.perform(
192+
put("/test-bucket/$key")
193+
.content(testFile.readBytes())
194+
.contentType(MediaType.TEXT_PLAIN)
195+
.accept(MediaType.APPLICATION_XML)
196+
)
197+
.andExpect(status().isOk)
198+
.andExpect(header().string(HttpHeaders.ETAG, "\"$digest\""))
199+
}
200+
201+
149202
@Test
150203
@Throws(Exception::class)
151204
fun testPutObject_Options() {

0 commit comments

Comments
 (0)