Skip to content

Fix S3 SSE-C copy/move (#500) and add addressing_style support (#527)#576

Merged
pjbull merged 3 commits into
masterfrom
pjbull-fix-issues-500-527
Jun 28, 2026
Merged

Fix S3 SSE-C copy/move (#500) and add addressing_style support (#527)#576
pjbull merged 3 commits into
masterfrom
pjbull-fix-issues-500-527

Conversation

@pjbull

@pjbull pjbull commented Jun 28, 2026

Copy link
Copy Markdown
Member

Two related S3 fixes that share a refactor of how S3Client constructs its boto3 resource/client.

#500 — SSE-C copy/move was broken. _move_file filtered extra_args through ALLOWED_UPLOAD_ARGS/ALLOWED_DOWNLOAD_ARGS, which excludes the CopySourceSSECustomer* keys. As a result, S3Path.copy, S3Path.move, and S3Path.touch on SSE-C objects failed with InvalidRequest from S3. The fix introduces a third filter, boto3_copy_extra_args, built from S3Transfer.ALLOWED_COPY_ARGS, and routes both Object.copy(ExtraArgs=...) (cross-key) and Object.copy_from(**extras) (same-key touch) through it.

#527 — no way to use virtual-hosted addressing. Added an addressing_style kwarg on S3Client that gets folded into a single botocore.config.Config(s3={"addressing_style": ...}) passed to both the resource and client. The existing no_sign_request path now composes with it via the same Config, and _get_public_url's unsigned client picks up the same s3 settings so presigned URLs against virtual-host-only endpoints work too.

Drive-by fix: in the same-key touch branch of _move_file, copy_from was called with explicit Metadata and MetadataDirective and **boto3_copy_extra_args. Since both keys live in ALLOWED_COPY_ARGS, a user who passed either via extra_args would hit TypeError: copy_from() got multiple values for keyword argument. Those keys are now dropped from the splat in the touch branch.

Tests

  • Mocked: test_copy_and_move_pass_copy_extra_args, test_touch_passes_copy_extra_args_without_collision, test_s3_addressing_style_virtual_passed_to_boto3.
  • Live (gated on s3_rig.live_server): test_sse_c_copy_and_move_live round-trips an SSE-C object through write/copy/touch/move; test_addressing_style_virtual_live asserts the presigned URL host is {bucket}.s3... and round-trips a real object.
  • Mock additions are minimal: the fake session now records resource_config/client_config, and MockBoto3Object records last_copy/last_copy_from so tests can inspect what was forwarded.

Boto3 docs verified (boto3 1.43.36)

  • S3Transfer.ALLOWED_COPY_ARGS contains CopySourceSSECustomer*, SSECustomer*, MetadataDirective, etc.
  • Object.copy(ExtraArgs=...) validates against ALLOWED_COPY_ARGS and raises on anything else, so the prefilter is required for that path.
  • Object.copy_from(**kw) forwards directly to the CopyObject API without validation; using the same allow-list is conservative but safe.
  • Config(s3={"addressing_style": "virtual"}) is the documented botocore knob.

Closes #500
Closes #527


Contributor checklist:

  • I have read and understood CONTRIBUTING.md
  • Confirmed an issue exists for the PR, and the text Closes #issue appears in the PR summary (e.g., Closes #123).
  • Confirmed PR is rebased onto the latest base
  • Confirmed failure before change and success after change
  • Any generic new functionality is replicated across cloud providers if necessary
  • Tested manually against live server backend for at least one provider
  • Added tests for any new functionality
  • Linting passes locally
  • Tests pass locally
  • Updated HISTORY.md with the issue that is addressed and the PR you are submitting. If the top section is not ## UNRELEASED, then you need to add a new section to the top of the document for your change.

Note on live testing: the changes ship with two live-server-only tests (test_sse_c_copy_and_move_live, test_addressing_style_virtual_live) that exercise both fixes end-to-end against a real S3 bucket. They have not been run from this branch yet against LIVE_S3_BUCKET; maintainers running USE_LIVE_CLOUD=1 make test-live-cloud will cover that gap.

@github-actions

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot temporarily deployed to pull request June 28, 2026 02:05 Inactive
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.3%. Comparing base (5ed46d3) to head (a505617).

Additional details and impacted files
@@           Coverage Diff            @@
##           master    #576     +/-   ##
========================================
- Coverage    94.3%   94.3%   -0.1%     
========================================
  Files          28      28             
  Lines        2259    2267      +8     
========================================
+ Hits         2132    2138      +6     
- Misses        127     129      +2     
Files with missing lines Coverage Δ
cloudpathlib/s3/s3client.py 94.6% <100.0%> (+0.3%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

pjbull and others added 2 commits June 27, 2026 19:52
Fixes #500: copy/move/touch on S3 paths now forward CopySource* extras
(notably CopySourceSSECustomerKey) by filtering extra_args against
S3Transfer.ALLOWED_COPY_ARGS instead of ALLOWED_UPLOAD_ARGS/DOWNLOAD_ARGS.

Fixes #527: adds addressing_style kwarg to S3Client that flows through
to botocore.config.Config(s3={"addressing_style": ...}) for both the
resource and client, including the unsigned client used for public URLs.

Also fixes a latent TypeError in the same-key touch path where
Metadata/MetadataDirective in user-supplied extra_args would collide
with the explicit kwargs to copy_from.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
botocore's sse_md5 handler base64-encodes SSECustomerKey and computes
SSECustomerKeyMD5 itself. Passing an already-base64-encoded string
double-encodes the key, so S3 rejects PutObject with "The secret key
was invalid for the specified algorithm". Pass the raw 32 random bytes
instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pjbull pjbull force-pushed the pjbull-fix-issues-500-527 branch from 61ed796 to 8cc33d0 Compare June 28, 2026 02:52
@github-actions github-actions Bot temporarily deployed to pull request June 28, 2026 02:55 Inactive
The drivendata CI bucket has an S3 bucket policy that explicitly denies
PutObject when SSE-C is specified, so the SSE-C live regression test
can't run end-to-end there. Detect that exact AccessDenied message and
pytest.skip instead of failing, so the test still provides regression
coverage on buckets that allow SSE-C without breaking CI on ones that
don't.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot temporarily deployed to pull request June 28, 2026 03:20 Inactive
@pjbull pjbull merged commit 9d5fa70 into master Jun 28, 2026
28 checks passed
@pjbull pjbull deleted the pjbull-fix-issues-500-527 branch June 28, 2026 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for s3.addressing_style virtual Failing S3 move and copy operations with SSE-C

1 participant