s3: reject UploadPart checksum algorithms conflicting with the upload

An UploadPart that explicitly selects a different checksum algorithm than
the one declared at CreateMultipartUpload would store a checksum
CompleteMultipartUpload could never accept. Reject the conflict up front
with InvalidRequest, matching AWS.
This commit is contained in:
Chris Lu
2026-09-20 01:15:11 -07:00
committed by Chris Lu
parent f849b7c823
commit b03419ee92
2 changed files with 39 additions and 4 deletions
+30
View File
@@ -211,3 +211,33 @@ func TestCompleteMultipartUploadValidatesPartChecksums(t *testing.T) {
require.NoError(t, complete(part.ChecksumSHA256))
}
// UploadPart with a checksum algorithm that conflicts with the one declared at
// CreateMultipartUpload is rejected, as on AWS.
func TestMultipartPartConflictingAlgorithm(t *testing.T) {
client := newWhenRequiredChecksumClient(t)
bucket := uniqueBucket()
createBucket(t, client, bucket)
defer cleanupBucket(t, client, bucket)
create, err := client.CreateMultipartUpload(context.Background(), &s3.CreateMultipartUploadInput{
Bucket: aws.String(bucket),
Key: aws.String("conflict"),
ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
})
require.NoError(t, err)
_, err = client.UploadPart(context.Background(), &s3.UploadPartInput{
Bucket: aws.String(bucket),
Key: aws.String("conflict"),
UploadId: create.UploadId,
PartNumber: aws.Int32(1),
Body: bytes.NewReader([]byte("data")),
ChecksumAlgorithm: types.ChecksumAlgorithmCrc32,
})
var apiErr smithy.APIError
require.Error(t, err)
require.True(t, errors.As(err, &apiErr))
require.Equal(t, "InvalidRequest", apiErr.ErrorCode())
}
@@ -446,11 +446,16 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ
}
// Parts inherit the checksum algorithm declared at CreateMultipartUpload
// when the request doesn't specify one (AWS behavior).
// when the request doesn't specify one; a conflicting one is rejected.
if headerName := string(uploadEntry.Extended[s3_constants.ExtChecksumAlgorithm]); headerName != "" {
if algo, _, code := detectRequestedChecksumAlgorithm(r); code == s3err.ErrNone && algo == ChecksumAlgorithmNone {
if name := checksumAlgorithmNameFromHeaderName(headerName); name != "" {
r.Header.Set(s3_constants.AmzChecksumAlgorithm, name)
if algo, reqHeaderName, code := detectRequestedChecksumAlgorithm(r); code == s3err.ErrNone {
if algo == ChecksumAlgorithmNone {
if name := checksumAlgorithmNameFromHeaderName(headerName); name != "" {
r.Header.Set(s3_constants.AmzChecksumAlgorithm, name)
}
} else if reqHeaderName != headerName {
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
return
}
}
}