Skip to content

Commit

Permalink
fix: Fixed CopyObject copy-source parsing to handle object names with…
Browse files Browse the repository at this point in the history
… special characters
  • Loading branch information
0x180 committed Oct 2, 2024
1 parent fed72e9 commit d2df00a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 10 deletions.
16 changes: 7 additions & 9 deletions backend/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,13 @@ func ParseCopySource(copySourceHeader string) (string, string, string, error) {
copySourceHeader = copySourceHeader[1:]
}

cSplitted := strings.Split(copySourceHeader, "?")
copySource := cSplitted[0]
var versionId string
if len(cSplitted) > 1 {
versionIdParts := strings.Split(cSplitted[1], "=")
if len(versionIdParts) != 2 || versionIdParts[0] != "versionId" {
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidRequest)
}
versionId = versionIdParts[1]
var copySource, versionId string
i := strings.LastIndex(copySourceHeader, "?versionId=")
if i == -1 {
copySource = copySourceHeader
} else {
copySource = copySourceHeader[:i]
versionId = copySourceHeader[i+11:]
}

srcBucket, srcObject, ok := strings.Cut(copySource, "/")
Expand Down
2 changes: 1 addition & 1 deletion s3err/s3err.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ var errorCodeResponse = map[ErrorCode]APIError{
},
ErrInvalidAccessKeyID: {
Code: "InvalidAccessKeyId",
Description: "The access key ID you provided does not exist in our records.",
Description: "The AWS Access Key Id you provided does not exist in our records.",
HTTPStatusCode: http.StatusForbidden,
},
ErrRequestNotReadyYet: {
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/group-tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ func TestVersioning(s *S3Conf) {
Versioning_CopyObject_success(s)
Versioning_CopyObject_non_existing_version_id(s)
Versioning_CopyObject_from_an_object_version(s)
Versioning_CopyObject_special_chars(s)
// HeadObject action
Versioning_HeadObject_invalid_versionId(s)
Versioning_HeadObject_success(s)
Expand Down Expand Up @@ -895,6 +896,7 @@ func GetIntTests() IntTests {
"Versioning_CopyObject_success": Versioning_CopyObject_success,
"Versioning_CopyObject_non_existing_version_id": Versioning_CopyObject_non_existing_version_id,
"Versioning_CopyObject_from_an_object_version": Versioning_CopyObject_from_an_object_version,
"Versioning_CopyObject_special_chars": Versioning_CopyObject_special_chars,
"Versioning_HeadObject_invalid_versionId": Versioning_HeadObject_invalid_versionId,
"Versioning_HeadObject_success": Versioning_HeadObject_success,
"Versioning_HeadObject_delete_marker": Versioning_HeadObject_delete_marker,
Expand Down
53 changes: 53 additions & 0 deletions tests/integration/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -10673,6 +10673,59 @@ func Versioning_CopyObject_from_an_object_version(s *S3Conf) error {
}, withVersioning())
}

func Versioning_CopyObject_special_chars(s *S3Conf) error {
testName := "Versioning_CopyObject_special_chars"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
srcObj, dstBucket, dstObj := "foo?bar", getBucketName(), "bar&foo"
err := setup(s, dstBucket)
if err != nil {
return err
}

srcObjVersions, err := createObjVersions(s3client, bucket, srcObj, 1)
if err != nil {
return err
}

srcObjVersionId := *srcObjVersions[0].VersionId

ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
res, err := s3client.CopyObject(ctx, &s3.CopyObjectInput{
Bucket: &bucket,
Key: &dstObj,
CopySource: getPtr(fmt.Sprintf("%v/%v?versionId=%v", bucket, srcObj, srcObjVersionId)),
})
cancel()
if err != nil {
return err
}

if res.VersionId == nil || *res.VersionId == "" {
return fmt.Errorf("expected non empty versionId")
}
if *res.CopySourceVersionId != srcObjVersionId {
return fmt.Errorf("expected the SourceVersionId to be %v, instead got %v", srcObjVersionId, *res.CopySourceVersionId)
}

ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
out, err := s3client.HeadObject(ctx, &s3.HeadObjectInput{
Bucket: &bucket,
Key: &dstObj,
VersionId: res.VersionId,
})
cancel()
if err != nil {
return err
}

if *out.VersionId != *res.VersionId {
return fmt.Errorf("expected the copied object versionId to be %v, instead got %v", *res.VersionId, *out.VersionId)
}

return nil
}, withVersioning())
}

func Versioning_HeadObject_invalid_versionId(s *S3Conf) error {
testName := "Versioning_HeadObject_invalid_versionId"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
Expand Down

0 comments on commit d2df00a

Please sign in to comment.