4 files changed,
52 insertions(+),
13 deletions(-)
Author:
Oleksandr Smirnov
olexsmir@gmail.com
Committed at:
2026-04-23 21:54:34 +0300
Authored at:
2026-04-23 21:20:21 +0300
Change ID:
soxrtypuworrmyxkxtyxzvlzvyvrluvy
Parent:
46e2219
M
internal/cli/ssh_shell.go
路路路 17 17 } 18 18 19 19 sshCommand := os.Getenv("SSH_ORIGINAL_COMMAND") 20 - return c.ssh.HandleCommand(ctx, sshCommand, os.Stdin, os.Stdout, os.Stderr) 20 + if err := c.ssh.HandleCommand(ctx, sshCommand, os.Stdin, os.Stdout, os.Stderr); err != nil { 21 + os.Exit(1) 22 + return nil 23 + } 24 + return nil 21 25 } 22 26 23 27 func (c *Cli) sshAuthorizedKeysAction(ctx context.Context, cmd *cli.Command) error {
M
internal/ssh/ssh.go
路路路 2 2 3 3 import ( 4 4 "context" 5 + "errors" 5 6 "fmt" 6 7 "io" 7 - "log/slog" 8 8 "strings" 9 9 10 10 "olexsmir.xyz/mugit/internal/config" 路路路 44 44 func (s *Shell) HandleCommand(ctx context.Context, cmd string, stdin io.Reader, stdout, stderr io.Writer) error { 45 45 gitCmd, repoName, err := s.parseCommand(cmd) 46 46 if err != nil { 47 - slog.Error("ssh invalid command", "error", err, "raw_cmd", cmd) 48 - return err 47 + return s.replyWithGitError(stderr, "access denied: invalid command", err) 49 48 } 50 49 51 50 if !validCommands[gitCmd] { 52 - slog.Error("access denied: invalid git command") 53 - return fmt.Errorf("access denied: invalid git command") 51 + msg := "access denied: invalid git command" 52 + return s.replyWithGitError(stderr, msg, errors.New(msg)) 54 53 } 55 54 56 55 repoPath, err := git.ResolvePath(s.cfg.Repo.Dir, git.ResolveName(repoName)) 57 56 if err != nil { 58 - slog.Error("ssh access denied", "cmd", gitCmd, "repo", repoName, "error", err) 59 - return err 57 + return s.replyWithGitError(stderr, "access denied", err) 60 58 } 61 59 62 60 repo, err := git.Open(repoPath, "") 63 61 if err != nil { 64 - slog.Error("ssh access denied", "cmd", gitCmd, "repo", repoName, "error", err) 65 - return err 62 + return s.replyWithGitError(stderr, "repository not found", err) 66 63 } 67 64 68 65 switch gitCmd { 路路路 73 70 case "git-receive-pack": 74 71 err = repo.ReceivePack(ctx, stdin, stdout, stderr) 75 72 default: 76 - err = fmt.Errorf("access denied: invalid git command %q", gitCmd) 73 + msg := "access denied: invalid git command" 74 + return s.replyWithGitError(stderr, msg, errors.New(msg)) 77 75 } 78 76 79 77 if err != nil { 80 - slog.Error("ssh operation failed", "cmd", gitCmd, "repo", repoName, "error", err) 78 + return err 81 79 } 82 80 83 - return err 81 + return nil 84 82 } 85 83 86 84 func (s *Shell) AuthorizedKeys(executablePath string) string { 路路路 106 104 107 105 return gitCmd, repoName, nil 108 106 } 107 + 108 +func (s *Shell) replyWithGitError(stderr io.Writer, msg string, cause error) error { 109 + if _, err := fmt.Fprintf(stderr, "fatal: %s\n", msg); err != nil { 110 + return err 111 + } 112 + 113 + return cause 114 +}
A
testscript/ssh-clone-nonexistent.txtar
路路路 1 +git init local 2 +cp readme.txt local/readme.txt 3 +git -C local add . 4 +git -C local commit -m 'init' 5 + 6 +mugit repo new ssh-clone.git 7 +git -C local push file://$REPOS/ssh-clone.git master 8 + 9 + 10 +exec env GIT_SSH_COMMAND=$SSH_WRAPPER git clone git@localhost:ssh-clone.git clone 11 +exists clone/readme.txt 12 + 13 +exec cat clone/readme.txt 14 +stdout 'hello world' 15 + 16 + 17 +# clone non existent repo 18 +! exec env GIT_SSH_COMMAND=$SSH_WRAPPER git clone git@localhost:nonexistent.git wont-clone 19 +stderr 'fatal: repository not found' 20 +! stderr 'ERROR mugit' 21 + 22 + 23 +-- readme.txt -- 24 +hello world