knotserver: allow downloading archives of refs with slashes in their names #441

merged
opened by winter.bsky.social targeting master from winter.bsky.social/core: push-vtrouwuvwzqv
Changed files
+17 -3
knotserver
+17 -3
knotserver/routes.go
··· 361 362 ref := strings.TrimSuffix(file, ".tar.gz") 363 364 // This allows the browser to use a proper name for the file when 365 // downloading 366 - filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) 367 setContentDisposition(w, filename) 368 setGZipMIME(w) 369 370 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 371 - gr, err := git.Open(path, ref) 372 if err != nil { 373 notFound(w) 374 return ··· 377 gw := gzip.NewWriter(w) 378 defer gw.Close() 379 380 - prefix := fmt.Sprintf("%s-%s", name, ref) 381 err = gr.WriteTar(gw, prefix) 382 if err != nil { 383 // once we start writing to the body we can't report error anymore
··· 361 362 ref := strings.TrimSuffix(file, ".tar.gz") 363 364 + unescapedRef, err := url.PathUnescape(ref) 365 + if err != nil { 366 + notFound(w) 367 + return 368 + } 369 + 370 + // For now, only accept `/` in a ref name if it's refs/tags/* 371 + if strings.Contains(unescapedRef, "/") && !strings.HasPrefix(unescapedRef, "refs/tags/") { 372 + notFound(w) 373 + return 374 + } 375 + 376 + safeRefFilename := strings.TrimPrefix(unescapedRef, "refs/tags/") 377 + 378 // This allows the browser to use a proper name for the file when 379 // downloading 380 + filename := fmt.Sprintf("%s-%s.tar.gz", name, safeRefFilename) 381 setContentDisposition(w, filename) 382 setGZipMIME(w) 383 384 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 385 + gr, err := git.Open(path, unescapedRef) 386 if err != nil { 387 notFound(w) 388 return ··· 391 gw := gzip.NewWriter(w) 392 defer gw.Close() 393 394 + prefix := fmt.Sprintf("%s-%s", name, safeRefFilename) 395 err = gr.WriteTar(gw, prefix) 396 if err != nil { 397 // once we start writing to the body we can't report error anymore