mirror of
https://github.com/versity/versitygw.git
synced 2026-10-01 12:16:12 +00:00
fix: forward slash url encoded used as bucket/key separator
handle %2F ("/") url encoded separator for bucket/key
Fixes #2024
---------
Co-authored-by: Galie Alexandru MTANA <alexandru.galie@mt.com>
This commit is contained in:
co-authored by
Galie Alexandru MTANA
parent
e49bb1d76a
commit
fe3cfbfce9
+14
-15
@@ -232,29 +232,28 @@ func ParseCopySource(copySourceHeader string) (string, string, string, error) {
|
||||
copySourceHeader = copySourceHeader[1:]
|
||||
}
|
||||
|
||||
var copySource, versionId string
|
||||
// Split the raw header on the versionId query parameter before any
|
||||
// URL-decoding so that the '?' delimiter is not percent-encoded.
|
||||
var rawSource, versionId string
|
||||
i := strings.LastIndex(copySourceHeader, "?versionId=")
|
||||
if i == -1 {
|
||||
copySource = copySourceHeader
|
||||
rawSource = copySourceHeader
|
||||
} else {
|
||||
copySource = copySourceHeader[:i]
|
||||
rawSource = copySourceHeader[:i]
|
||||
versionId = copySourceHeader[i+11:]
|
||||
}
|
||||
|
||||
srcBucket, srcObject, ok := strings.Cut(copySource, "/")
|
||||
if !ok {
|
||||
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket)
|
||||
// URL-decode the entire source path first so that clients that send the
|
||||
// bucket/key separator as "%2F" (e.g. AWS .NET SDK v4) are handled
|
||||
// correctly before we split on a literal '/'.
|
||||
decoded, err := url.QueryUnescape(rawSource)
|
||||
if err != nil {
|
||||
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding)
|
||||
}
|
||||
|
||||
var err error
|
||||
// URL-decode the bucket and object names to handle special characters
|
||||
srcBucket, err = url.QueryUnescape(srcBucket)
|
||||
if err != nil {
|
||||
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding)
|
||||
}
|
||||
srcObject, err = url.QueryUnescape(srcObject)
|
||||
if err != nil {
|
||||
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidCopySourceEncoding)
|
||||
srcBucket, srcObject, ok := strings.Cut(decoded, "/")
|
||||
if !ok {
|
||||
return "", "", "", s3err.GetAPIError(s3err.ErrInvalidCopySourceBucket)
|
||||
}
|
||||
|
||||
return srcBucket, srcObject, versionId, nil
|
||||
|
||||
@@ -87,6 +87,38 @@ func TestParseCopySource(t *testing.T) {
|
||||
wantVersionId: "xyz789",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "percent-encoded slash as bucket/key separator",
|
||||
copySourceHeader: "my-namespace-test-container%2Ftest-blob",
|
||||
wantBucket: "my-namespace-test-container",
|
||||
wantObject: "test-blob",
|
||||
wantVersionId: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "percent-encoded slash separator with leading slash",
|
||||
copySourceHeader: "/my-namespace-test-container%2Ftest-blob",
|
||||
wantBucket: "my-namespace-test-container",
|
||||
wantObject: "test-blob",
|
||||
wantVersionId: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "percent-encoded slash separator with versionId",
|
||||
copySourceHeader: "my-bucket%2Fmy-object?versionId=abc123",
|
||||
wantBucket: "my-bucket",
|
||||
wantObject: "my-object",
|
||||
wantVersionId: "abc123",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "percent-encoded slash separator with encoded object key",
|
||||
copySourceHeader: "my-bucket%2Fmy%20folder%2Fmy%20object",
|
||||
wantBucket: "my-bucket",
|
||||
wantObject: "my folder/my object",
|
||||
wantVersionId: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid URL encoding - incomplete escape",
|
||||
copySourceHeader: "mybucket/object%",
|
||||
|
||||
Reference in New Issue
Block a user