Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

knotserver/git: reject requests to unknown repos

Signed-off-by: Seongmin Lee <git@boltless.me>

authored by

Seongmin Lee and committed by tangled.org b3222c44 c516e353

+64 -34
+24 -34
knotserver/git.go
··· 5 5 "fmt" 6 6 "io" 7 7 "net/http" 8 - "path/filepath" 8 + "os" 9 9 "strings" 10 10 11 - securejoin "github.com/cyphar/filepath-securejoin" 12 11 "github.com/go-chi/chi/v5" 13 12 "tangled.org/core/knotserver/git/service" 14 13 ) 15 14 16 15 func (h *Knot) InfoRefs(w http.ResponseWriter, r *http.Request) { 17 - did := chi.URLParam(r, "did") 18 16 name := chi.URLParam(r, "name") 19 - repoName, err := securejoin.SecureJoin(did, name) 20 - if err != nil { 21 - gitError(w, "repository not found", http.StatusNotFound) 22 - h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 23 - return 24 - } 25 - 26 - repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, repoName) 27 - if err != nil { 28 - gitError(w, "repository not found", http.StatusNotFound) 29 - h.l.Error("git: failed to secure join repo path", "handler", "InfoRefs", "error", err) 17 + repoPath, ok := repoPathFromcontext(r.Context()) 18 + if !ok { 19 + w.WriteHeader(http.StatusInternalServerError) 20 + w.Write([]byte("Failed to find repository path")) 30 21 return 31 22 } 32 23 ··· 48 57 } 49 58 50 59 func (h *Knot) UploadArchive(w http.ResponseWriter, r *http.Request) { 51 - did := chi.URLParam(r, "did") 52 - name := chi.URLParam(r, "name") 53 - repo, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name)) 54 - if err != nil { 55 - gitError(w, err.Error(), http.StatusInternalServerError) 56 - h.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err) 60 + repo, ok := repoPathFromcontext(r.Context()) 61 + if !ok { 62 + w.WriteHeader(http.StatusInternalServerError) 63 + w.Write([]byte("Failed to find repository path")) 57 64 return 58 65 } 59 66 ··· 93 104 } 94 105 95 106 func (h *Knot) UploadPack(w http.ResponseWriter, r *http.Request) { 96 - did := chi.URLParam(r, "did") 97 - name := chi.URLParam(r, "name") 98 - repo, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name)) 99 - if err != nil { 100 - gitError(w, err.Error(), http.StatusInternalServerError) 101 - h.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err) 107 + repo, ok := repoPathFromcontext(r.Context()) 108 + if !ok { 109 + w.WriteHeader(http.StatusInternalServerError) 110 + w.Write([]byte("Failed to find repository path")) 102 111 return 103 112 } 104 113 ··· 140 153 } 141 154 142 155 func (h *Knot) ReceivePack(w http.ResponseWriter, r *http.Request) { 143 - did := chi.URLParam(r, "did") 144 156 name := chi.URLParam(r, "name") 145 - _, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name)) 146 - if err != nil { 147 - gitError(w, err.Error(), http.StatusForbidden) 148 - h.l.Error("git: failed to secure join repo path", "handler", "ReceivePack", "error", err) 149 - return 150 - } 151 - 152 157 h.RejectPush(w, r, name) 153 158 } 154 159 ··· 169 190 fmt.Fprintf(w, " Try:\ngit remote set-url --push origin git@%s:%s/%s\n\n... and push again.", hostname, ownerHandle, unqualifiedRepoName) 170 191 } 171 192 fmt.Fprintf(w, "\n\n") 193 + } 194 + 195 + func isDir(path string) (bool, error) { 196 + info, err := os.Stat(path) 197 + if err == nil && info.IsDir() { 198 + return true, nil 199 + } 200 + if os.IsNotExist(err) { 201 + return false, nil 202 + } 203 + return false, err 172 204 } 173 205 174 206 func gitError(w http.ResponseWriter, msg string, status int) {
+40
knotserver/router.go
··· 5 5 "fmt" 6 6 "log/slog" 7 7 "net/http" 8 + "path/filepath" 8 9 "strings" 9 10 11 + securejoin "github.com/cyphar/filepath-securejoin" 10 12 "github.com/go-chi/chi/v5" 11 13 "tangled.org/core/idresolver" 12 14 "tangled.org/core/jetstream" ··· 83 81 84 82 r.Route("/{did}", func(r chi.Router) { 85 83 r.Use(h.resolveDidRedirect) 84 + r.Use(h.resolveRepo) 86 85 r.Route("/{name}", func(r chi.Router) { 87 86 // routes for git operations 88 87 r.Get("/info/refs", h.InfoRefs) ··· 141 138 suffix := strings.TrimPrefix(r.URL.Path, "/"+didOrHandle) 142 139 newPath := fmt.Sprintf("/%s/%s?%s", id.DID.String(), suffix, r.URL.RawQuery) 143 140 http.Redirect(w, r, newPath, http.StatusTemporaryRedirect) 141 + }) 142 + } 143 + 144 + type ctxRepoPathKey struct{} 145 + 146 + func repoPathFromcontext(ctx context.Context) (string, bool) { 147 + v, ok := ctx.Value(ctxRepoPathKey{}).(string) 148 + return v, ok 149 + } 150 + 151 + // resolveRepo is a http middleware that constructs git repo path from given did & name pair. 152 + // It will reject the requests to unknown repos (when dir doesn't exist) 153 + func (h *Knot) resolveRepo(next http.Handler) http.Handler { 154 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 155 + did := chi.URLParam(r, "did") 156 + name := chi.URLParam(r, "name") 157 + repoPath, err := securejoin.SecureJoin(h.c.Repo.ScanPath, filepath.Join(did, name)) 158 + if err != nil { 159 + w.WriteHeader(http.StatusNotFound) 160 + w.Write([]byte("Repository not found")) 161 + return 162 + } 163 + 164 + exist, err := isDir(repoPath) 165 + if err != nil { 166 + w.WriteHeader(http.StatusInternalServerError) 167 + w.Write([]byte("Failed to check repository path")) 168 + return 169 + } 170 + if !exist { 171 + w.WriteHeader(http.StatusNotFound) 172 + w.Write([]byte("Repository not found")) 173 + return 174 + } 175 + 176 + ctx := context.WithValue(r.Context(), "repoPath", repoPath) 177 + next.ServeHTTP(w, r.WithContext(ctx)) 144 178 }) 145 179 } 146 180