From 9ff4785b4a68e3cb75ca8709bf425e4d9bb2dc91 Mon Sep 17 00:00:00 2001 From: Winter Date: Sat, 9 Aug 2025 23:13:36 -0400 Subject: [PATCH] knotserver: allow downloading archives of refs with slashes in their names This allows us to be more precise when downloading e.g. tags that have the same name as branches (in which case Git defaults to the branch, not sure what go-git does). Signed-off-by: Winter --- knotserver/routes.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/knotserver/routes.go b/knotserver/routes.go index ba0afa3d..19ef2c2e 100644 --- a/knotserver/routes.go +++ b/knotserver/routes.go @@ -361,14 +361,22 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { ref := strings.TrimSuffix(file, ".tar.gz") + unescapedRef, err := url.PathUnescape(ref) + if err != nil { + notFound(w) + return + } + + safeRefFilename := strings.ReplaceAll(plumbing.ReferenceName(unescapedRef).Short(), "/", "-") + // This allows the browser to use a proper name for the file when // downloading - filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) + filename := fmt.Sprintf("%s-%s.tar.gz", name, safeRefFilename) setContentDisposition(w, filename) setGZipMIME(w) path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) - gr, err := git.Open(path, ref) + gr, err := git.Open(path, unescapedRef) if err != nil { notFound(w) return @@ -377,7 +385,7 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { gw := gzip.NewWriter(w) defer gw.Close() - prefix := fmt.Sprintf("%s-%s", name, ref) + prefix := fmt.Sprintf("%s-%s", name, safeRefFilename) err = gr.WriteTar(gw, prefix) if err != nil { // once we start writing to the body we can't report error anymore -- 2.51.2