Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2019-08-26 22:11:30 +0300
committerJohn Cai <jcai@gitlab.com>2019-09-03 18:43:16 +0300
commit2f75855225ca33d255cdff7c9030bc35c671ef78 (patch)
treed4c4230f125ac4258bf0dede3cff2ec2e297479d
parent56a370623544ef831ee9b4d5191a18151a2dfdee (diff)
Guard against path traversal in AddNamespace RPCjc-guard-path-traversal
-rw-r--r--changelogs/unreleased/jc-add-mkdirp-to-rename-namespace.yml5
-rw-r--r--internal/service/namespace/namespace.go44
-rw-r--r--internal/service/namespace/namespace_test.go70
3 files changed, 110 insertions, 9 deletions
diff --git a/changelogs/unreleased/jc-add-mkdirp-to-rename-namespace.yml b/changelogs/unreleased/jc-add-mkdirp-to-rename-namespace.yml
new file mode 100644
index 000000000..8c63f0615
--- /dev/null
+++ b/changelogs/unreleased/jc-add-mkdirp-to-rename-namespace.yml
@@ -0,0 +1,5 @@
+---
+title: Create parent directory in RenameNamespace
+merge_request: 1452
+author:
+type: other
diff --git a/internal/service/namespace/namespace.go b/internal/service/namespace/namespace.go
index 005c03655..909d310f5 100644
--- a/internal/service/namespace/namespace.go
+++ b/internal/service/namespace/namespace.go
@@ -2,8 +2,11 @@ package namespace
import (
"context"
+ "errors"
+ "fmt"
"os"
"path"
+ "path/filepath"
"gitlab.com/gitlab-org/gitaly/internal/helper"
"gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
@@ -52,26 +55,49 @@ func (s *server) AddNamespace(ctx context.Context, in *gitalypb.AddNamespaceRequ
return &gitalypb.AddNamespaceResponse{}, nil
}
-func (s *server) RenameNamespace(ctx context.Context, in *gitalypb.RenameNamespaceRequest) (*gitalypb.RenameNamespaceResponse, error) {
- storagePath, err := helper.GetStorageByName(in.GetStorageName())
- if err != nil {
- return nil, err
+func (s *server) validateRenameNamespaceRequest(ctx context.Context, in *gitalypb.RenameNamespaceRequest) error {
+ if in.GetFrom() == "" || in.GetTo() == "" {
+ return errors.New("from and to cannot be empty")
}
- if in.GetFrom() == "" || in.GetTo() == "" {
- return nil, status.Errorf(codes.InvalidArgument, "from and to cannot be empty")
+ if helper.ContainsPathTraversal(in.To) {
+ return errors.New("destination cannot contain path traversal")
+ }
+
+ if helper.ContainsPathTraversal(in.From) {
+ return errors.New("source cannot contain path traversal")
}
// No need to check if the from path exists, if it doesn't, we'd later get an
// os.LinkError
toExistsCheck := &gitalypb.NamespaceExistsRequest{StorageName: in.StorageName, Name: in.GetTo()}
if exists, err := s.NamespaceExists(ctx, toExistsCheck); err != nil {
- return nil, err
+ return err
} else if exists.Exists {
- return nil, status.Errorf(codes.InvalidArgument, "to directory %s already exists", in.GetTo())
+ return fmt.Errorf("to directory %s already exists", in.GetTo())
+ }
+
+ return nil
+}
+
+func (s *server) RenameNamespace(ctx context.Context, in *gitalypb.RenameNamespaceRequest) (*gitalypb.RenameNamespaceResponse, error) {
+ if err := s.validateRenameNamespaceRequest(ctx, in); err != nil {
+ return nil, helper.ErrInvalidArgument(err)
+ }
+
+ storagePath, err := helper.GetStorageByName(in.GetStorageName())
+ if err != nil {
+ return nil, err
+ }
+
+ targetPath := namespacePath(storagePath, in.GetTo())
+
+ // Create the parent directory.
+ if err = os.MkdirAll(filepath.Dir(targetPath), 0775); err != nil {
+ return nil, helper.ErrInternalf("create directory: %v", err)
}
- err = os.Rename(namespacePath(storagePath, in.GetFrom()), namespacePath(storagePath, in.GetTo()))
+ err = os.Rename(namespacePath(storagePath, in.GetFrom()), targetPath)
if _, ok := err.(*os.LinkError); ok {
return nil, status.Errorf(codes.InvalidArgument, "from directory %s not found", in.GetFrom())
} else if err != nil {
diff --git a/internal/service/namespace/namespace_test.go b/internal/service/namespace/namespace_test.go
index f21dc0174..6ae48b298 100644
--- a/internal/service/namespace/namespace_test.go
+++ b/internal/service/namespace/namespace_test.go
@@ -297,3 +297,73 @@ func TestRenameNamespace(t *testing.T) {
})
}
}
+
+func TestRenameNamespaceWithNonexistentParentDir(t *testing.T) {
+ server, serverSocketPath := runNamespaceServer(t)
+ defer server.Stop()
+
+ client, conn := newNamespaceClient(t, serverSocketPath)
+ defer conn.Close()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ _, err := client.AddNamespace(ctx, &gitalypb.AddNamespaceRequest{
+ StorageName: "default",
+ Name: "existing",
+ })
+ require.NoError(t, err)
+
+ testCases := []struct {
+ desc string
+ request *gitalypb.RenameNamespaceRequest
+ errorCode codes.Code
+ }{
+ {
+ desc: "existing source, non existing target directory",
+ request: &gitalypb.RenameNamespaceRequest{
+ From: "existing",
+ To: "some/other/new-path",
+ StorageName: "default",
+ },
+ errorCode: codes.OK,
+ },
+ {
+ desc: "path traversal in source",
+ request: &gitalypb.RenameNamespaceRequest{
+ From: "../some/traversed/path",
+ To: "some/other/new-path",
+ StorageName: "default",
+ },
+ errorCode: codes.InvalidArgument,
+ },
+ {
+ desc: "path traversal in destination",
+ request: &gitalypb.RenameNamespaceRequest{
+ From: "existing",
+ To: "../some/traversed/path",
+ StorageName: "default",
+ },
+ errorCode: codes.InvalidArgument,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+
+ _, err = client.RenameNamespace(ctx, &gitalypb.RenameNamespaceRequest{
+ From: "existing",
+ To: "some/other/new-path",
+ StorageName: "default"})
+ require.Equal(t, tc.errorCode, helper.GrpcCode(err))
+
+ if tc.errorCode == codes.OK {
+ storagePath, err := helper.GetStorageByName(tc.request.StorageName)
+ require.NoError(t, err)
+
+ path := namespacePath(storagePath, tc.request.GetTo())
+ require.NoError(t, os.RemoveAll(path))
+ }
+ })
+ }
+}