From 18d598959dbf8a192efab935b989a6a8bb9790d0 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Thu, 17 Nov 2022 12:02:01 -0500 Subject: [PATCH] fix(session): unauthorized access to private repos Prevent unauthorized ui access to private repos. Fixes: https://github.com/charmbracelet/soft-serve/issues/189 --- server/server_test.go | 4 ++- server/session.go | 8 +++++ server/session_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 server/session_test.go diff --git a/server/server_test.go b/server/server_test.go index d7dcf9b8b21cedaba940b88c709c172b94bd9bfc..6f126ce9bbd81b60f608d936f05996d57914339f 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -99,7 +99,9 @@ func setupServer(t *testing.T) *Server { go func() { s.Start() }() - defer s.Close() + t.Cleanup(func() { + s.Close() + }) return s } diff --git a/server/session.go b/server/session.go index 96aeba4a919ecf6afa290219cb4efdd74b2da904..a645c09c97948ba19ddcd231e427b241f2b44875 100644 --- a/server/session.go +++ b/server/session.go @@ -6,11 +6,14 @@ import ( "github.com/aymanbagabas/go-osc52" tea "github.com/charmbracelet/bubbletea" appCfg "github.com/charmbracelet/soft-serve/config" + cm "github.com/charmbracelet/soft-serve/server/cmd" "github.com/charmbracelet/soft-serve/ui" "github.com/charmbracelet/soft-serve/ui/common" "github.com/charmbracelet/soft-serve/ui/keymap" "github.com/charmbracelet/soft-serve/ui/styles" + "github.com/charmbracelet/wish" bm "github.com/charmbracelet/wish/bubbletea" + gm "github.com/charmbracelet/wish/git" "github.com/gliderlabs/ssh" zone "github.com/lrstanley/bubblezone" ) @@ -26,6 +29,11 @@ func SessionHandler(ac *appCfg.Config) bm.ProgramHandler { initialRepo := "" if len(cmd) == 1 { initialRepo = cmd[0] + auth := ac.AuthRepo(initialRepo, s.PublicKey()) + if auth < gm.ReadOnlyAccess { + wish.Fatalln(s, cm.ErrUnauthorized) + return nil + } } if ac.Cfg.Callbacks != nil { ac.Cfg.Callbacks.Tui("new session") diff --git a/server/session_test.go b/server/session_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3257be89c852af1612b7b8a8349fed8e2c4a0001 --- /dev/null +++ b/server/session_test.go @@ -0,0 +1,77 @@ +package server + +import ( + "bytes" + "errors" + "os" + "strings" + "testing" + "time" + + appCfg "github.com/charmbracelet/soft-serve/config" + cm "github.com/charmbracelet/soft-serve/server/cmd" + "github.com/charmbracelet/soft-serve/server/config" + bm "github.com/charmbracelet/wish/bubbletea" + "github.com/charmbracelet/wish/testsession" + "github.com/gliderlabs/ssh" + "github.com/matryer/is" + "github.com/muesli/termenv" + gossh "golang.org/x/crypto/ssh" +) + +func TestSession(t *testing.T) { + is := is.New(t) + t.Run("unauthorized repo access", func(t *testing.T) { + var out bytes.Buffer + s := setup(t) + s.Stderr = &out + defer s.Close() + err := s.RequestPty("xterm", 80, 40, nil) + is.NoErr(err) + err = s.Run("config") + // Session writes error and exits + is.True(strings.Contains(out.String(), cm.ErrUnauthorized.Error())) + var ee *gossh.ExitError + is.True(errors.As(err, &ee) && ee.ExitStatus() == 1) + }) + t.Run("authorized repo access", func(t *testing.T) { + s := setup(t) + s.Stderr = os.Stderr + defer s.Close() + err := s.RequestPty("xterm", 80, 40, nil) + is.NoErr(err) + go func() { + time.Sleep(1 * time.Second) + s.Signal(gossh.SIGTERM) + // FIXME: exit with code 0 instead of forcibly closing the session + s.Close() + }() + err = s.Run("test") + var ee *gossh.ExitMissingError + is.True(errors.As(err, &ee)) + }) +} + +func setup(tb testing.TB) *gossh.Session { + is := is.New(tb) + tb.Helper() + cfg.RepoPath = tb.TempDir() + ac, err := appCfg.NewConfig(&config.Config{ + Port: 22226, + KeyPath: tb.TempDir(), + RepoPath: tb.TempDir(), + InitialAdminKeys: []string{ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMJlb/qf2B2kMNdBxfpCQqI2ctPcsOkdZGVh5zTRhKtH", + }, + }) + ac.AnonAccess = "read-only" + is.NoErr(err) + return testsession.New(tb, &ssh.Server{ + Handler: bm.MiddlewareWithProgramHandler(SessionHandler(ac), termenv.ANSI256)(func(s ssh.Session) { + _, _, active := s.Pty() + tb.Logf("PTY active %v", active) + tb.Log(s.Command()) + s.Exit(0) + }), + }, nil) +}