Skip to content

Commit bbf1f7d

Browse files
committed
prevent path traversal outside content directory
1 parent 271a70e commit bbf1f7d

5 files changed

Lines changed: 77 additions & 6 deletions

File tree

src/app/app.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,29 @@ func (app *App) GetOrCreateResponseItem(requestedPath string, compression Compre
203203
return &responseItem, 0
204204
}
205205

206+
func (app *App) GetFilePath(urlPath string) (string, bool) {
207+
requestedPath := path.Join(app.params.Directory, urlPath)
208+
209+
requestedPath, err := filepath.EvalSymlinks(requestedPath)
210+
if err != nil {
211+
return "", false
212+
}
213+
214+
if !strings.HasPrefix(requestedPath, app.params.Directory) {
215+
return "", false
216+
}
217+
218+
return requestedPath, true
219+
}
220+
206221
func (app *App) HandlerFuncNew(w http.ResponseWriter, r *http.Request) {
207-
requestedPath := path.Join(app.params.Directory, r.URL.Path)
222+
requestedPath, valid := app.GetFilePath(r.URL.Path)
223+
224+
if !valid {
225+
w.WriteHeader(http.StatusNotFound)
226+
return
227+
}
228+
208229
responseItem, errorCode := app.GetOrCreateResponseItem(requestedPath, None, nil)
209230
if errorCode != 0 {
210231
w.WriteHeader(errorCode)

src/app/app_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,40 @@ func TestListen(t *testing.T) {
308308

309309
a.Listen()
310310
}
311+
312+
func TestGetFilePath(t *testing.T) {
313+
params := param.Params{
314+
Address: "0.0.0.0",
315+
Port: 8080,
316+
Gzip: false,
317+
Brotli: true,
318+
Threshold: 1024,
319+
Directory: "../../test/frontend/dist",
320+
CacheControlMaxAge: 99999999999,
321+
SpaMode: true,
322+
IgnoreCacheControlPaths: []string{"../../test/frontend/dist/example.html"},
323+
CacheEnabled: true,
324+
CacheBuffer: 50 * 1024,
325+
}
326+
app := app.NewApp(&params)
327+
328+
_, valid := app.GetFilePath("../test/index.html")
329+
if valid {
330+
t.Errorf("Expected false, got %t", valid)
331+
}
332+
333+
_, valid = app.GetFilePath("../../test/index.html")
334+
if valid {
335+
t.Errorf("Expected false, got %t", valid)
336+
}
337+
338+
_, valid = app.GetFilePath("test/../index.html")
339+
if !valid {
340+
t.Errorf("Expected false, got %t", valid)
341+
}
342+
343+
_, valid = app.GetFilePath("test/../test/../index.html")
344+
if !valid {
345+
t.Errorf("Expected false, got %t", valid)
346+
}
347+
}

src/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ func main() {
1313
Name: "spa-to-http",
1414
Flags: param.Flags,
1515
Action: func(c *cli.Context) error {
16-
params := param.ContextToParams(c)
16+
params, err := param.ContextToParams(c)
17+
if err != nil {
18+
return err
19+
}
1720

1821
newApp := app.NewApp(params)
1922
go newApp.CompressFiles()

src/param/param.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package param
22

33
import (
44
"github.com/urfave/cli/v2"
5+
"path/filepath"
56
)
67

78
var Flags = []cli.Flag{
@@ -98,14 +99,19 @@ type Params struct {
9899
//DirectoryListing bool
99100
}
100101

101-
func ContextToParams(c *cli.Context) *Params {
102+
func ContextToParams(c *cli.Context) (*Params, error) {
103+
directory, err := filepath.Abs(c.String("directory"))
104+
if err != nil {
105+
return nil, err
106+
}
107+
102108
return &Params{
103109
Address: c.String("address"),
104110
Port: c.Int("port"),
105111
Gzip: c.Bool("gzip"),
106112
Brotli: c.Bool("brotli"),
107113
Threshold: c.Int64("threshold"),
108-
Directory: c.String("directory"),
114+
Directory: directory,
109115
CacheControlMaxAge: c.Int64("cache-max-age"),
110116
SpaMode: c.Bool("spa"),
111117
IgnoreCacheControlPaths: c.StringSlice("ignore-cache-control-paths"),
@@ -114,5 +120,5 @@ func ContextToParams(c *cli.Context) *Params {
114120
Logger: c.Bool("logger"),
115121
LogPretty: c.Bool("log-pretty"),
116122
//DirectoryListing: c.Bool("directory-listing"),
117-
}
123+
}, nil
118124
}

src/param/param_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ func TestContextToParams(t *testing.T) {
3737

3838
ctx := cli.NewContext(nil, f, nil)
3939
ctx.Context = context.WithValue(context.Background(), "key", "val")
40-
params := param.ContextToParams(ctx)
40+
params, err := param.ContextToParams(ctx)
41+
if err != nil {
42+
t.Errorf("Error: %s", err)
43+
return
44+
}
4145

4246
if params.Address != e_adress {
4347
t.Errorf("Got %s, expected %s", params.Address, e_adress)

0 commit comments

Comments
 (0)