diff --git a/backend/common.go b/backend/common.go index 44bd0d9..b9ae2e1 100644 --- a/backend/common.go +++ b/backend/common.go @@ -634,6 +634,7 @@ type ObjectDeletePreconditions struct { // EvaluateObjectDeletePreconditions evaluates preconditions for DeleteObject func EvaluateObjectDeletePreconditions(etag string, modTime time.Time, size int64, preconditions ObjectDeletePreconditions) error { + etag = strings.Trim(etag, `"`) ifMatch := preconditions.IfMatch if ifMatch != nil && *ifMatch != etag { return errPreconditionFailed diff --git a/s3api/controllers/object-delete.go b/s3api/controllers/object-delete.go index bac9d45..3a3b6ee 100644 --- a/s3api/controllers/object-delete.go +++ b/s3api/controllers/object-delete.go @@ -133,7 +133,7 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) { key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") bypass := strings.EqualFold(ctx.Get("X-Amz-Bypass-Governance-Retention"), "true") - ifMatch := utils.GetStringPtr(ctx.Get("If-Match")) + ifMatch := utils.GetStringPtr(utils.TrimQuotes(ctx.Get("If-Match"))) ifMatchLastModTime := utils.ParsePreconditionDateHeader(ctx.Get("X-Amz-If-Match-Last-Modified-Time")) ifMatchSize := utils.ParseIfMatchSize(ctx) // context locals diff --git a/s3api/utils/precondition.go b/s3api/utils/precondition.go index 0852980..52a857b 100644 --- a/s3api/utils/precondition.go +++ b/s3api/utils/precondition.go @@ -68,8 +68,8 @@ func ParsePreconditionMatchHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) (*st prefix = "X-Amz-Copy-Source-" } - ifMatch := trimQuotes(ctx.Get(prefix + "If-Match")) - ifNoneMatch := trimQuotes(ctx.Get(prefix + "If-None-Match")) + ifMatch := TrimQuotes(ctx.Get(prefix + "If-Match")) + ifNoneMatch := TrimQuotes(ctx.Get(prefix + "If-None-Match")) return GetStringPtr(ifMatch), GetStringPtr(ifNoneMatch) } @@ -143,7 +143,7 @@ func ParseIfMatchSize(ctx *fiber.Ctx) *int64 { return &ifMatchSize } -func trimQuotes(str string) string { +func TrimQuotes(str string) string { if len(str) < 2 { return str } diff --git a/tests/integration/DeleteObject.go b/tests/integration/DeleteObject.go index 95acfe1..8c9a0fd 100644 --- a/tests/integration/DeleteObject.go +++ b/tests/integration/DeleteObject.go @@ -19,6 +19,7 @@ import ( "context" "fmt" "net/http" + "strings" "time" "github.com/aws/aws-sdk-go-v2/service/s3" @@ -124,6 +125,7 @@ func DeleteObject_conditional_writes(s *S3Conf) error { return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { obj := "my-obj" var etag *string = getPtr("") + var etagUnquoted *string = getPtr("") var size *int64 = getPtr(int64(0)) var modTime *time.Time = getPtr(time.Now()) @@ -149,6 +151,9 @@ func DeleteObject_conditional_writes(s *S3Conf) error { } *etag = *res.res.ETag + if etag != nil { + *etagUnquoted = strings.Trim(*etag, `"`) + } *size = *res.res.Size *modTime = *out.LastModified @@ -176,12 +181,16 @@ func DeleteObject_conditional_writes(s *S3Conf) error { {etag, size, nil, nil}, {etag, nil, modTime, nil}, {nil, size, modTime, nil}, + // unqoted etag + {etagUnquoted, nil, nil, nil}, // error cases {getPtr("incorrect_etag"), nil, nil, errPrecond}, {nil, getPtr(int64(23234)), nil, errPrecond}, {nil, nil, getPtr(time.Now().AddDate(-1, -1, -1)), errPrecond}, {getPtr("incorrect_etag"), getPtr(int64(23234)), nil, errPrecond}, {getPtr("incorrect_etag"), getPtr(int64(23234)), getPtr(time.Now().AddDate(-1, -1, -1)), errPrecond}, + // quoted incorrect etag + {getPtr(`"incorrect_etag"`), nil, nil, errPrecond}, } { err := createObj() if err != nil {