From 7eb9713a67a510fb38e283f504566f30663b329a Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 6 Aug 2021 16:05:57 +0300 Subject: [PATCH] [#189] Add bucket name checking Signed-off-by: Denis Kirillov --- api/handler/put.go | 40 ++++++++++++++++++++++++++++++++++++ api/handler/put_test.go | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 api/handler/put_test.go diff --git a/api/handler/put.go b/api/handler/put.go index 77da94fb..d8e8d19f 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -3,6 +3,7 @@ package handler import ( "encoding/xml" "fmt" + "net" "net/http" "strconv" "strings" @@ -73,6 +74,11 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { p = layer.CreateBucketParams{Name: reqInfo.BucketName} ) + if err = checkBucketName(reqInfo.BucketName); err != nil { + h.logAndSendError(w, "invalid bucket name", reqInfo, err) + return + } + if val, ok := r.Header["X-Amz-Acl"]; ok { p.ACL, err = parseBasicACL(val[0]) } else { @@ -124,6 +130,40 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteSuccessResponseHeadersOnly(w) } +func checkBucketName(bucketName string) error { + if len(bucketName) < 3 || len(bucketName) > 63 { + return api.GetAPIError(api.ErrInvalidBucketName) + } + + if strings.HasPrefix(bucketName, "xn--") || strings.HasSuffix(bucketName, "-s3alias") { + return api.GetAPIError(api.ErrInvalidBucketName) + } + if net.ParseIP(bucketName) != nil { + return api.GetAPIError(api.ErrInvalidBucketName) + } + + labels := strings.Split(bucketName, ".") + for _, label := range labels { + if len(label) == 0 { + return api.GetAPIError(api.ErrInvalidBucketName) + } + for i, r := range label { + if !isAlphaNum(r) && r != '-' { + return api.GetAPIError(api.ErrInvalidBucketName) + } + if (i == 0 || i == len(label)-1) && r == '-' { + return api.GetAPIError(api.ErrInvalidBucketName) + } + } + } + + return nil +} + +func isAlphaNum(char int32) bool { + return 'a' <= char && char <= 'z' || '0' <= char && char <= '9' +} + func parseLocationConstraint(r *http.Request) (*createBucketParams, error) { if r.ContentLength == 0 { return new(createBucketParams), nil diff --git a/api/handler/put_test.go b/api/handler/put_test.go new file mode 100644 index 00000000..65ea8a07 --- /dev/null +++ b/api/handler/put_test.go @@ -0,0 +1,45 @@ +package handler + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCheckBucketName(t *testing.T) { + for _, tc := range []struct { + name string + err bool + }{ + {name: "bucket"}, + {name: "2bucket"}, + {name: "buc.ket"}, + {name: "buc-ket"}, + {name: "abc"}, + {name: "63aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, + {name: "buc.-ket", err: true}, + {name: "bucket.", err: true}, + {name: ".bucket", err: true}, + {name: "bucket.", err: true}, + {name: "bucket-", err: true}, + {name: "-bucket", err: true}, + {name: "Bucket", err: true}, + {name: "buc.-ket", err: true}, + {name: "buc-.ket", err: true}, + {name: "Bucket", err: true}, + {name: "buc!ket", err: true}, + {name: "buc_ket", err: true}, + {name: "xn--bucket", err: true}, + {name: "bucket-s3alias", err: true}, + {name: "192.168.0.1", err: true}, + {name: "as", err: true}, + {name: "64aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", err: true}, + } { + err := checkBucketName(tc.name) + if tc.err { + require.Error(t, err, "bucket name: %s", tc.name) + } else { + require.NoError(t, err, "bucket name: %s", tc.name) + } + } +}