Merge pull request #1613 from versity/sis/copyobject-non-empty-body

fix: adds request body check for CopyObject and UploadPartCopy
This commit is contained in:
Ben McClelland
2025-11-04 11:39:56 -08:00
committed by GitHub
3 changed files with 65 additions and 0 deletions

View File

@@ -354,6 +354,15 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) {
}, err }, err
} }
if len(ctx.Request().Body()) != 0 {
debuglogger.Logf("expected empty request body")
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrNonEmptyRequestBody)
}
if partNumber < minPartNumber || partNumber > maxPartNumber { if partNumber < minPartNumber || partNumber > maxPartNumber {
debuglogger.Logf("invalid part number: %d", partNumber) debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{ return &Response{
@@ -490,6 +499,15 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) {
}, err }, err
} }
if len(ctx.Request().Body()) != 0 {
debuglogger.Logf("expected empty request body")
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.GetAPIError(s3err.ErrNonEmptyRequestBody)
}
metadata := utils.GetUserMetaData(&ctx.Request().Header) metadata := utils.GetUserMetaData(&ctx.Request().Header)
if metaDirective != "" && metaDirective != types.MetadataDirectiveCopy && metaDirective != types.MetadataDirectiveReplace { if metaDirective != "" && metaDirective != types.MetadataDirectiveCopy && metaDirective != types.MetadataDirectiveReplace {

View File

@@ -599,6 +599,27 @@ func TestS3ApiController_UploadPartCopy(t *testing.T) {
err: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding), err: s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding),
}, },
}, },
{
name: "non empty request body",
input: testInput{
locals: defaultLocals,
headers: map[string]string{
"X-Amz-Copy-Source": "bucket/object",
},
queries: map[string]string{
"partNumber": "2",
},
body: []byte("body"),
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: "root",
},
},
err: s3err.GetAPIError(s3err.ErrNonEmptyRequestBody),
},
},
{ {
name: "invalid part number", name: "invalid part number",
input: testInput{ input: testInput{
@@ -696,6 +717,7 @@ func TestS3ApiController_UploadPartCopy(t *testing.T) {
locals: tt.input.locals, locals: tt.input.locals,
headers: tt.input.headers, headers: tt.input.headers,
queries: tt.input.queries, queries: tt.input.queries,
body: tt.input.body,
}) })
}) })
} }
@@ -817,6 +839,24 @@ func TestS3ApiController_CopyObject(t *testing.T) {
err: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket), err: s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket),
}, },
}, },
{
name: "invalid copy source",
input: testInput{
locals: defaultLocals,
headers: map[string]string{
"X-Amz-Copy-Source": "bucket/object",
},
body: []byte("body"),
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: "root",
},
},
err: s3err.GetAPIError(s3err.ErrNonEmptyRequestBody),
},
},
{ {
name: "invalid metadata directive", name: "invalid metadata directive",
input: testInput{ input: testInput{
@@ -999,6 +1039,7 @@ func TestS3ApiController_CopyObject(t *testing.T) {
ctxInputs{ ctxInputs{
locals: tt.input.locals, locals: tt.input.locals,
headers: tt.input.headers, headers: tt.input.headers,
body: tt.input.body,
}) })
}) })
} }

View File

@@ -87,6 +87,7 @@ const (
ErrInvalidPartOrder ErrInvalidPartOrder
ErrInvalidCompleteMpPartNumber ErrInvalidCompleteMpPartNumber
ErrInternalError ErrInternalError
ErrNonEmptyRequestBody
ErrInvalidCopyDest ErrInvalidCopyDest
ErrInvalidCopySourceRange ErrInvalidCopySourceRange
ErrInvalidCopySourceBucket ErrInvalidCopySourceBucket
@@ -319,6 +320,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "We encountered an internal error, please try again.", Description: "We encountered an internal error, please try again.",
HTTPStatusCode: http.StatusInternalServerError, HTTPStatusCode: http.StatusInternalServerError,
}, },
ErrNonEmptyRequestBody: {
Code: "InvalidRequest",
Description: "The request included a body. Requests of this type must not include a non-empty body.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidPart: { ErrInvalidPart: {
Code: "InvalidPart", Code: "InvalidPart",
Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.", Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.",