From f8b418d07d75e614ce067e669586250ce3ef63b2 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 17 Jan 2026 19:40:57 +0800 Subject: [PATCH] fix git service handling --- routers/web/githttp.go | 9 +++-- routers/web/repo/githttp.go | 70 ++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/routers/web/githttp.go b/routers/web/githttp.go index 1b6ef455a7..794c2944bb 100644 --- a/routers/web/githttp.go +++ b/routers/web/githttp.go @@ -10,10 +10,13 @@ import ( ) func addOwnerRepoGitHTTPRouters(m *web.Router) { + presetGitService := func(service string) func(ctx *context.Context) { + return func(ctx *context.Context) { ctx.SetPathParam("preset-git-service", service) } + } m.Group("/{username}/{reponame}", func() { - m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack) - m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack) - m.Methods("POST,OPTIONS", "/git-upload-archive", repo.ServiceUploadArchive) + m.Methods("POST,OPTIONS", "/git-upload-pack", presetGitService("git-upload-pack"), repo.ServiceUploadPack) + m.Methods("POST,OPTIONS", "/git-receive-pack", presetGitService("git-receive-pack"), repo.ServiceReceivePack) + m.Methods("POST,OPTIONS", "/git-upload-archive", presetGitService("git-upload-archive"), repo.ServiceUploadArchive) m.Methods("GET,OPTIONS", "/info/refs", repo.GetInfoRefs) m.Methods("GET,OPTIONS", "/HEAD", repo.GetTextFile("HEAD")) m.Methods("GET,OPTIONS", "/objects/info/alternates", repo.GetTextFile("objects/info/alternates")) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 915057e5f1..3e5de9c0e9 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -66,16 +66,13 @@ func httpBase(ctx *context.Context) *serviceHandler { } var isPull, receivePack bool - service := ctx.FormString("service") - if service == "git-receive-pack" || - strings.HasSuffix(ctx.Req.URL.Path, "git-receive-pack") { + gitService := ctx.FormString("service", ctx.PathParam("preset-git-service")) + if gitService == "git-receive-pack" { isPull = false receivePack = true - } else if service == "git-upload-pack" || - strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") { + } else if gitService == "git-upload-pack" { isPull = true - } else if service == "git-upload-archive" || - strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") { + } else if gitService == "git-upload-archive" { isPull = true } else { isPull = ctx.Req.Method == http.MethodHead || ctx.Req.Method == http.MethodGet @@ -380,39 +377,35 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string // one or more key=value pairs separated by colons var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) -func prepareGitCmdWithAllowedService(service string) (*gitcmd.Command, error) { - if service == ServiceTypeReceivePack { - return gitcmd.NewCommand(ServiceTypeReceivePack), nil +func prepareGitCmdWithAllowedService(service string, allowedServices []string) *gitcmd.Command { + if !slices.Contains(allowedServices, service) { + return nil } - if service == ServiceTypeUploadPack { - return gitcmd.NewCommand(ServiceTypeUploadPack), nil + switch service { + case ServiceTypeReceivePack: + return gitcmd.NewCommand(ServiceTypeReceivePack) + case ServiceTypeUploadPack: + return gitcmd.NewCommand(ServiceTypeUploadPack) + case ServiceTypeUploadArchive: + return gitcmd.NewCommand(ServiceTypeUploadArchive) + default: + return nil } - if service == ServiceTypeUploadArchive { - return gitcmd.NewCommand(ServiceTypeUploadArchive), nil - } - return nil, fmt.Errorf("service %q is not allowed", service) } func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { - defer func() { - if err := ctx.Req.Body.Close(); err != nil { - log.Error("serviceRPC: Close: %v", err) - } - }() + defer ctx.Req.Body.Close() expectedContentType := fmt.Sprintf("application/x-git-%s-request", service) if ctx.Req.Header.Get("Content-Type") != expectedContentType { log.Error("Content-Type (%q) doesn't match expected: %q", ctx.Req.Header.Get("Content-Type"), expectedContentType) - // FIXME: why it's 401 if the content type is unexpected? - ctx.Resp.WriteHeader(http.StatusUnauthorized) + ctx.Resp.WriteHeader(http.StatusBadRequest) return } - cmd, err := prepareGitCmdWithAllowedService(service) - if err != nil { - log.Error("Failed to prepareGitCmdWithService: %v", err) - // FIXME: why it's 401 if the service type doesn't supported? - ctx.Resp.WriteHeader(http.StatusUnauthorized) + cmd := prepareGitCmdWithAllowedService(service, []string{ServiceTypeUploadPack, ServiceTypeReceivePack, ServiceTypeUploadArchive}) + if cmd == nil { + ctx.Resp.WriteHeader(http.StatusBadRequest) return } @@ -422,10 +415,10 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { // Handle GZIP. if ctx.Req.Header.Get("Content-Encoding") == "gzip" { + var err error reqBody, err = gzip.NewReader(reqBody) if err != nil { - log.Error("Fail to create gzip reader: %v", err) - ctx.Resp.WriteHeader(http.StatusInternalServerError) + ctx.Resp.WriteHeader(http.StatusBadRequest) return } } @@ -438,7 +431,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { } var stderr bytes.Buffer - // git upload-archive does not have a -- stateless-rpc option + // git upload-archive does not have a "--stateless-rpc" option if service == ServiceTypeUploadArchive || service == ServiceTypeReceivePack { cmd.AddArguments("--stateless-rpc") } @@ -484,7 +477,8 @@ func ServiceReceivePack(ctx *context.Context) { } func getServiceType(ctx *context.Context) string { - switch ctx.Req.FormValue("service") { + gitService := ctx.Req.FormValue("service") + switch gitService { case "git-" + ServiceTypeUploadPack: return ServiceTypeUploadPack case "git-" + ServiceTypeReceivePack: @@ -511,12 +505,8 @@ func GetInfoRefs(ctx *context.Context) { } setHeaderNoCache(ctx) service := getServiceType(ctx) - if !(service == ServiceTypeUploadPack || service == ServiceTypeReceivePack) { - ctx.Resp.WriteHeader(http.StatusBadRequest) - return - } - cmd, err := prepareGitCmdWithAllowedService(service) - if err == nil { + cmd := prepareGitCmdWithAllowedService(service, []string{ServiceTypeUploadPack, ServiceTypeReceivePack}) + if cmd != nil { if protocol := ctx.Req.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) { h.environ = append(h.environ, "GIT_PROTOCOL="+protocol) } @@ -533,11 +523,13 @@ func GetInfoRefs(ctx *context.Context) { _, _ = ctx.Resp.Write(packetWrite("# service=git-" + service + "\n")) _, _ = ctx.Resp.Write([]byte("0000")) _, _ = ctx.Resp.Write(refs) - } else { + } else if service == "" { if err := gitrepo.UpdateServerInfo(ctx, h.getStorageRepo()); err != nil { log.Error("Failed to update server info: %v", err) } h.sendFile(ctx, "text/plain; charset=utf-8", "info/refs") + } else { + ctx.Resp.WriteHeader(http.StatusBadRequest) } }