diff --git a/changelogs/unreleased/get-archive-exclude-relative.yml b/changelogs/unreleased/get-archive-exclude-relative.yml new file mode 100644 index 0000000000000000000000000000000000000000..d4d8a94f8b10aec87068b5cf1b0bfab1f22c1a4e --- /dev/null +++ b/changelogs/unreleased/get-archive-exclude-relative.yml @@ -0,0 +1,5 @@ +--- +title: 'GetArchive: make exclude relative to path' +merge_request: 2363 +author: Ethan Reesor (@firelizzard) +type: changed diff --git a/internal/service/repository/archive.go b/internal/service/repository/archive.go index 354653126663d13ddf6b096d55347960a59f4689..daff70da474288e56afc31e6bd47903acbccde50 100644 --- a/internal/service/repository/archive.go +++ b/internal/service/repository/archive.go @@ -4,6 +4,7 @@ import ( "context" "io" "os/exec" + "path/filepath" "gitlab.com/gitlab-org/gitaly/internal/command" "gitlab.com/gitlab-org/gitaly/internal/git" @@ -31,7 +32,7 @@ func (s *server) GetArchive(in *gitalypb.GetArchiveRequest, stream gitalypb.Repo exclude := make([]string, len(in.GetExclude())) for i, ex := range in.GetExclude() { - exclude[i], err = storage.ValidateRelativePath(repoRoot, string(ex)) + exclude[i], err = storage.ValidateRelativePath(repoRoot, filepath.Join(string(in.GetPath()), string(ex))) if err != nil { return helper.ErrInvalidArgument(err) } diff --git a/internal/service/repository/archive_test.go b/internal/service/repository/archive_test.go index 87436d4a5d1e0eb8b712a9ba6cad009980b62883..a91c5963191d49c1a48ed6b6381b3c188f5b33d4 100644 --- a/internal/service/repository/archive_test.go +++ b/internal/service/repository/archive_test.go @@ -89,6 +89,15 @@ func TestGetArchiveSuccess(t *testing.T) { contents: []string{"/.gitignore", "/LICENSE", "/README.md"}, excluded: []string{"/files/whitespace", "/files/html/500.html"}, }, + { + desc: "with path and exclusion", + commitID: "1e292f8fedd741b75372e19097c76d327140c312", + prefix: "", + path: []byte("files/"), + exclude: [][]byte{[]byte("html/")}, + contents: []string{"/files/whitespace"}, + excluded: []string{"/files/html/500.html"}, + }, } for _, tc := range testCases { @@ -215,6 +224,16 @@ func TestGetArchiveFailure(t *testing.T) { exclude: [][]byte{[]byte("files/")}, code: codes.FailedPrecondition, }, + { + desc: "Rooted exclude when path is specified", + repo: testRepo, + prefix: "", + commitID: "1e292f8fedd741b75372e19097c76d327140c312", + format: gitalypb.GetArchiveRequest_ZIP, + path: []byte("files/"), + exclude: [][]byte{[]byte("files/html")}, + code: codes.FailedPrecondition, + }, { desc: "path contains directory traversal outside repository root", repo: testRepo, diff --git a/proto/go/gitalypb/repository-service.pb.go b/proto/go/gitalypb/repository-service.pb.go index 0af7c9ef8ad616394d8c69693301e9d49e5c8ca2..632cb32f25e521623d20ecaa1e43b1dbc5f0136e 100644 --- a/proto/go/gitalypb/repository-service.pb.go +++ b/proto/go/gitalypb/repository-service.pb.go @@ -901,15 +901,17 @@ func (m *CreateRepositoryResponse) XXX_DiscardUnknown() { var xxx_messageInfo_CreateRepositoryResponse proto.InternalMessageInfo type GetArchiveRequest struct { - Repository *Repository `protobuf:"bytes,1,opt,name=repository,proto3" json:"repository,omitempty"` - CommitId string `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id,omitempty"` - Prefix string `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix,omitempty"` - Format GetArchiveRequest_Format `protobuf:"varint,4,opt,name=format,proto3,enum=gitaly.GetArchiveRequest_Format" json:"format,omitempty"` - Path []byte `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` - Exclude [][]byte `protobuf:"bytes,6,rep,name=exclude,proto3" json:"exclude,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Repository *Repository `protobuf:"bytes,1,opt,name=repository,proto3" json:"repository,omitempty"` + CommitId string `protobuf:"bytes,2,opt,name=commit_id,json=commitId,proto3" json:"commit_id,omitempty"` + Prefix string `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix,omitempty"` + Format GetArchiveRequest_Format `protobuf:"varint,4,opt,name=format,proto3,enum=gitaly.GetArchiveRequest_Format" json:"format,omitempty"` + Path []byte `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` + // Excluded paths must be relative to the base path. For example, to exclude + // 'a/b' when `path` is 'a/', `exclude` must be ['b/']. + Exclude [][]byte `protobuf:"bytes,6,rep,name=exclude,proto3" json:"exclude,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *GetArchiveRequest) Reset() { *m = GetArchiveRequest{} } diff --git a/proto/repository-service.proto b/proto/repository-service.proto index 5138dfe6cbb0a53ce9608b4b3eca6cfc7bd87ac4..b56b4078d658f7c24d5cfc448a005c6ba09df48c 100644 --- a/proto/repository-service.proto +++ b/proto/repository-service.proto @@ -311,6 +311,8 @@ message GetArchiveRequest { string prefix = 3; Format format = 4; bytes path = 5; + // Excluded paths must be relative to the base path. For example, to exclude + // 'a/b' when `path` is 'a/', `exclude` must be ['b/']. repeated bytes exclude = 6; }