mirror of
https://github.com/versity/versitygw.git
synced 2026-07-19 22:42:20 +00:00
Merge pull request #2184 from versity/sis/empty-content-md5
fix: reject empty Content-MD5 on PUT operations
This commit is contained in:
@@ -33,13 +33,19 @@ import (
|
||||
// the x-amz-checksum-* headers are explicitly processed by the backend.
|
||||
func VerifyChecksums(streamBody bool, requireBody bool, requireChecksum bool) fiber.Handler {
|
||||
return func(ctx fiber.Ctx) error {
|
||||
md5sum := ctx.Get("Content-Md5")
|
||||
md5Header := ctx.Request().Header.Peek("Content-Md5")
|
||||
hasMD5Header := md5Header != nil
|
||||
md5sum := string(md5Header)
|
||||
|
||||
if hasMD5Header && md5sum == "" {
|
||||
return s3err.GetInvalidDigestErr(md5sum)
|
||||
}
|
||||
|
||||
if streamBody {
|
||||
// for large data actions(PutObject, UploadPart)
|
||||
// only stack the md5 reader,as x-amz-checksum-*
|
||||
// calculation is explicitly handled in back-end
|
||||
if md5sum == "" {
|
||||
if !hasMD5Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ package integration
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -642,6 +644,54 @@ func PutObject_invalid_website_redirect_location(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func PutObject_md5(s *S3Conf) error {
|
||||
testName := "PutObject_md5"
|
||||
return actionHandler(s, testName, func(_ *s3.Client, bucket string) error {
|
||||
body := []byte("dummy data")
|
||||
sum := md5.Sum(body)
|
||||
contentMd5 := base64.StdEncoding.EncodeToString(sum[:])
|
||||
|
||||
for i, test := range []struct {
|
||||
name string
|
||||
md5 string
|
||||
err s3err.S3Error
|
||||
}{
|
||||
{"empty_md5", "", s3err.GetInvalidDigestErr("")},
|
||||
{"invalid_md5", "invalid_md5", s3err.GetInvalidDigestErr("invalid_md5")},
|
||||
// valid base64, but invalid md5
|
||||
{"invalid_md5_length", "aGVsbCBzLGRham5mamFuc2Y=", s3err.GetInvalidDigestErr("aGVsbCBzLGRham5mamFuc2Y=")},
|
||||
// valid md5, but incorrect
|
||||
{"incorrect_md5", "XrY7u+Ae7tCTyyK7j1rNww==", s3err.GetBadDigestErr(contentMd5, base64ToHexString("XrY7u+Ae7tCTyyK7j1rNww=="))},
|
||||
{"success", contentMd5, nil},
|
||||
} {
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, fmt.Sprintf("%s/obj-%d", bucket, i), s.awsID, s.awsSecret, "s3", s.awsRegion, "", body, time.Now(), map[string]string{
|
||||
"Content-Md5": test.md5,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if test.err == nil {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("test %q failed: expected response status code to be %v, instead got %v", test.name, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, test.err); err != nil {
|
||||
return fmt.Errorf("test %q failed: %w", test.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PutObject_checksum_algorithm_and_header_mismatch(s *S3Conf) error {
|
||||
testName := "PutObject_checksum_algorithm_and_header_mismatch"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
|
||||
@@ -36,7 +36,6 @@ func TestAuthentication(ts *TestState) {
|
||||
ts.Run(Authentication_date_mismatch)
|
||||
ts.Run(Authentication_incorrect_payload_hash)
|
||||
ts.Run(Authentication_invalid_sha256_payload_hash)
|
||||
ts.Run(Authentication_md5)
|
||||
ts.Run(Authentication_unsigned_required_header)
|
||||
ts.Run(Authentication_unsigned_non_required_header)
|
||||
ts.Run(Authentication_signature_error_incorrect_secret_key)
|
||||
@@ -179,6 +178,7 @@ func TestPutObject(ts *TestState) {
|
||||
ts.Run(PutObject_invalid_retain_until_date)
|
||||
ts.Run(PutObject_conditional_writes)
|
||||
ts.Run(PutObject_should_combine_metadata)
|
||||
ts.Run(PutObject_md5)
|
||||
//TODO: remove the condition after implementing checksums in azure
|
||||
if !ts.conf.azureTests {
|
||||
ts.Run(PutObject_checksum_algorithm_and_header_mismatch)
|
||||
@@ -1362,7 +1362,6 @@ func GetIntTests() IntTests {
|
||||
"Authentication_date_mismatch": Authentication_date_mismatch,
|
||||
"Authentication_incorrect_payload_hash": Authentication_incorrect_payload_hash,
|
||||
"Authentication_invalid_sha256_payload_hash": Authentication_invalid_sha256_payload_hash,
|
||||
"Authentication_md5": Authentication_md5,
|
||||
"Authentication_unsigned_required_header": Authentication_unsigned_required_header,
|
||||
"Authentication_unsigned_non_required_header": Authentication_unsigned_non_required_header,
|
||||
"Authentication_signature_error_incorrect_secret_key": Authentication_signature_error_incorrect_secret_key,
|
||||
@@ -1402,6 +1401,7 @@ func GetIntTests() IntTests {
|
||||
"PutObject_invalid_retain_until_date": PutObject_invalid_retain_until_date,
|
||||
"PutObject_conditional_writes": PutObject_conditional_writes,
|
||||
"PutObject_should_combine_metadata": PutObject_should_combine_metadata,
|
||||
"PutObject_md5": PutObject_md5,
|
||||
"PutObject_long_metadata": PutObject_long_metadata,
|
||||
"PutObject_with_metadata": PutObject_with_metadata,
|
||||
"PutObject_invalid_website_redirect_location": PutObject_invalid_website_redirect_location,
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -500,43 +498,6 @@ func Authentication_incorrect_payload_hash(s *S3Conf) error {
|
||||
})
|
||||
}
|
||||
|
||||
func Authentication_md5(s *S3Conf) error {
|
||||
testName := "Authentication_md5"
|
||||
return actionHandler(s, testName, func(_ *s3.Client, bucket string) error {
|
||||
sum := md5.Sum(nil)
|
||||
emptyMd5 := base64.StdEncoding.EncodeToString(sum[:])
|
||||
|
||||
for i, test := range []struct {
|
||||
md5 string
|
||||
err s3err.S3Error
|
||||
}{
|
||||
{"invalid_md5", s3err.GetInvalidDigestErr("invalid_md5")},
|
||||
// valid base64, but invalid md5
|
||||
{"aGVsbCBzLGRham5mamFuc2Y=", s3err.GetInvalidDigestErr("aGVsbCBzLGRham5mamFuc2Y=")},
|
||||
// valid md5, but incorrect
|
||||
{"XrY7u+Ae7tCTyyK7j1rNww==", s3err.GetBadDigestErr(emptyMd5, base64ToHexString("XrY7u+Ae7tCTyyK7j1rNww=="))},
|
||||
} {
|
||||
req, err := createSignedReq(http.MethodPut, s.endpoint, fmt.Sprintf("%s/obj", bucket), s.awsID, s.awsSecret, "s3", s.awsRegion, "", nil, time.Now(), map[string]string{
|
||||
"Content-Md5": test.md5,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, test.err); err != nil {
|
||||
return fmt.Errorf("test %v failed: %w", i+1, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Authentication_unsigned_required_header(s *S3Conf) error {
|
||||
testName := "Authentication_unsigned_required_header"
|
||||
return actionHandler(s, testName, func(_ *s3.Client, bucket string) error {
|
||||
|
||||
Reference in New Issue
Block a user