1de446f208019d90dc08fa24437ee63bbbc80667

Author
DongoDB <109906379+kyokugirl@users.noreply.github.com>
Committer
GitHub <noreply@github.com>
Date

Message

fix: prevent enumeration of private repo (#614)

Diff

  1diff --git a/pkg/ssh/cmd/branch.go b/pkg/ssh/cmd/branch.go
  2index daa0efaa0500e6a952ad5a078abf690624b61a6b..85474606ed8f285f12f96aa6520ceced1ee67c8c 100644
  3--- a/pkg/ssh/cmd/branch.go
  4+++ b/pkg/ssh/cmd/branch.go
  5@@ -61,18 +61,16 @@ func branchListCommand() *cobra.Command {
  6 
  7 func branchDefaultCommand() *cobra.Command {
  8 	cmd := &cobra.Command{
  9-		Use:   "default REPOSITORY [BRANCH]",
 10-		Short: "Set or get the default branch",
 11-		Args:  cobra.RangeArgs(1, 2),
 12+		Use:               "default REPOSITORY [BRANCH]",
 13+		Short:             "Set or get the default branch",
 14+		Args:              cobra.RangeArgs(1, 2),
 15+		PersistentPreRunE: checkIfReadable,
 16 		RunE: func(cmd *cobra.Command, args []string) error {
 17 			ctx := cmd.Context()
 18 			be := backend.FromContext(ctx)
 19 			rn := strings.TrimSuffix(args[0], ".git")
 20 			switch len(args) {
 21 			case 1:
 22-				if err := checkIfReadable(cmd, args); err != nil {
 23-					return err
 24-				}
 25 				rr, err := be.Repository(ctx, rn)
 26 				if err != nil {
 27 					return err
 28@@ -149,7 +147,7 @@ func branchDeleteCommand() *cobra.Command {
 29 		Aliases:           []string{"remove", "rm", "del"},
 30 		Short:             "Delete a branch",
 31 		Args:              cobra.ExactArgs(2),
 32-		PersistentPreRunE: checkIfCollab,
 33+		PersistentPreRunE: checkIfReadableAndCollab,
 34 		RunE: func(cmd *cobra.Command, args []string) error {
 35 			ctx := cmd.Context()
 36 			be := backend.FromContext(ctx)
 37diff --git a/pkg/ssh/cmd/cmd.go b/pkg/ssh/cmd/cmd.go
 38index 2d0b00c2538a7fa5006bd64a9f68d9df6820cc2c..18624eb333adb56ba7992c154c79d15fad20f43b 100644
 39--- a/pkg/ssh/cmd/cmd.go
 40+++ b/pkg/ssh/cmd/cmd.go
 41@@ -121,7 +121,7 @@ func checkIfReadable(cmd *cobra.Command, args []string) error {
 42 	user := proto.UserFromContext(ctx)
 43 	auth := be.AccessLevelForUser(cmd.Context(), rn, user)
 44 	if auth < access.ReadOnlyAccess {
 45-		return proto.ErrUnauthorized
 46+		return proto.ErrRepoNotFound
 47 	}
 48 	return nil
 49 }
 50@@ -185,3 +185,13 @@ func checkIfCollab(cmd *cobra.Command, args []string) error {
 51 	}
 52 	return nil
 53 }
 54+
 55+func checkIfReadableAndCollab(cmd *cobra.Command, args []string) error {
 56+	if err := checkIfReadable(cmd, args); err != nil {
 57+		return err
 58+	}
 59+	if err := checkIfCollab(cmd, args); err != nil {
 60+		return err
 61+	}
 62+	return nil
 63+}
 64diff --git a/pkg/ssh/cmd/collab.go b/pkg/ssh/cmd/collab.go
 65index bc4e22d810350fd35480075fdfb4d496ad85e22c..df1e059f5ee6820fb9e7774c733dcf9ca37ad7ef 100644
 66--- a/pkg/ssh/cmd/collab.go
 67+++ b/pkg/ssh/cmd/collab.go
 68@@ -28,7 +28,7 @@ func collabAddCommand() *cobra.Command {
 69 		Short:             "Add a collaborator to a repo",
 70 		Long:              "Add a collaborator to a repo. LEVEL can be one of: no-access, read-only, read-write, or admin-access. Defaults to read-write.",
 71 		Args:              cobra.RangeArgs(2, 3),
 72-		PersistentPreRunE: checkIfCollab,
 73+		PersistentPreRunE: checkIfReadableAndCollab,
 74 		RunE: func(cmd *cobra.Command, args []string) error {
 75 			ctx := cmd.Context()
 76 			be := backend.FromContext(ctx)
 77@@ -54,7 +54,7 @@ func collabRemoveCommand() *cobra.Command {
 78 		Use:               "remove REPOSITORY USERNAME",
 79 		Args:              cobra.ExactArgs(2),
 80 		Short:             "Remove a collaborator from a repo",
 81-		PersistentPreRunE: checkIfCollab,
 82+		PersistentPreRunE: checkIfReadableAndCollab,
 83 		RunE: func(cmd *cobra.Command, args []string) error {
 84 			ctx := cmd.Context()
 85 			be := backend.FromContext(ctx)
 86@@ -73,7 +73,7 @@ func collabListCommand() *cobra.Command {
 87 		Use:               "list REPOSITORY",
 88 		Short:             "List collaborators for a repo",
 89 		Args:              cobra.ExactArgs(1),
 90-		PersistentPreRunE: checkIfCollab,
 91+		PersistentPreRunE: checkIfReadableAndCollab,
 92 		RunE: func(cmd *cobra.Command, args []string) error {
 93 			ctx := cmd.Context()
 94 			be := backend.FromContext(ctx)
 95diff --git a/pkg/ssh/cmd/delete.go b/pkg/ssh/cmd/delete.go
 96index a1c25cb292b14de0e38ec3eb9a86ccf2a9300048..09fe3de6bf314859dcbbc336bb457e87bb61cc86 100644
 97--- a/pkg/ssh/cmd/delete.go
 98+++ b/pkg/ssh/cmd/delete.go
 99@@ -11,7 +11,7 @@ func deleteCommand() *cobra.Command {
100 		Aliases:           []string{"del", "remove", "rm"},
101 		Short:             "Delete a repository",
102 		Args:              cobra.ExactArgs(1),
103-		PersistentPreRunE: checkIfCollab,
104+		PersistentPreRunE: checkIfReadableAndCollab,
105 		RunE: func(cmd *cobra.Command, args []string) error {
106 			ctx := cmd.Context()
107 			be := backend.FromContext(ctx)
108diff --git a/pkg/ssh/cmd/description.go b/pkg/ssh/cmd/description.go
109index 9ca7998917df781300616e11bcc93f4f6bc09916..3e11c61f4e9d60e8725f8ce41cbbbef3f8a56726 100644
110--- a/pkg/ssh/cmd/description.go
111+++ b/pkg/ssh/cmd/description.go
112@@ -9,20 +9,17 @@ import (
113 
114 func descriptionCommand() *cobra.Command {
115 	cmd := &cobra.Command{
116-		Use:     "description REPOSITORY [DESCRIPTION]",
117-		Aliases: []string{"desc"},
118-		Short:   "Set or get the description for a repository",
119-		Args:    cobra.MinimumNArgs(1),
120+		Use:               "description REPOSITORY [DESCRIPTION]",
121+		Aliases:           []string{"desc"},
122+		Short:             "Set or get the description for a repository",
123+		Args:              cobra.MinimumNArgs(1),
124+		PersistentPreRunE: checkIfReadable,
125 		RunE: func(cmd *cobra.Command, args []string) error {
126 			ctx := cmd.Context()
127 			be := backend.FromContext(ctx)
128 			rn := strings.TrimSuffix(args[0], ".git")
129 			switch len(args) {
130 			case 1:
131-				if err := checkIfReadable(cmd, args); err != nil {
132-					return err
133-				}
134-
135 				desc, err := be.Description(ctx, rn)
136 				if err != nil {
137 					return err
138diff --git a/pkg/ssh/cmd/hidden.go b/pkg/ssh/cmd/hidden.go
139index 79441b4a70ee8430579fdb77a760d239f66dc323..82f544307681985935cd9f365a936bf43c096ceb 100644
140--- a/pkg/ssh/cmd/hidden.go
141+++ b/pkg/ssh/cmd/hidden.go
142@@ -7,20 +7,17 @@ import (
143 
144 func hiddenCommand() *cobra.Command {
145 	cmd := &cobra.Command{
146-		Use:     "hidden REPOSITORY [TRUE|FALSE]",
147-		Short:   "Hide or unhide a repository",
148-		Aliases: []string{"hide"},
149-		Args:    cobra.MinimumNArgs(1),
150+		Use:               "hidden REPOSITORY [TRUE|FALSE]",
151+		Short:             "Hide or unhide a repository",
152+		Aliases:           []string{"hide"},
153+		Args:              cobra.MinimumNArgs(1),
154+		PersistentPreRunE: checkIfReadable,
155 		RunE: func(cmd *cobra.Command, args []string) error {
156 			ctx := cmd.Context()
157 			be := backend.FromContext(ctx)
158 			repo := args[0]
159 			switch len(args) {
160 			case 1:
161-				if err := checkIfReadable(cmd, args); err != nil {
162-					return err
163-				}
164-
165 				hidden, err := be.IsHidden(ctx, repo)
166 				if err != nil {
167 					return err
168diff --git a/pkg/ssh/cmd/private.go b/pkg/ssh/cmd/private.go
169index 11340a6f009cd0dc54a5cb82672c6f17c458f358..9c91dbcda1e8d89f763cbe4e13300848e7389466 100644
170--- a/pkg/ssh/cmd/private.go
171+++ b/pkg/ssh/cmd/private.go
172@@ -10,9 +10,10 @@ import (
173 
174 func privateCommand() *cobra.Command {
175 	cmd := &cobra.Command{
176-		Use:   "private REPOSITORY [true|false]",
177-		Short: "Set or get a repository private property",
178-		Args:  cobra.RangeArgs(1, 2),
179+		Use:               "private REPOSITORY [true|false]",
180+		Short:             "Set or get a repository private property",
181+		Args:              cobra.RangeArgs(1, 2),
182+		PersistentPreRunE: checkIfReadable,
183 		RunE: func(cmd *cobra.Command, args []string) error {
184 			ctx := cmd.Context()
185 			be := backend.FromContext(ctx)
186@@ -20,10 +21,6 @@ func privateCommand() *cobra.Command {
187 
188 			switch len(args) {
189 			case 1:
190-				if err := checkIfReadable(cmd, args); err != nil {
191-					return err
192-				}
193-
194 				isPrivate, err := be.IsPrivate(ctx, rn)
195 				if err != nil {
196 					return err
197diff --git a/pkg/ssh/cmd/project_name.go b/pkg/ssh/cmd/project_name.go
198index 469a2e16dc923127c1bff7edbd31ccaf9ccc9c89..d24931f5dcf23af7d8656d5d41c37fbe7f901d37 100644
199--- a/pkg/ssh/cmd/project_name.go
200+++ b/pkg/ssh/cmd/project_name.go
201@@ -9,20 +9,17 @@ import (
202 
203 func projectName() *cobra.Command {
204 	cmd := &cobra.Command{
205-		Use:     "project-name REPOSITORY [NAME]",
206-		Aliases: []string{"project"},
207-		Short:   "Set or get the project name for a repository",
208-		Args:    cobra.MinimumNArgs(1),
209+		Use:               "project-name REPOSITORY [NAME]",
210+		Aliases:           []string{"project"},
211+		Short:             "Set or get the project name for a repository",
212+		Args:              cobra.MinimumNArgs(1),
213+		PersistentPreRunE: checkIfReadable,
214 		RunE: func(cmd *cobra.Command, args []string) error {
215 			ctx := cmd.Context()
216 			be := backend.FromContext(ctx)
217 			rn := strings.TrimSuffix(args[0], ".git")
218 			switch len(args) {
219 			case 1:
220-				if err := checkIfReadable(cmd, args); err != nil {
221-					return err
222-				}
223-
224 				pn, err := be.ProjectName(ctx, rn)
225 				if err != nil {
226 					return err
227diff --git a/pkg/ssh/cmd/rename.go b/pkg/ssh/cmd/rename.go
228index 77cdc74e70b6dcda5044bc90467ae928b1f0f520..fe74a293bc64fb4d34de02b51f3ce7b5e540d6a7 100644
229--- a/pkg/ssh/cmd/rename.go
230+++ b/pkg/ssh/cmd/rename.go
231@@ -11,7 +11,7 @@ func renameCommand() *cobra.Command {
232 		Aliases:           []string{"mv", "move"},
233 		Short:             "Rename an existing repository",
234 		Args:              cobra.ExactArgs(2),
235-		PersistentPreRunE: checkIfCollab,
236+		PersistentPreRunE: checkIfReadableAndCollab,
237 		RunE: func(cmd *cobra.Command, args []string) error {
238 			ctx := cmd.Context()
239 			be := backend.FromContext(ctx)
240diff --git a/pkg/ssh/cmd/tag.go b/pkg/ssh/cmd/tag.go
241index 811de2316afe127e8edb52689e52207b60a72f0c..009ac03083a90c6809b61d2c904ae398a59572fe 100644
242--- a/pkg/ssh/cmd/tag.go
243+++ b/pkg/ssh/cmd/tag.go
244@@ -64,7 +64,7 @@ func tagDeleteCommand() *cobra.Command {
245 		Aliases:           []string{"remove", "rm", "del"},
246 		Short:             "Delete a tag",
247 		Args:              cobra.ExactArgs(2),
248-		PersistentPreRunE: checkIfCollab,
249+		PersistentPreRunE: checkIfReadableAndCollab,
250 		RunE: func(cmd *cobra.Command, args []string) error {
251 			ctx := cmd.Context()
252 			be := backend.FromContext(ctx)
253diff --git a/testscript/testdata/repo-perms.txtar b/testscript/testdata/repo-perms.txtar
254index a3e4515c066791dfa2998d0b7e6186c70edba352..b21c0c3ff08965565a6e4eedadd1d7048fc84344 100644
255--- a/testscript/testdata/repo-perms.txtar
256+++ b/testscript/testdata/repo-perms.txtar
257@@ -36,33 +36,33 @@ soft repo collab list repo1
258 
259 # regular user can't access it
260 ! usoft repo info repo1
261-stderr 'unauthorized'
262+stderr 'repository not found'
263 ! usoft repo tree repo1
264-stderr 'unauthorized'
265+stderr 'repository not found'
266 ! usoft repo tag list repo1
267-stderr 'unauthorized'
268+stderr 'repository not found'
269 ! usoft repo tag delete repo1 v1.0.0
270-stderr 'unauthorized'
271+stderr 'repository not found'
272 ! usoft repo blob repo1 README.md
273-stderr 'unauthorized'
274+stderr 'repository not found'
275 ! usoft repo description repo1
276-stderr 'unauthorized'
277+stderr 'repository not found'
278 ! usoft repo description repo1 'new desc'
279-stderr 'unauthorized'
280+stderr 'repository not found'
281 ! usoft repo project-name repo1
282-stderr 'unauthorized'
283+stderr 'repository not found'
284 ! usoft repo private repo1 true
285-stderr 'unauthorized'
286+stderr 'repository not found'
287 ! usoft repo private repo1
288-stderr 'unauthorized'
289+stderr 'repository not found'
290 ! usoft repo rename repo1 repo11
291-stderr 'unauthorized'
292+stderr 'repository not found'
293 ! usoft repo branch default repo1
294-stderr 'unauthorized'
295+stderr 'repository not found'
296 ! usoft repo branch default repo1 main
297-stderr 'unauthorized'
298+stderr 'repository not found'
299 ! usoft repo delete repo1
300-stderr 'unauthorized'
301+stderr 'repository not found'
302 
303 # add user1 as collab
304 ! soft repo collab add repo1 user1 foobar