Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-s3transfer for 
openSUSE:Factory checked in at 2025-09-18 21:07:58
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-s3transfer (Old)
 and      /work/SRC/openSUSE:Factory/.python-s3transfer.new.27445 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-s3transfer"

Thu Sep 18 21:07:58 2025 rev:40 rq:1305266 version:0.14.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-s3transfer/python-s3transfer.changes      
2025-07-30 11:45:43.383582429 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-s3transfer.new.27445/python-s3transfer.changes
   2025-09-18 21:08:01.788778227 +0200
@@ -1,0 +2,7 @@
+Tue Sep 16 11:53:51 UTC 2025 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 0.14.0
+  * feature:download: Validate requested range matches content
+    range in response for multipart downloads
+
+-------------------------------------------------------------------

Old:
----
  s3transfer-0.13.1.tar.gz

New:
----
  s3transfer-0.14.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-s3transfer.spec ++++++
--- /var/tmp/diff_new_pack.uaFiwG/_old  2025-09-18 21:08:02.284798965 +0200
+++ /var/tmp/diff_new_pack.uaFiwG/_new  2025-09-18 21:08:02.288799132 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-s3transfer
-Version:        0.13.1
+Version:        0.14.0
 Release:        0
 Summary:        Python S3 transfer manager
 License:        Apache-2.0

++++++ s3transfer-0.13.1.tar.gz -> s3transfer-0.14.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/PKG-INFO 
new/s3transfer-0.14.0/PKG-INFO
--- old/s3transfer-0.13.1/PKG-INFO      2025-07-18 20:11:17.464557200 +0200
+++ new/s3transfer-0.14.0/PKG-INFO      2025-09-09 20:09:31.783774000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: s3transfer
-Version: 0.13.1
+Version: 0.14.0
 Summary: An Amazon S3 Transfer Manager
 Home-page: https://github.com/boto/s3transfer
 Author: Amazon Web Services
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/s3transfer/__init__.py 
new/s3transfer-0.14.0/s3transfer/__init__.py
--- old/s3transfer-0.13.1/s3transfer/__init__.py        2025-07-18 
20:11:17.000000000 +0200
+++ new/s3transfer-0.14.0/s3transfer/__init__.py        2025-09-09 
20:09:31.000000000 +0200
@@ -146,7 +146,7 @@
 from s3transfer.exceptions import RetriesExceededError, S3UploadFailedError
 
 __author__ = 'Amazon Web Services'
