Skip to content

Commit 71900a8

Browse files
authored
Merge pull request #2206 from apache/fix_storage_path
Fix path traversal when downloading objects to a directory
2 parents 731331a + 7f8bd1c commit 71900a8

3 files changed

Lines changed: 24 additions & 1 deletion

File tree

‎CHANGES.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ Compute
8282
Storage
8383
~~~~~~~
8484

85+
- [Storage] Prevent path traversal when downloading objects to a directory by
86+
using only the object name's basename as the local filename.
87+
(#2206)
88+
[Miguel Caballer - @micafer]
89+
8590
- [Azure Blobs] Fix SAS signatures for percent-encoded object paths by using
8691
the decoded path in the canonicalized resource while preserving the encoded
8792
path in the returned URL.

‎libcloud/storage/base.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ def _save_object(
763763
raise LibcloudError(value="Path %s does not exist" % (destination_path), driver=self)
764764

765765
if not base_name:
766-
file_path = pjoin(destination_path, obj.name)
766+
file_path = pjoin(destination_path, os.path.basename(obj.name))
767767
else:
768768
file_path = destination_path
769769

‎libcloud/test/storage/test_base.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import os
1617
import sys
1718
import errno
1819
import hashlib
20+
import tempfile
1921
from io import BytesIO
2022
from unittest import mock
2123
from unittest.mock import Mock
@@ -53,6 +55,22 @@ def setUp(self):
5355
self.driver1.strict_mode = False
5456
self.driver1.strict_mode = False
5557

58+
def test_save_object_confines_provider_name_to_destination_directory(self):
59+
with tempfile.TemporaryDirectory() as temp_dir:
60+
destination_path = os.path.join(temp_dir, "downloads") + os.sep
61+
os.mkdir(destination_path)
62+
outside_path = os.path.join(temp_dir, "evil")
63+
response = Mock()
64+
response._response.iter_content.return_value = [b"PWNED"]
65+
obj = Mock(size=5)
66+
obj.name = "../../evil"
67+
68+
self.driver1._save_object(response, obj, destination_path)
69+
70+
self.assertFalse(os.path.exists(outside_path))
71+
with open(os.path.join(destination_path, "evil"), "rb") as file_handle:
72+
self.assertEqual(file_handle.read(), b"PWNED")
73+
5674
def test__upload_object_iterator_must_have_next_method(self):
5775
valid_iterators = [BytesIO(b("134")), StringIO("bar")]
5876
invalid_iterators = ["foobar", "", False, True, 1, object()]

0 commit comments

Comments
 (0)