From 4ef090dbfc95932de7462d3b572db9c6559170c3 Mon Sep 17 00:00:00 2001 From: niksis02 Date: Sat, 23 May 2026 04:18:06 +0400 Subject: [PATCH] fix: fix empty ownership control rules panic Fix the gateway panic when validating malformed bucket ownership controls bodies with no rules. The handler now checks the rules count before indexing the first rule. --- s3api/controllers/bucket-put.go | 15 ++++++++----- s3api/controllers/bucket-put_test.go | 18 +++++++++++++++ .../integration/PutBucketOwnershipControls.go | 22 +++++++++++++++++++ tests/integration/group-tests.go | 2 ++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/s3api/controllers/bucket-put.go b/s3api/controllers/bucket-put.go index 04c0311f..860fb015 100644 --- a/s3api/controllers/bucket-put.go +++ b/s3api/controllers/bucket-put.go @@ -108,11 +108,16 @@ func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response, } rulesCount := len(ownershipControls.Rules) - isValidOwnership := utils.IsValidOwnership(ownershipControls.Rules[0].ObjectOwnership) - if rulesCount != 1 || !isValidOwnership { - if rulesCount != 1 { - debuglogger.Logf("ownership control rules should be 1, got %v", rulesCount) - } + if rulesCount != 1 { + debuglogger.Logf("ownership control rules should be 1, got %v", rulesCount) + return &Response{ + MetaOpts: &MetaOptions{ + BucketOwner: parsedAcl.Owner, + }, + }, s3err.GetAPIError(s3err.ErrMalformedXML) + } + + if !utils.IsValidOwnership(ownershipControls.Rules[0].ObjectOwnership) { return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, diff --git a/s3api/controllers/bucket-put_test.go b/s3api/controllers/bucket-put_test.go index bffe4662..c699eb8a 100644 --- a/s3api/controllers/bucket-put_test.go +++ b/s3api/controllers/bucket-put_test.go @@ -158,6 +158,11 @@ func TestS3ApiController_PutBucketOwnershipControls(t *testing.T) { ) assert.NoError(t, err) + emptyRulesBody, err := xml.Marshal( + s3response.OwnershipControls{}, + ) + assert.NoError(t, err) + tests := []struct { name string input testInput @@ -203,6 +208,19 @@ func TestS3ApiController_PutBucketOwnershipControls(t *testing.T) { err: s3err.GetAPIError(s3err.ErrMalformedXML), }, }, + { + name: "empty rules count", + input: testInput{ + locals: defaultLocals, + body: emptyRulesBody, + }, + output: testOutput{ + response: &Response{ + MetaOpts: &MetaOptions{BucketOwner: "root"}, + }, + err: s3err.GetAPIError(s3err.ErrMalformedXML), + }, + }, { name: "backend error", input: testInput{ diff --git a/tests/integration/PutBucketOwnershipControls.go b/tests/integration/PutBucketOwnershipControls.go index b9e116ec..ad0c823c 100644 --- a/tests/integration/PutBucketOwnershipControls.go +++ b/tests/integration/PutBucketOwnershipControls.go @@ -16,6 +16,8 @@ package integration import ( "context" + "net/http" + "time" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" @@ -94,6 +96,26 @@ func PutBucketOwnershipControls_invalid_ownership(s *S3Conf) error { }) } +func PutBucketOwnershipControls_empty_rules(s *S3Conf) error { + testName := "PutBucketOwnershipControls_empty_rules" + return actionHandler(s, testName, func(_ *s3.Client, bucket string) error { + body := []byte(``) + req, err := createSignedReq(http.MethodPut, s.endpoint, bucket+"?ownershipControls", s.awsID, s.awsSecret, "s3", s.awsRegion, "", body, time.Now(), map[string]string{ + "Content-Type": "application/xml", + }) + if err != nil { + return err + } + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedXML)) + }) +} + func PutBucketOwnershipControls_success(s *S3Conf) error { testName := "PutBucketOwnershipControls_success" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 0bd7254b..4ef10bd5 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -119,6 +119,7 @@ func TestPutBucketOwnershipControls(ts *TestState) { ts.Run(PutBucketOwnershipControls_non_existing_bucket) ts.Run(PutBucketOwnershipControls_multiple_rules) ts.Run(PutBucketOwnershipControls_invalid_ownership) + ts.Run(PutBucketOwnershipControls_empty_rules) ts.Run(PutBucketOwnershipControls_success) } @@ -1383,6 +1384,7 @@ func GetIntTests() IntTests { "PutBucketOwnershipControls_non_existing_bucket": PutBucketOwnershipControls_non_existing_bucket, "PutBucketOwnershipControls_multiple_rules": PutBucketOwnershipControls_multiple_rules, "PutBucketOwnershipControls_invalid_ownership": PutBucketOwnershipControls_invalid_ownership, + "PutBucketOwnershipControls_empty_rules": PutBucketOwnershipControls_empty_rules, "PutBucketOwnershipControls_success": PutBucketOwnershipControls_success, "GetBucketOwnershipControls_non_existing_bucket": GetBucketOwnershipControls_non_existing_bucket, "GetBucketOwnershipControls_default_ownership": GetBucketOwnershipControls_default_ownership,