1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-26 03:20:08 +02:00

Bug Fix: Directory Traversal

This commit is contained in:
little-cui 2020-12-13 21:49:11 +08:00
parent 2b36b3d1d7
commit 1beaf09740
3 changed files with 99 additions and 40 deletions

View File

@ -49,7 +49,6 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
@ -486,7 +485,7 @@ func (common) static(prefix, root string, get func(string, HandlerFunc, ...Middl
return err return err
} }
name := filepath.Join(root, path.Clean("/"+p)) // "/"+ for security name := filepath.Join(root, filepath.Clean("/"+p)) // "/"+ for security
fi, err := os.Stat(name) fi, err := os.Stat(name)
if err != nil { if err != nil {
// The access path does not exist // The access path does not exist

View File

@ -60,45 +60,105 @@ func TestEcho(t *testing.T) {
} }
func TestEchoStatic(t *testing.T) { func TestEchoStatic(t *testing.T) {
e := New() var testCases = []struct {
name string
givenPrefix string
givenRoot string
whenURL string
expectStatus int
expectHeaderLocation string
expectBodyStartsWith string
}{
{
name: "ok",
givenPrefix: "/images",
givenRoot: "_fixture/images",
whenURL: "/images/walle.png",
expectStatus: http.StatusOK,
expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}),
},
{
name: "No file",
givenPrefix: "/images",
givenRoot: "_fixture/scripts",
whenURL: "/images/bolt.png",
expectStatus: http.StatusNotFound,
expectBodyStartsWith: "{\"message\":\"Not Found\"}\n",
},
{
name: "Directory",
givenPrefix: "/images",
givenRoot: "_fixture/images",
whenURL: "/images/",
expectStatus: http.StatusNotFound,
expectBodyStartsWith: "{\"message\":\"Not Found\"}\n",
},
{
name: "Directory Redirect",
givenPrefix: "/",
givenRoot: "_fixture",
whenURL: "/folder",
expectStatus: http.StatusMovedPermanently,
expectHeaderLocation: "/folder/",
expectBodyStartsWith: "",
},
{
name: "Directory with index.html",
givenPrefix: "/",
givenRoot: "_fixture",
whenURL: "/",
expectStatus: http.StatusOK,
expectBodyStartsWith: "<!doctype html>",
},
{
name: "Sub-directory with index.html",
givenPrefix: "/",
givenRoot: "_fixture",
whenURL: "/folder/",
expectStatus: http.StatusOK,
expectBodyStartsWith: "<!doctype html>",
},
{
name: "do not allow directory traversal (backslash - windows separator)",
givenPrefix: "/",
givenRoot: "_fixture/",
whenURL: `/..\\middleware/basic_auth.go`,
expectStatus: http.StatusNotFound,
expectBodyStartsWith: "{\"message\":\"Not Found\"}\n",
},
{
name: "do not allow directory traversal (slash - unix separator)",
givenPrefix: "/",
givenRoot: "_fixture/",
whenURL: `/../middleware/basic_auth.go`,
expectStatus: http.StatusNotFound,
expectBodyStartsWith: "{\"message\":\"Not Found\"}\n",
},
}
assert := assert.New(t) for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// OK e := New()
e.Static("/images", "_fixture/images") e.Static(tc.givenPrefix, tc.givenRoot)
c, b := request(http.MethodGet, "/images/walle.png", e) req := httptest.NewRequest(http.MethodGet, tc.whenURL, nil)
assert.Equal(http.StatusOK, c) rec := httptest.NewRecorder()
assert.NotEmpty(b) e.ServeHTTP(rec, req)
assert.Equal(t, tc.expectStatus, rec.Code)
// No file body := rec.Body.String()
e.Static("/images", "_fixture/scripts") if tc.expectBodyStartsWith != "" {
c, _ = request(http.MethodGet, "/images/bolt.png", e) assert.True(t, strings.HasPrefix(body, tc.expectBodyStartsWith))
assert.Equal(http.StatusNotFound, c) } else {
assert.Equal(t, "", body)
// Directory }
e.Static("/images", "_fixture/images")
c, _ = request(http.MethodGet, "/images/", e)
assert.Equal(http.StatusNotFound, c)
// Directory Redirect
e.Static("/", "_fixture")
req := httptest.NewRequest(http.MethodGet, "/folder", nil)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
assert.Equal(http.StatusMovedPermanently, rec.Code)
assert.Equal("/folder/", rec.HeaderMap["Location"][0])
// Directory with index.html
e.Static("/", "_fixture")
c, r := request(http.MethodGet, "/", e)
assert.Equal(http.StatusOK, c)
assert.Equal(true, strings.HasPrefix(r, "<!doctype html>"))
// Sub-directory with index.html
c, r = request(http.MethodGet, "/folder/", e)
assert.Equal(http.StatusOK, c)
assert.Equal(true, strings.HasPrefix(r, "<!doctype html>"))
if tc.expectHeaderLocation != "" {
assert.Equal(t, tc.expectHeaderLocation, rec.Result().Header["Location"][0])
} else {
_, ok := rec.Result().Header["Location"]
assert.False(t, ok)
}
})
}
} }
func TestEchoFile(t *testing.T) { func TestEchoFile(t *testing.T) {

View File

@ -167,7 +167,7 @@ func StaticWithConfig(config StaticConfig) echo.MiddlewareFunc {
if err != nil { if err != nil {
return return
} }
name := filepath.Join(config.Root, path.Clean("/"+p)) // "/"+ for security name := filepath.Join(config.Root, filepath.Clean("/"+p)) // "/"+ for security
if config.IgnoreBase { if config.IgnoreBase {
routePath := path.Base(strings.TrimRight(c.Path(), "/*")) routePath := path.Base(strings.TrimRight(c.Path(), "/*"))