feat(s3_v2): send Content-MD5 on PUT and optional server-side encryption - #31928
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
Mubashir Osmani seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Greptile SummaryThis PR fixes S3 PUT uploads for Object Lock–enabled buckets by adding the required
Confidence Score: 5/5Safe to merge — both upload paths correctly compute and sign Content-MD5, the SSE param is fully opt-in, and all new tests are properly mocked. The MD5 is computed over the exact bytes sent as the request body and is placed in the header dict before SigV4 signing, matching what AWS requires. usedforsecurity=False is present on both paths, so FIPS environments are handled. The SSE parameter defaults to None and is conditionally applied, leaving existing deployments unaffected. Tests are isolated (no network calls) and faithfully simulate both the normal and FIPS-restricted code paths. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/integrations/s3_v2.py | Adds Content-MD5 header (base64 MD5, computed before SigV4 signing) to both async and sync PUT paths, and optionally injects x-amz-server-side-encryption; usedforsecurity=False is correctly used on both paths; s3_server_side_encryption is properly threaded through constructor and _configure_s3_logger |
| tests/test_litellm/integrations/test_s3_v2.py | Four new unit tests added: async/sync Content-MD5 correctness, SSE header presence when configured, and SSE loading from s3_callback_params; all use mocked HTTP clients (no real network calls), and the FIPS-mode stub correctly validates usedforsecurity=False is passed |
Reviews (2): Last reviewed commit: "fix: allow S3 Content-MD5 on FIPS hosts" | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
are greptile comments legit and in scope? |
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: FIPS MD5 call may fail
- MD5 Content-MD5 generation now passes usedforsecurity=False and has regression coverage for both S3 upload paths.
You can send follow-ups to the cloud agent here.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 68a8fc2. Configure here.
|
|
||
| # Calculate SHA256 hash of the content | ||
| content_hash = hashlib.sha256(json_string.encode("utf-8")).hexdigest() | ||
| content_md5 = base64.b64encode(hashlib.md5(json_string.encode("utf-8")).digest()).decode() |
There was a problem hiding this comment.
FIPS MD5 call may fail
Low Severity
New Content-MD5 computation uses hashlib.md5() without usedforsecurity=False. On FIPS-enabled OpenSSL setups that block MD5 for security, that call can raise before the PUT runs, so s3_v2 uploads fail even though the payload SHA-256 signing path still works.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 68a8fc2. Configure here.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
@yucheng-berri the Greptile finding is legit and in scope. Both Fixed in e542be1 by passing The rest of the Greptile summary is descriptive and accurate, no other actionable items |
|
can you reping greptile to verify again? |


Relevant issues
s3_v2 PUTs only sent
x-amz-content-sha256, so buckets with Object Lock enabled reject the upload because AWS requires aContent-MD5header on those PUTs per the S3 specLinear ticket
Pre-Submission checklist
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewScreenshots / Proof of Fix
Ran a live proxy (
litellm/proxy/proxy_cli.py) withs3_v2logging pointed at a local S3-compatible sink vias3_endpoint_url, made a real Anthropic chat completion (real tokens, real $), and inspected the actual PUT the logger emitted. The sink recomputes the md5 of the body it received and compares it to the headerReal LLM call through the proxy:
The PUT captured by the sink (real request the logger sent):
{ "path": "/litellm-proof-bucket/2026-07-02/time-00-58-50-283780_chatcmpl-....json", "headers": { "content-type": "application/json", "content-md5": "FJNbl8YV7yqoOg5rAftWJQ==", "x-amz-content-sha256": "3dced6c57ddca77f3c16029b557811c56ba58568a5b5f0f32c9fa75fc4a6af93", "x-amz-server-side-encryption": "aws:kms", "authorization": "AWS4-HMAC-SHA256 Credential=.../s3/aws4_request, SignedHeaders=cache-control;content-disposition;content-language;content-length;content-md5;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-server-side-encryption, Signature=..." }, "recomputed_content_md5_of_body": "FJNbl8YV7yqoOg5rAftWJQ==", "content_md5_matches_body": true }content_md5_matches_bodyis true, and bothcontent-md5andx-amz-server-side-encryptionappear in the SigV4SignedHeaders, so Object Lock buckets will accept the upload rather than rejecting it as unsigned/missingType
🆕 New Feature
🐛 Bug Fix
Changes
Both PUT paths in
s3_v2.py(async_upload_data_to_s3andupload_data_to_s3) now compute and sendContent-MD5The md5 is over the exact
json_stringthat becomes the request body, and because the header is set beforeSigV4Auth().add_auth()it ends up in the signed header setAlso adds an optional
s3_server_side_encryptionparam (configurable directly or vias3_callback_params) that, when set, sendsx-amz-server-side-encryption(e.g.aws:kms) for buckets with a KMS default-encryption policy. It is injected into the header dict only when configured, so behavior is unchanged when it is unsetTests in
tests/test_litellm/integrations/test_s3_v2.pyassert the Content-MD5 header matchesbase64(md5(body))on both the async and sync PUT paths, thatx-amz-server-side-encryptionis absent by default and present with the correct value when configured, and that the param resolves froms3_callback_paramsLink to Devin session: https://app.devin.ai/sessions/3fddf4390d7948ba879615b49e6ef468
Requested by: @mubashir1osmani
Note
Medium Risk
Changes the signed S3 PUT header set for all s3_v2 log uploads; miscomputed MD5 would break uploads, though behavior is covered by tests and SSE remains opt-in.
Overview
s3_v2 logging PUTs now include a
Content-MD5header (base64 MD5 of the exact JSON body) on bothasync_upload_data_to_s3andupload_data_to_s3, so Object Lock–enabled buckets accept uploads instead of rejecting them for missing MD5.Adds optional
s3_server_side_encryption(constructor arg ors3_callback_params). When set, PUTs also sendx-amz-server-side-encryption(e.g.aws:kms); when unset, headers are unchanged.New tests assert MD5 on async/sync paths, SSE header when configured, and loading SSE from global callback params.
Reviewed by Cursor Bugbot for commit 68a8fc2. Bugbot is set up for automated code reviews on this repo. Configure here.