From aebc9f801438746ac4ade327551576cb75f635f2 Mon Sep 17 00:00:00 2001 From: Sean O'Brien <60306702+stobrien89@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:30:39 -0500 Subject: [PATCH] bugfix: dot segment handling (#2835) --- .changes/nextrelease/s3-dot-segment.json | 7 ++++ src/Api/Serializer/RestSerializer.php | 11 ++++- tests/S3/S3ClientTest.php | 53 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 .changes/nextrelease/s3-dot-segment.json diff --git a/.changes/nextrelease/s3-dot-segment.json b/.changes/nextrelease/s3-dot-segment.json new file mode 100644 index 0000000000..afe6799b55 --- /dev/null +++ b/.changes/nextrelease/s3-dot-segment.json @@ -0,0 +1,7 @@ +[ + { + "type": "bugfix", + "category": "s3", + "description": "Disables transformation of request URI paths with dot segments" + } +] diff --git a/src/Api/Serializer/RestSerializer.php b/src/Api/Serializer/RestSerializer.php index dbd925ad15..db431e5e72 100644 --- a/src/Api/Serializer/RestSerializer.php +++ b/src/Api/Serializer/RestSerializer.php @@ -242,14 +242,21 @@ function (array $matches) use ($varDefinitions) { $path = rtrim($path, '/'); } $relative = $path . $relative; + + if (strpos($relative, '../') !== false) { + if ($relative[0] !== '/') { + $relative = '/' . $relative; + } + return new Uri($this->endpoint . $relative); + } } // If endpoint has path, remove leading '/' to preserve URI resolution. if ($path && $relative[0] === '/') { $relative = substr($relative, 1); } - //Append path to endpoint when leading '//...' present - // as uri cannot be properly resolved + //Append path to endpoint when leading '//...' + // present as uri cannot be properly resolved if ($this->api->isModifiedModel() && strpos($relative, '//') === 0 ) { diff --git a/tests/S3/S3ClientTest.php b/tests/S3/S3ClientTest.php index 9e6eabf8b5..4bd1f35748 100644 --- a/tests/S3/S3ClientTest.php +++ b/tests/S3/S3ClientTest.php @@ -2321,4 +2321,57 @@ public function testDoesNotComputeMD5($options, $operation) ); $s3->execute($command); } + + /** + * @dataProvider dotSegmentProvider + */ + public function testHandlesDotSegmentsInKey($key, $expectedUri) + { + $s3 = $this->getTestClient('s3'); + $this->addMockResults($s3, [[]]); + $command = $s3->getCommand('getObject', ['Bucket' => 'foo', 'Key' => $key]); + $command->getHandlerList()->appendSign( + Middleware::tap(function ($cmd, $req) use ($expectedUri) { + $this->assertSame($expectedUri, (string) $req->getUri()); + }) + ); + $s3->execute($command); + } + + public function dotSegmentProvider() + { + return [ + ['../foo' , 'https://foo.s3.amazonaws.com/../foo'], + ['bar/../../foo', 'https://foo.s3.amazonaws.com/bar/../../foo'], + ['/../foo', 'https://foo.s3.amazonaws.com//../foo'], + ['foo/bar/../baz', 'https://foo.s3.amazonaws.com/foo/bar/../baz'] + ]; + } + + /** + * @dataProvider dotSegmentPathStyleProvider + */ + public function testHandlesDotSegmentsInKeyWithPathStyle($key, $expectedUri) + { + $s3 = $this->getTestClient('s3', ['use_path_style_endpoint' => true]); + $this->addMockResults($s3, [[]]); + $command = $s3->getCommand('getObject', ['Bucket' => 'foo', 'Key' => $key]); + $command->getHandlerList()->appendSign( + Middleware::tap(function ($cmd, $req) use ($expectedUri) { + $this->assertSame($expectedUri, (string) $req->getUri()); + }) + ); + $s3->execute($command); + } + + public function dotSegmentPathStyleProvider() + { + return [ + ['../foo' , 'https://s3.amazonaws.com/foo/foo/../foo'], + ['bar/../../foo', 'https://s3.amazonaws.com/foo/foo/bar/../../foo'], + ['/../foo', 'https://s3.amazonaws.com/foo/foo//../foo'], + ['foo/bar/../baz', 'https://s3.amazonaws.com/foo/foo/foo/bar/../baz'], + ]; + } + }