From 1de446f208019d90dc08fa24437ee63bbbc80667 Mon Sep 17 00:00:00 2001 From: DongoDB <109906379+kyokugirl@users.noreply.github.com> Date: Mon, 9 Dec 2024 05:31:58 +0900 Subject: [PATCH] fix: prevent enumeration of private repo (#614) --- pkg/ssh/cmd/branch.go | 12 +++++------- pkg/ssh/cmd/cmd.go | 12 +++++++++++- pkg/ssh/cmd/collab.go | 6 +++--- pkg/ssh/cmd/delete.go | 2 +- pkg/ssh/cmd/description.go | 13 +++++-------- pkg/ssh/cmd/hidden.go | 13 +++++-------- pkg/ssh/cmd/private.go | 11 ++++------- pkg/ssh/cmd/project_name.go | 13 +++++-------- pkg/ssh/cmd/rename.go | 2 +- pkg/ssh/cmd/tag.go | 2 +- testscript/testdata/repo-perms.txtar | 28 ++++++++++++++-------------- 11 files changed, 55 insertions(+), 59 deletions(-) diff --git a/pkg/ssh/cmd/branch.go b/pkg/ssh/cmd/branch.go index daa0efaa0500e6a952ad5a078abf690624b61a6b..85474606ed8f285f12f96aa6520ceced1ee67c8c 100644 --- a/pkg/ssh/cmd/branch.go +++ b/pkg/ssh/cmd/branch.go @@ -61,18 +61,16 @@ func branchListCommand() *cobra.Command { func branchDefaultCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "default REPOSITORY [BRANCH]", - Short: "Set or get the default branch", - Args: cobra.RangeArgs(1, 2), + Use: "default REPOSITORY [BRANCH]", + Short: "Set or get the default branch", + Args: cobra.RangeArgs(1, 2), + PersistentPreRunE: checkIfReadable, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) rn := strings.TrimSuffix(args[0], ".git") switch len(args) { case 1: - if err := checkIfReadable(cmd, args); err != nil { - return err - } rr, err := be.Repository(ctx, rn) if err != nil { return err @@ -149,7 +147,7 @@ func branchDeleteCommand() *cobra.Command { Aliases: []string{"remove", "rm", "del"}, Short: "Delete a branch", Args: cobra.ExactArgs(2), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) diff --git a/pkg/ssh/cmd/cmd.go b/pkg/ssh/cmd/cmd.go index 2d0b00c2538a7fa5006bd64a9f68d9df6820cc2c..18624eb333adb56ba7992c154c79d15fad20f43b 100644 --- a/pkg/ssh/cmd/cmd.go +++ b/pkg/ssh/cmd/cmd.go @@ -121,7 +121,7 @@ func checkIfReadable(cmd *cobra.Command, args []string) error { user := proto.UserFromContext(ctx) auth := be.AccessLevelForUser(cmd.Context(), rn, user) if auth < access.ReadOnlyAccess { - return proto.ErrUnauthorized + return proto.ErrRepoNotFound } return nil } @@ -185,3 +185,13 @@ func checkIfCollab(cmd *cobra.Command, args []string) error { } return nil } + +func checkIfReadableAndCollab(cmd *cobra.Command, args []string) error { + if err := checkIfReadable(cmd, args); err != nil { + return err + } + if err := checkIfCollab(cmd, args); err != nil { + return err + } + return nil +} diff --git a/pkg/ssh/cmd/collab.go b/pkg/ssh/cmd/collab.go index bc4e22d810350fd35480075fdfb4d496ad85e22c..df1e059f5ee6820fb9e7774c733dcf9ca37ad7ef 100644 --- a/pkg/ssh/cmd/collab.go +++ b/pkg/ssh/cmd/collab.go @@ -28,7 +28,7 @@ func collabAddCommand() *cobra.Command { Short: "Add a collaborator to a repo", 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.", Args: cobra.RangeArgs(2, 3), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) @@ -54,7 +54,7 @@ func collabRemoveCommand() *cobra.Command { Use: "remove REPOSITORY USERNAME", Args: cobra.ExactArgs(2), Short: "Remove a collaborator from a repo", - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) @@ -73,7 +73,7 @@ func collabListCommand() *cobra.Command { Use: "list REPOSITORY", Short: "List collaborators for a repo", Args: cobra.ExactArgs(1), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) diff --git a/pkg/ssh/cmd/delete.go b/pkg/ssh/cmd/delete.go index a1c25cb292b14de0e38ec3eb9a86ccf2a9300048..09fe3de6bf314859dcbbc336bb457e87bb61cc86 100644 --- a/pkg/ssh/cmd/delete.go +++ b/pkg/ssh/cmd/delete.go @@ -11,7 +11,7 @@ func deleteCommand() *cobra.Command { Aliases: []string{"del", "remove", "rm"}, Short: "Delete a repository", Args: cobra.ExactArgs(1), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) diff --git a/pkg/ssh/cmd/description.go b/pkg/ssh/cmd/description.go index 9ca7998917df781300616e11bcc93f4f6bc09916..3e11c61f4e9d60e8725f8ce41cbbbef3f8a56726 100644 --- a/pkg/ssh/cmd/description.go +++ b/pkg/ssh/cmd/description.go @@ -9,20 +9,17 @@ import ( func descriptionCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "description REPOSITORY [DESCRIPTION]", - Aliases: []string{"desc"}, - Short: "Set or get the description for a repository", - Args: cobra.MinimumNArgs(1), + Use: "description REPOSITORY [DESCRIPTION]", + Aliases: []string{"desc"}, + Short: "Set or get the description for a repository", + Args: cobra.MinimumNArgs(1), + PersistentPreRunE: checkIfReadable, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) rn := strings.TrimSuffix(args[0], ".git") switch len(args) { case 1: - if err := checkIfReadable(cmd, args); err != nil { - return err - } - desc, err := be.Description(ctx, rn) if err != nil { return err diff --git a/pkg/ssh/cmd/hidden.go b/pkg/ssh/cmd/hidden.go index 79441b4a70ee8430579fdb77a760d239f66dc323..82f544307681985935cd9f365a936bf43c096ceb 100644 --- a/pkg/ssh/cmd/hidden.go +++ b/pkg/ssh/cmd/hidden.go @@ -7,20 +7,17 @@ import ( func hiddenCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "hidden REPOSITORY [TRUE|FALSE]", - Short: "Hide or unhide a repository", - Aliases: []string{"hide"}, - Args: cobra.MinimumNArgs(1), + Use: "hidden REPOSITORY [TRUE|FALSE]", + Short: "Hide or unhide a repository", + Aliases: []string{"hide"}, + Args: cobra.MinimumNArgs(1), + PersistentPreRunE: checkIfReadable, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) repo := args[0] switch len(args) { case 1: - if err := checkIfReadable(cmd, args); err != nil { - return err - } - hidden, err := be.IsHidden(ctx, repo) if err != nil { return err diff --git a/pkg/ssh/cmd/private.go b/pkg/ssh/cmd/private.go index 11340a6f009cd0dc54a5cb82672c6f17c458f358..9c91dbcda1e8d89f763cbe4e13300848e7389466 100644 --- a/pkg/ssh/cmd/private.go +++ b/pkg/ssh/cmd/private.go @@ -10,9 +10,10 @@ import ( func privateCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "private REPOSITORY [true|false]", - Short: "Set or get a repository private property", - Args: cobra.RangeArgs(1, 2), + Use: "private REPOSITORY [true|false]", + Short: "Set or get a repository private property", + Args: cobra.RangeArgs(1, 2), + PersistentPreRunE: checkIfReadable, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) @@ -20,10 +21,6 @@ func privateCommand() *cobra.Command { switch len(args) { case 1: - if err := checkIfReadable(cmd, args); err != nil { - return err - } - isPrivate, err := be.IsPrivate(ctx, rn) if err != nil { return err diff --git a/pkg/ssh/cmd/project_name.go b/pkg/ssh/cmd/project_name.go index 469a2e16dc923127c1bff7edbd31ccaf9ccc9c89..d24931f5dcf23af7d8656d5d41c37fbe7f901d37 100644 --- a/pkg/ssh/cmd/project_name.go +++ b/pkg/ssh/cmd/project_name.go @@ -9,20 +9,17 @@ import ( func projectName() *cobra.Command { cmd := &cobra.Command{ - Use: "project-name REPOSITORY [NAME]", - Aliases: []string{"project"}, - Short: "Set or get the project name for a repository", - Args: cobra.MinimumNArgs(1), + Use: "project-name REPOSITORY [NAME]", + Aliases: []string{"project"}, + Short: "Set or get the project name for a repository", + Args: cobra.MinimumNArgs(1), + PersistentPreRunE: checkIfReadable, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) rn := strings.TrimSuffix(args[0], ".git") switch len(args) { case 1: - if err := checkIfReadable(cmd, args); err != nil { - return err - } - pn, err := be.ProjectName(ctx, rn) if err != nil { return err diff --git a/pkg/ssh/cmd/rename.go b/pkg/ssh/cmd/rename.go index 77cdc74e70b6dcda5044bc90467ae928b1f0f520..fe74a293bc64fb4d34de02b51f3ce7b5e540d6a7 100644 --- a/pkg/ssh/cmd/rename.go +++ b/pkg/ssh/cmd/rename.go @@ -11,7 +11,7 @@ func renameCommand() *cobra.Command { Aliases: []string{"mv", "move"}, Short: "Rename an existing repository", Args: cobra.ExactArgs(2), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) diff --git a/pkg/ssh/cmd/tag.go b/pkg/ssh/cmd/tag.go index 811de2316afe127e8edb52689e52207b60a72f0c..009ac03083a90c6809b61d2c904ae398a59572fe 100644 --- a/pkg/ssh/cmd/tag.go +++ b/pkg/ssh/cmd/tag.go @@ -64,7 +64,7 @@ func tagDeleteCommand() *cobra.Command { Aliases: []string{"remove", "rm", "del"}, Short: "Delete a tag", Args: cobra.ExactArgs(2), - PersistentPreRunE: checkIfCollab, + PersistentPreRunE: checkIfReadableAndCollab, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() be := backend.FromContext(ctx) diff --git a/testscript/testdata/repo-perms.txtar b/testscript/testdata/repo-perms.txtar index a3e4515c066791dfa2998d0b7e6186c70edba352..b21c0c3ff08965565a6e4eedadd1d7048fc84344 100644 --- a/testscript/testdata/repo-perms.txtar +++ b/testscript/testdata/repo-perms.txtar @@ -36,33 +36,33 @@ soft repo collab list repo1 # regular user can't access it ! usoft repo info repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo tree repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo tag list repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo tag delete repo1 v1.0.0 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo blob repo1 README.md -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo description repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo description repo1 'new desc' -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo project-name repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo private repo1 true -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo private repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo rename repo1 repo11 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo branch default repo1 -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo branch default repo1 main -stderr 'unauthorized' +stderr 'repository not found' ! usoft repo delete repo1 -stderr 'unauthorized' +stderr 'repository not found' # add user1 as collab ! soft repo collab add repo1 user1 foobar