Skip to content

Commit f72949a

Browse files
committed
use os.Root to mitigate fs security issues
1 parent e000ae7 commit f72949a

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

cmd/src/servegit.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ Documentation at https://sourcegraph.com/docs/admin/code_hosts/src_serve_git
5858
dbug = log.New(os.Stderr, "DBUG serve-git: ", log.LstdFlags)
5959
}
6060

61+
root, err := os.OpenRoot(repoDir)
62+
if err != nil {
63+
return err
64+
}
65+
6166
s := &servegit.Serve{
62-
Addr: *addrFlag,
63-
Root: repoDir,
64-
Info: log.New(os.Stderr, "serve-git: ", log.LstdFlags),
65-
Debug: dbug,
67+
Addr: *addrFlag,
68+
Root: repoDir,
69+
RootFS: root,
70+
Info: log.New(os.Stderr, "serve-git: ", log.LstdFlags),
71+
Debug: dbug,
6672
}
6773

6874
if *listFlag {

internal/servegit/gitservice.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ type Handler struct {
6565
// call the returned function when done executing. If the executation
6666
// failed, it will pass in a non-nil error.
6767
Trace func(ctx context.Context, svc, repo, protocol string) func(error)
68+
69+
// RootFS is a traversal safe API that ensures file outside of the
70+
// root cannot be opened.
71+
RootFS *os.Root
6872
}
6973

7074
func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -93,7 +97,7 @@ func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
9397
return
9498
}
9599

96-
if _, err = os.Stat(dir); os.IsNotExist(err) {
100+
if _, err = s.RootFS.Stat(dir); os.IsNotExist(err) {
97101
http.Error(w, "repository not found", http.StatusNotFound)
98102
return
99103
} else if err != nil {
@@ -104,6 +108,7 @@ func (s *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
104108
body := r.Body
105109
defer body.Close()
106110

111+
// TODO(@evict) max filereader
107112
if r.Header.Get("Content-Encoding") == "gzip" {
108113
gzipReader, err := gzip.NewReader(body)
109114
if err != nil {

internal/servegit/serve.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"log"
1010
"net"
1111
"net/http"
12+
"os"
1213
"os/exec"
1314
pathpkg "path"
1415
"path/filepath"
@@ -19,10 +20,11 @@ import (
1920
)
2021

2122
type Serve struct {
22-
Addr string
23-
Root string
24-
Info *log.Logger
25-
Debug *log.Logger
23+
Addr string
24+
Root string
25+
RootFS *os.Root
26+
Info *log.Logger
27+
Debug *log.Logger
2628
}
2729

2830
func (s *Serve) Start() error {
@@ -69,7 +71,7 @@ func (s *Serve) handler() http.Handler {
6971

7072
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
7173
w.Header().Set("Content-Type", "text/html; charset=utf-8")
72-
err := indexHTML.Execute(w, map[string]interface{}{
74+
err := indexHTML.Execute(w, map[string]any{
7375
"Explain": explainAddr(s.Addr),
7476
"Links": []string{
7577
"/v1/list-repos",
@@ -100,7 +102,8 @@ func (s *Serve) handler() http.Handler {
100102
_ = enc.Encode(&resp)
101103
})
102104

103-
fs := http.FileServer(http.Dir(s.Root))
105+
safeFS := http.FS(s.RootFS.FS())
106+
fs := http.FileServer(safeFS)
104107
svc := &Handler{
105108
Dir: func(_ context.Context, name string) (string, error) {
106109
return filepath.Join(s.Root, filepath.FromSlash(name)), nil
@@ -117,6 +120,7 @@ func (s *Serve) handler() http.Handler {
117120
}
118121
}
119122
},
123+
RootFS: s.RootFS,
120124
}
121125
mux.Handle("/repos/", http.StripPrefix("/repos/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
122126
// Use git service if git is trying to clone. Otherwise show http.FileServer for convenience

0 commit comments

Comments
 (0)