fix: fixes DeleteObject if-match quoted comparison

Fixes #1835

If-Match in DeleteObject is a precondition header that compares the client-provided ETag with the server-side ETag before deleting the object. Previously, the comparison failed when the client sent an unquoted ETag, because server ETags are stored with quotes. The implementation now trims quotes from both the input ETag and the server ETag before comparison to avoid mismatches. Both quoted and unquoted ETags are valid according to S3.
This commit is contained in:
niksis02
2026-02-11 16:45:36 +04:00
parent a4323d5338
commit 89aa822a40
4 changed files with 14 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 {