Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2

knotserver: reject 'git-receive-pack' service in info/refs

* Checks service name query parameter in info/refs handler; rejects any
service request that doesn't match 'git-upload-pack'.

* Produces 'text/plain' error responses so the git client will display
them.

* Adds a specific reject message for 'git-receive-pack'. This will
produce a less cryptic error in the terminal when trying to push over
http.

authored by tjh.dev and committed by

Tangled ca454e92 f884856c

+45 -8
+45 -8
knotserver/git.go
··· 2 3 import ( 4 "compress/gzip" 5 "io" 6 "net/http" 7 "path/filepath" ··· 15 func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) { 16 did := chi.URLParam(r, "did") 17 name := chi.URLParam(r, "name") 18 - repo, _ := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name)) 19 20 - w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") 21 - w.WriteHeader(http.StatusOK) 22 23 cmd := service.ServiceCommand{ 24 - Dir: repo, 25 Stdout: w, 26 } 27 28 - if err := cmd.InfoRefs(); err != nil { 29 - writeError(w, err.Error(), 500) 30 - d.l.Error("git: failed to execute git-upload-pack (info/refs)", "handler", "InfoRefs", "error", err) 31 - return 32 } 33 } 34 ··· 91 d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err) 92 return 93 } 94 }
··· 2 3 import ( 4 "compress/gzip" 5 + "fmt" 6 "io" 7 "net/http" 8 "path/filepath" ··· 14 func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) { 15 did := chi.URLParam(r, "did") 16 name := chi.URLParam(r, "name") 17 + repoName, err := securejoin.SecureJoin(did, name) 18 + if err != nil { 19 + gitError(w, "repository not found", http.StatusNotFound) 20 + d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 21 + return 22 + } 23 24 + repoPath, err := securejoin.SecureJoin(d.c.Repo.ScanPath, repoName) 25 + if err != nil { 26 + gitError(w, "repository not found", http.StatusNotFound) 27 + d.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 28 + return 29 + } 30 31 cmd := service.ServiceCommand{ 32 + Dir: repoPath, 33 Stdout: w, 34 } 35 36 + serviceName := r.URL.Query().Get("service") 37 + switch serviceName { 38 + case "git-upload-pack": 39 + w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") 40 + w.WriteHeader(http.StatusOK) 41 + 42 + if err := cmd.InfoRefs(); err != nil { 43 + gitError(w, err.Error(), http.StatusInternalServerError) 44 + d.l.Error("git: process failed", "handler", "InfoRefs", "service", serviceName, "error", err) 45 + return 46 + } 47 + case "git-receive-pack": 48 + d.RejectPush(w, r, name) 49 + default: 50 + gitError(w, fmt.Sprintf("service unsupported: '%s'", serviceName), http.StatusForbidden) 51 } 52 } 53 ··· 70 d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err) 71 return 72 } 73 + } 74 + 75 + func (d *Handle) RejectPush(w http.ResponseWriter, r *http.Request, unqualifiedRepoName string) { 76 + // A text/plain response will cause git to print each line of the body 77 + // prefixed with "remote: ". 78 + w.Header().Set("content-type", "text/plain; charset=UTF-8") 79 + w.WriteHeader(http.StatusForbidden) 80 + 81 + fmt.Fprintf(w, "Welcome to Tangled.sh!\n\nPushes are currently only supported over SSH.") 82 + fmt.Fprintf(w, "\n\n") 83 + } 84 + 85 + func gitError(w http.ResponseWriter, msg string, status int) { 86 + w.Header().Set("content-type", "text/plain; charset=UTF-8") 87 + w.WriteHeader(status) 88 + fmt.Fprintf(w, "%s\n", msg) 89 }