4 files changed,
42 insertions(+),
14 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-01-23 01:02:33 +0200
Change ID:
zorkqmlkxzmsxttskskwuvryrmxvpoko
Parent:
a0a9d4a
M
go.mod
@@ -4,6 +4,7 @@ go 1.25.3
require ( github.com/bluekeyes/go-gitdiff v0.8.1 + github.com/cyphar/filepath-securejoin v0.4.1 github.com/gliderlabs/ssh v0.3.8 github.com/go-git/go-git/v5 v5.16.4 github.com/urfave/cli/v3 v3.6.2@@ -19,7 +20,6 @@ github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/cloudflare/circl v1.6.1 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect
M
internal/handlers/git.go
@@ -5,8 +5,8 @@ "compress/gzip"
"io" "log/slog" "net/http" - "path/filepath" + securejoin "github.com/cyphar/filepath-securejoin" "olexsmir.xyz/mugit/internal/git/gitservice" )@@ -40,8 +40,15 @@
w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") w.WriteHeader(http.StatusOK) - path := filepath.Join(h.c.Repo.Dir, filepath.Clean(name)) + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + slog.Error("git: info/refs", "err", err) + return + } + if err := gitservice.InfoRefs(path, w); err != nil { + w.WriteHeader(http.StatusInternalServerError) slog.Error("git: info/refs", "err", err) return }@@ -62,18 +69,25 @@ w.WriteHeader(http.StatusOK)
reader := io.Reader(r.Body) if r.Header.Get("Content-Encoding") == "gzip" { - gr, err := gzip.NewReader(r.Body) - if err != nil { + gr, gerr := gzip.NewReader(r.Body) + if gerr != nil { w.WriteHeader(http.StatusInternalServerError) - slog.Error("git: gzip reader", "err", err) + slog.Error("git: gzip reader", "err", gerr) return } defer gr.Close() reader = gr } - path := filepath.Join(h.c.Repo.Dir, filepath.Clean(name)) + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + slog.Error("git: info/refs", "err", err) + return + } + if err := gitservice.UploadPack(path, true, reader, newFlushWriter(w)); err != nil { + w.WriteHeader(http.StatusInternalServerError) slog.Error("git: upload-pack", "err", err) return }
M
internal/handlers/repo.go
@@ -15,6 +15,7 @@ "strconv"
"strings" "time" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/yuin/goldmark" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/renderer/html"@@ -323,11 +324,15 @@ }
} } -var errPrivateRepo = errors.New("privat err") +var errPrivateRepo = errors.New("private err") func (h *handlers) openPublicRepo(name, ref string) (*git.Repo, error) { - n := filepath.Clean(name) - repo, err := git.Open(filepath.Join(h.c.Repo.Dir, n), ref) + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) + if err != nil { + return nil, err + } + + repo, err := git.Open(path, ref) if err != nil { return nil, err }
M
internal/ssh/server.go
@@ -3,10 +3,10 @@
import ( "fmt" "log/slog" - "path/filepath" "slices" "strconv" + securejoin "github.com/cyphar/filepath-securejoin" "github.com/gliderlabs/ssh" "olexsmir.xyz/mugit/internal/config" "olexsmir.xyz/mugit/internal/git"@@ -41,7 +41,12 @@ Addr: ":" + strconv.Itoa(s.c.SSH.Port),
Handler: s.handler, PublicKeyHandler: s.authhandler, } - srv.SetOption(ssh.HostKeyFile(s.c.SSH.HostKey)) // TODO: validate `gossh.ParsePrivateKey` + + if err := srv.SetOption(ssh.HostKeyFile(s.c.SSH.HostKey)); err != nil { + // TODO: validate `gossh.ParsePrivateKey` + return err + } + return srv.ListenAndServe() }@@ -53,7 +58,6 @@ return false
} slog.Info("ssh request", "fingerprint", fingerprint) - authorized := slices.ContainsFunc(s.authKeys, func(i gossh.PublicKey) bool { return ssh.KeysEqual(key, i) })@@ -73,8 +77,13 @@ }
gitCmd := cmd[0] repoPath := cmd[1] + repoPath, err := securejoin.SecureJoin(s.c.Repo.Dir, repoPath) + if err != nil { + slog.Error("ssh: invalid path", "err", err) + s.repoNotFound(sess) + return + } - repoPath = filepath.Join(s.c.Repo.Dir, filepath.Clean(repoPath)) repo, err := git.Open(repoPath, "") if err != nil { slog.Error("ssh: failed to open repo", "err", err)