4 files changed,
42 insertions(+),
14 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-01-23 01:02:33 +0200
Authored at:
2026-01-23 00:21:32 +0200
Change ID:
zorkqmlkxzmsxttskskwuvryrmxvpoko
Parent:
a0a9d4a
M
go.mod
路路路 4 4 5 5 require ( 6 6 github.com/bluekeyes/go-gitdiff v0.8.1 7 + github.com/cyphar/filepath-securejoin v0.4.1 7 8 github.com/gliderlabs/ssh v0.3.8 8 9 github.com/go-git/go-git/v5 v5.16.4 9 10 github.com/urfave/cli/v3 v3.6.2 路路路 19 20 github.com/ProtonMail/go-crypto v1.1.6 // indirect 20 21 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect 21 22 github.com/cloudflare/circl v1.6.1 // indirect 22 - github.com/cyphar/filepath-securejoin v0.4.1 // indirect 23 23 github.com/emirpasic/gods v1.18.1 // indirect 24 24 github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 25 25 github.com/go-git/go-billy/v5 v5.6.2 // indirect
M
internal/handlers/git.go
路路路 5 5 "io" 6 6 "log/slog" 7 7 "net/http" 8 - "path/filepath" 9 8 9 + securejoin "github.com/cyphar/filepath-securejoin" 10 10 "olexsmir.xyz/mugit/internal/git/gitservice" 11 11 ) 12 12 路路路 40 40 w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") 41 41 w.WriteHeader(http.StatusOK) 42 42 43 - path := filepath.Join(h.c.Repo.Dir, filepath.Clean(name)) 43 + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) 44 + if err != nil { 45 + w.WriteHeader(http.StatusBadRequest) 46 + slog.Error("git: info/refs", "err", err) 47 + return 48 + } 49 + 44 50 if err := gitservice.InfoRefs(path, w); err != nil { 51 + w.WriteHeader(http.StatusInternalServerError) 45 52 slog.Error("git: info/refs", "err", err) 46 53 return 47 54 } 路路路 62 69 63 70 reader := io.Reader(r.Body) 64 71 if r.Header.Get("Content-Encoding") == "gzip" { 65 - gr, err := gzip.NewReader(r.Body) 66 - if err != nil { 72 + gr, gerr := gzip.NewReader(r.Body) 73 + if gerr != nil { 67 74 w.WriteHeader(http.StatusInternalServerError) 68 - slog.Error("git: gzip reader", "err", err) 75 + slog.Error("git: gzip reader", "err", gerr) 69 76 return 70 77 } 71 78 defer gr.Close() 72 79 reader = gr 73 80 } 74 81 75 - path := filepath.Join(h.c.Repo.Dir, filepath.Clean(name)) 82 + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) 83 + if err != nil { 84 + w.WriteHeader(http.StatusBadRequest) 85 + slog.Error("git: info/refs", "err", err) 86 + return 87 + } 88 + 76 89 if err := gitservice.UploadPack(path, true, reader, newFlushWriter(w)); err != nil { 90 + w.WriteHeader(http.StatusInternalServerError) 77 91 slog.Error("git: upload-pack", "err", err) 78 92 return 79 93 }
M
internal/handlers/repo.go
路路路 15 15 "strings" 16 16 "time" 17 17 18 + securejoin "github.com/cyphar/filepath-securejoin" 18 19 "github.com/yuin/goldmark" 19 20 "github.com/yuin/goldmark/extension" 20 21 "github.com/yuin/goldmark/renderer/html" 路路路 323 324 } 324 325 } 325 326 326 -var errPrivateRepo = errors.New("privat err") 327 +var errPrivateRepo = errors.New("private err") 327 328 328 329 func (h *handlers) openPublicRepo(name, ref string) (*git.Repo, error) { 329 - n := filepath.Clean(name) 330 - repo, err := git.Open(filepath.Join(h.c.Repo.Dir, n), ref) 330 + path, err := securejoin.SecureJoin(h.c.Repo.Dir, name) 331 + if err != nil { 332 + return nil, err 333 + } 334 + 335 + repo, err := git.Open(path, ref) 331 336 if err != nil { 332 337 return nil, err 333 338 }
M
internal/ssh/server.go
路路路 3 3 import ( 4 4 "fmt" 5 5 "log/slog" 6 - "path/filepath" 7 6 "slices" 8 7 "strconv" 9 8 9 + securejoin "github.com/cyphar/filepath-securejoin" 10 10 "github.com/gliderlabs/ssh" 11 11 "olexsmir.xyz/mugit/internal/config" 12 12 "olexsmir.xyz/mugit/internal/git" 路路路 41 41 Handler: s.handler, 42 42 PublicKeyHandler: s.authhandler, 43 43 } 44 - srv.SetOption(ssh.HostKeyFile(s.c.SSH.HostKey)) // TODO: validate `gossh.ParsePrivateKey` 44 + 45 + if err := srv.SetOption(ssh.HostKeyFile(s.c.SSH.HostKey)); err != nil { 46 + // TODO: validate `gossh.ParsePrivateKey` 47 + return err 48 + } 49 + 45 50 return srv.ListenAndServe() 46 51 } 47 52 路路路 53 58 } 54 59 55 60 slog.Info("ssh request", "fingerprint", fingerprint) 56 - 57 61 authorized := slices.ContainsFunc(s.authKeys, func(i gossh.PublicKey) bool { 58 62 return ssh.KeysEqual(key, i) 59 63 }) 路路路 73 77 74 78 gitCmd := cmd[0] 75 79 repoPath := cmd[1] 80 + repoPath, err := securejoin.SecureJoin(s.c.Repo.Dir, repoPath) 81 + if err != nil { 82 + slog.Error("ssh: invalid path", "err", err) 83 + s.repoNotFound(sess) 84 + return 85 + } 76 86 77 - repoPath = filepath.Join(s.c.Repo.Dir, filepath.Clean(repoPath)) 78 87 repo, err := git.Open(repoPath, "") 79 88 if err != nil { 80 89 slog.Error("ssh: failed to open repo", "err", err)