Skip to content

Commit 7b79be2

Browse files
authored
Fix panic in storageHandler (#27446)
storageHandler() is written as a middleware but is used as an endpoint handler, and thus `next` is actually `nil`, which causes a null pointer dereference when a request URL does not match the pattern (where it calls `next.ServerHTTP()`). Example CURL command to trigger the panic: ``` curl -I "http://yourhost/gitea//avatars/a" ``` Fixes #27409 --- Note: the diff looks big but it's actually a small change - all I did was to remove the outer closure (and one level of indentation) ~and removed the HTTP method and pattern checks as they seem redundant because go-chi already does those checks~. You might want to check "Hide whitespace" when reviewing it. Alternative solution (a bit simpler): append `, misc.DummyOK` to the route declarations that utilize `storageHandler()` - this makes it return an empty response when the URL is invalid. I've tested this one and it works too. Or maybe it would be better to return a 400 error in that case (?)
1 parent 023e937 commit 7b79be2

File tree

1 file changed

+50
-51
lines changed

1 file changed

+50
-51
lines changed

routers/web/base.go

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,81 +19,80 @@ import (
1919
"code.gitea.io/gitea/modules/web/routing"
2020
)
2121

22-
func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
22+
func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) http.HandlerFunc {
2323
prefix = strings.Trim(prefix, "/")
2424
funcInfo := routing.GetFuncInfo(storageHandler, prefix)
25-
return func(next http.Handler) http.Handler {
26-
if storageSetting.MinioConfig.ServeDirect {
27-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
28-
if req.Method != "GET" && req.Method != "HEAD" {
29-
next.ServeHTTP(w, req)
30-
return
31-
}
32-
33-
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
34-
next.ServeHTTP(w, req)
35-
return
36-
}
37-
routing.UpdateFuncInfo(req.Context(), funcInfo)
38-
39-
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
40-
rPath = util.PathJoinRelX(rPath)
41-
42-
u, err := objStore.URL(rPath, path.Base(rPath))
43-
if err != nil {
44-
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
45-
log.Warn("Unable to find %s %s", prefix, rPath)
46-
http.Error(w, "file not found", http.StatusNotFound)
47-
return
48-
}
49-
log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err)
50-
http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), http.StatusInternalServerError)
51-
return
52-
}
53-
54-
http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
55-
})
56-
}
5725

26+
if storageSetting.MinioConfig.ServeDirect {
5827
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
5928
if req.Method != "GET" && req.Method != "HEAD" {
60-
next.ServeHTTP(w, req)
29+
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
6130
return
6231
}
6332

6433
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
65-
next.ServeHTTP(w, req)
34+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
6635
return
6736
}
6837
routing.UpdateFuncInfo(req.Context(), funcInfo)
6938

7039
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
7140
rPath = util.PathJoinRelX(rPath)
72-
if rPath == "" || rPath == "." {
73-
http.Error(w, "file not found", http.StatusNotFound)
74-
return
75-
}
7641

77-
fi, err := objStore.Stat(rPath)
42+
u, err := objStore.URL(rPath, path.Base(rPath))
7843
if err != nil {
7944
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
8045
log.Warn("Unable to find %s %s", prefix, rPath)
81-
http.Error(w, "file not found", http.StatusNotFound)
46+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
8247
return
8348
}
84-
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
85-
http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), http.StatusInternalServerError)
49+
log.Error("Error whilst getting URL for %s %s. Error: %v", prefix, rPath, err)
50+
http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), http.StatusInternalServerError)
8651
return
8752
}
8853

89-
fr, err := objStore.Open(rPath)
90-
if err != nil {
91-
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
92-
http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), http.StatusInternalServerError)
93-
return
94-
}
95-
defer fr.Close()
96-
httpcache.ServeContentWithCacheControl(w, req, path.Base(rPath), fi.ModTime(), fr)
54+
http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
9755
})
9856
}
57+
58+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
59+
if req.Method != "GET" && req.Method != "HEAD" {
60+
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
61+
return
62+
}
63+
64+
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
65+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
66+
return
67+
}
68+
routing.UpdateFuncInfo(req.Context(), funcInfo)
69+
70+
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
71+
rPath = util.PathJoinRelX(rPath)
72+
if rPath == "" || rPath == "." {
73+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
74+
return
75+
}
76+
77+
fi, err := objStore.Stat(rPath)
78+
if err != nil {
79+
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
80+
log.Warn("Unable to find %s %s", prefix, rPath)
81+
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
82+
return
83+
}
84+
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
85+
http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), http.StatusInternalServerError)
86+
return
87+
}
88+
89+
fr, err := objStore.Open(rPath)
90+
if err != nil {
91+
log.Error("Error whilst opening %s %s. Error: %v", prefix, rPath, err)
92+
http.Error(w, fmt.Sprintf("Error whilst opening %s %s", prefix, rPath), http.StatusInternalServerError)
93+
return
94+
}
95+
defer fr.Close()
96+
httpcache.ServeContentWithCacheControl(w, req, path.Base(rPath), fi.ModTime(), fr)
97+
})
9998
}

0 commit comments

Comments
 (0)