-__version__ = '0.13.1'
+__version__ = '0.14.0'
 
 
 logger = logging.getLogger(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/s3transfer/download.py 
new/s3transfer-0.14.0/s3transfer/download.py
--- old/s3transfer-0.13.1/s3transfer/download.py        2025-07-18 
20:07:26.000000000 +0200
+++ new/s3transfer-0.14.0/s3transfer/download.py        2025-09-09 
20:09:31.000000000 +0200
@@ -17,7 +17,11 @@
 from botocore.exceptions import ClientError
 
 from s3transfer.compat import seekable
-from s3transfer.exceptions import RetriesExceededError, S3DownloadFailedError
+from s3transfer.exceptions import (
+    RetriesExceededError,
+    S3DownloadFailedError,
+    S3ValidationError,
+)
 from s3transfer.futures import IN_MEMORY_DOWNLOAD_TAG
 from s3transfer.tasks import SubmissionTask, Task
 from s3transfer.utils import (
@@ -578,6 +582,10 @@
                 response = client.get_object(
                     Bucket=bucket, Key=key, **extra_args
                 )
+                self._validate_content_range(
+                    extra_args.get('Range'),
+                    response.get('ContentRange'),
+                )
                 streaming_body = StreamReaderProgress(
                     response['Body'], callbacks
                 )
@@ -635,6 +643,27 @@
     def _handle_io(self, download_output_manager, fileobj, chunk, index):
         download_output_manager.queue_file_io_task(fileobj, chunk, index)
 
+    def _validate_content_range(self, requested_range, content_range):
+        if not requested_range or not content_range:
+            return
+        # Unparsed `ContentRange` looks like `bytes 0-8388607/39542919`,
+        # where `0-8388607` is the fetched range and `39542919` is
+        # the total object size.
+        response_range, total_size = content_range.split('/')
+        # Subtract `1` because range is 0-indexed.
+        final_byte = str(int(total_size) - 1)
+        # If it's the last part, the requested range will not include
+        # the final byte, eg `bytes=33554432-`.
+        if requested_range.endswith('-'):
+            requested_range += final_byte
+        # Request looks like `bytes=0-8388607`.
+        # Parsed response looks like `bytes 0-8388607`.
+        if requested_range[6:] != response_range[6:]:
+            raise S3ValidationError(
+                f"Requested range: `{requested_range[6:]}` does not match "
+                f"content range in response: `{response_range[6:]}`"
+            )
+
 
 class ImmediatelyWriteIOGetObjectTask(GetObjectTask):
     """GetObjectTask that immediately writes to the provided file object
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/s3transfer/exceptions.py 
new/s3transfer-0.14.0/s3transfer/exceptions.py
--- old/s3transfer-0.13.1/s3transfer/exceptions.py      2025-07-18 
20:07:26.000000000 +0200
+++ new/s3transfer-0.14.0/s3transfer/exceptions.py      2025-09-09 
20:09:31.000000000 +0200
@@ -39,3 +39,7 @@
     """A CancelledError raised from an error in the TransferManager"""
 
     pass
+
+
+class S3ValidationError(Exception):
+    pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/s3transfer.egg-info/PKG-INFO 
new/s3transfer-0.14.0/s3transfer.egg-info/PKG-INFO
--- old/s3transfer-0.13.1/s3transfer.egg-info/PKG-INFO  2025-07-18 
20:11:17.000000000 +0200
+++ new/s3transfer-0.14.0/s3transfer.egg-info/PKG-INFO  2025-09-09 
20:09:31.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: s3transfer
-Version: 0.13.1
+Version: 0.14.0
 Summary: An Amazon S3 Transfer Manager
 Home-page: https://github.com/boto/s3transfer
 Author: Amazon Web Services
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3transfer-0.13.1/tests/functional/test_download.py 
new/s3transfer-0.14.0/tests/functional/test_download.py
--- old/s3transfer-0.13.1/tests/functional/test_download.py     2025-07-18 
20:07:26.000000000 +0200
+++ new/s3transfer-0.14.0/tests/functional/test_download.py     2025-09-09 
20:09:31.000000000 +0200
@@ -21,7 +21,11 @@
 from botocore.exceptions import ClientError
 
 from s3transfer.compat import SOCKET_ERROR
-from s3transfer.exceptions import RetriesExceededError, S3DownloadFailedError
+from s3transfer.exceptions import (
+    RetriesExceededError,
+    S3DownloadFailedError,
+    S3ValidationError,
+)
 from s3transfer.manager import TransferConfig, TransferManager
 from tests import (
     BaseGeneralInterfaceTest,
@@ -109,7 +113,7 @@
         self.stubber.add_response(**head_response)
 
     def add_successful_get_object_responses(
-        self, expected_params=None, expected_ranges=None
+        self, expected_params=None, expected_ranges=None, extras=None
     ):
         # Add all get_object responses needed to complete the download.
         # Should account for both ranged and nonranged downloads.
@@ -124,6 +128,8 @@
                     stubbed_response['expected_params']['Range'] = (
                         expected_ranges[i]
                     )
+                if extras:
+                    stubbed_response['service_response'].update(extras[i])
             self.stubber.add_response(**stubbed_response)
 
     def add_n_retryable_get_object_responses(self, n, num_reads=0):
@@ -511,9 +517,12 @@
             'RequestPayer': 'requester',
         }
         expected_ranges = ['bytes=0-3', 'bytes=4-7', 'bytes=8-']
+        stubbed_ranges = ['bytes 0-3/10', 'bytes 4-7/10', 'bytes 8-9/10']
         self.add_head_object_response(expected_params)
         self.add_successful_get_object_responses(
-            {**expected_params, 'IfMatch': self.etag}, expected_ranges
+            {**expected_params, 'IfMatch': self.etag},
+            expected_ranges,
+            [{"ContentRange": r} for r in stubbed_ranges],
         )
 
         future = self.manager.download(
@@ -547,6 +556,28 @@
         with open(self.filename, 'rb') as f:
             self.assertEqual(self.content, f.read())
 
+    def test_download_raises_if_content_range_mismatch(self):
+        expected_params = {
+            'Bucket': self.bucket,
+            'Key': self.key,
+        }
+        expected_ranges = ['bytes=0-3', 'bytes=4-7', 'bytes=8-']
+        # Note that the final retrieved range should be `bytes 8-9/10`.
+        stubbed_ranges = ['bytes 0-3/10', 'bytes 4-7/10', 'bytes 7-8/10']
+        self.add_head_object_response(expected_params)
+        self.add_successful_get_object_responses(
+            {**expected_params, 'IfMatch': self.etag},
+            expected_ranges,
+            [{"ContentRange": r} for r in stubbed_ranges],
+        )
+
+        future = self.manager.download(
+            self.bucket, self.key, self.filename, self.extra_args
+        )
+        with self.assertRaises(S3ValidationError) as e:
+            future.result()
+        self.assertIn('does not match content range', str(e.exception))
+
     def test_download_raises_if_etag_validation_fails(self):
         expected_params = {
             'Bucket': self.bucket,

Reply via email to