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
}
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 {
debuglogger.Logf("invalid part number: %d", partNumber)
return &Response{
@@ -490,6 +499,15 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) {
}, 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)
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),
},
},
{
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",
input: testInput{
@@ -696,6 +717,7 @@ func TestS3ApiController_UploadPartCopy(t *testing.T) {
locals: tt.input.locals,
headers: tt.input.headers,
queries: tt.input.queries,
body: tt.input.body,
})
})
}
@@ -817,6 +839,24 @@ func TestS3ApiController_CopyObject(t *testing.T) {
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",
input: testInput{
@@ -999,6 +1039,7 @@ func TestS3ApiController_CopyObject(t *testing.T) {
ctxInputs{
locals: tt.input.locals,
headers: tt.input.headers,
body: tt.input.body,
})
})
}

View File

@@ -87,6 +87,7 @@ const (
ErrInvalidPartOrder
ErrInvalidCompleteMpPartNumber
ErrInternalError
ErrNonEmptyRequestBody
ErrInvalidCopyDest
ErrInvalidCopySourceRange
ErrInvalidCopySourceBucket
@@ -319,6 +320,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "We encountered an internal error, please try again.",
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: {
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.",