diff --git a/Makefile b/Makefile
index 5c247cb..5fae0f7 100755
--- a/Makefile
+++ b/Makefile
@@ -9,20 +9,24 @@ run:
make build
./gossa test-fixture
-run-prefix:
+run-extra:
make build
- ./gossa -prefix="/fancy-path/" test-fixture
+ ./gossa -prefix="/fancy-path/" -symlinks=true test-fixture
ci:
+ -@cd test-fixture && ln -s ../docker .
timeout 10 make run &
- sleep 11 && timeout 10 make run-prefix &
+ sleep 11 && timeout 10 make run-extra &
cp src/gossa_test.go . && go test
rm gossa_test.go
watch:
ls src/* gossa-ui/* | entr -rc make run
-ci-watch:
+watch-extra:
+ ls src/* gossa-ui/* | entr -rc make run-extra
+
+watch-ci:
ls src/* gossa-ui/* | entr -rc make ci
build-all:
diff --git a/src/gossa.go b/src/gossa.go
index 92576ff..4fceb93 100755
--- a/src/gossa.go
+++ b/src/gossa.go
@@ -21,6 +21,7 @@ import (
var host = flag.String("h", "127.0.0.1", "host to listen to")
var port = flag.String("p", "8001", "port to listen to")
var extraPath = flag.String("prefix", "/", "url prefix at which gossa can be reached, e.g. /gossa/ (slashes of importance)")
+var symlinks = flag.Bool("symlinks", false, "follow symlinks \033[4mWARNING\033[0m: symlinks will by nature allow to escape the defined path (default: false)")
var verb = flag.Bool("verb", true, "verbosity")
var skipHidden = flag.Bool("k", true, "skip hidden files")
var initPath = "."
@@ -54,21 +55,20 @@ func check(e error) {
}
func exitPath(w http.ResponseWriter, s ...interface{}) {
- if recover() != nil {
- log.Println("error", s)
+ if r := recover(); r != nil {
+ log.Println("error", s, r)
w.Write([]byte("error"))
} else if *verb {
log.Println(s...)
}
}
-func sizeToString(bytes int64) string {
- units := [9]string{"B", "k", "M", "G", "T", "P", "E", "Z", "Y"}
+func humanize(bytes int64) string {
b := float64(bytes)
u := 0
for {
if b < 1024 {
- return strconv.FormatFloat(b, 'f', 1, 64) + units[u]
+ return strconv.FormatFloat(b, 'f', 1, 64) + [9]string{"B", "k", "M", "G", "T", "P", "E", "Z", "Y"}[u]
}
b = b / 1024
u++
@@ -85,27 +85,27 @@ func replyList(w http.ResponseWriter, fullPath string, path string) {
title := "/" + strings.TrimPrefix(path, *extraPath)
p := pageTemplate{}
- if !(path == "/" || path == *extraPath) {
+ if path != *extraPath {
p.RowsFolders = append(p.RowsFolders, rowTemplate{"../", "../", "", "folder"})
}
p.ExtraPath = template.HTML(html.EscapeString(*extraPath))
p.Title = template.HTML(html.EscapeString(title))
for _, el := range _files {
- name := el.Name()
- href := url.PathEscape(name)
- if *skipHidden && strings.HasPrefix(name, ".") {
+ if *skipHidden && strings.HasPrefix(el.Name(), ".") {
continue
}
+ el, _ = os.Stat(fullPath + "/" + el.Name())
+ href := url.PathEscape(el.Name())
if el.IsDir() && strings.HasPrefix(href, "/") {
href = strings.Replace(href, "/", "", 1)
}
if el.IsDir() {
- p.RowsFolders = append(p.RowsFolders, rowTemplate{name + "/", template.HTML(href), "", "folder"})
+ p.RowsFolders = append(p.RowsFolders, rowTemplate{el.Name() + "/", template.HTML(href), "", "folder"})
} else {
- sl := strings.Split(name, ".")
+ sl := strings.Split(el.Name(), ".")
ext := strings.ToLower(sl[len(sl)-1])
- p.RowsFiles = append(p.RowsFiles, rowTemplate{name, template.HTML(href), sizeToString(el.Size()), ext})
+ p.RowsFiles = append(p.RowsFiles, rowTemplate{el.Name(), template.HTML(href), humanize(el.Size()), ext})
}
}
@@ -115,6 +115,7 @@ func replyList(w http.ResponseWriter, fullPath string, path string) {
func doContent(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, *extraPath) {
http.Redirect(w, r, *extraPath, 302)
+ return
}
path := html.UnescapeString(r.URL.Path)
@@ -162,8 +163,9 @@ func rpc(w http.ResponseWriter, r *http.Request) {
func checkPath(p string) string {
p = filepath.Join(initPath, strings.TrimPrefix(p, *extraPath))
fp, err := filepath.Abs(p)
+ sl, _ := filepath.EvalSymlinks(fp)
- if err != nil || !strings.HasPrefix(fp, initPath) {
+ if err != nil || !strings.HasPrefix(fp, initPath) || len(sl) > 0 && !*symlinks && !strings.HasPrefix(sl, initPath) {
panic(errors.New("invalid path"))
}
diff --git a/src/gossa_test.go b/src/gossa_test.go
index c5fcd11..8847794 100644
--- a/src/gossa_test.go
+++ b/src/gossa_test.go
@@ -84,7 +84,7 @@ func fetchAndTestDefault(t *testing.T, url string) string {
return bodyStr
}
-func doTest(t *testing.T, url string) {
+func doTest(t *testing.T, url string, symlinkEnabled bool) {
payload := ""
path := ""
bodyStr := ""
@@ -99,9 +99,11 @@ func doTest(t *testing.T, url string) {
fetchAndTestDefault(t, url+"hols/../../")
// ~~~~~~~~~~~~~~~~~
- fmt.Println("\r\n~~~~~~~~~~ test fetching a regular file")
+ fmt.Println("\r\n~~~~~~~~~~ test fetching regular files")
bodyStr = get(t, url+"subdir_with%20space/file_with%20space.html")
- if !strings.Contains(bodyStr, `spacious!!`) {
+ bodyStr2 := get(t, url+"fancy-path/a")
+ fmt.Println(bodyStr2)
+ if !strings.Contains(bodyStr, `spacious!!`) || !strings.Contains(bodyStr2, `fancy!`) {
t.Fatal("fetching a regular file errored")
}
@@ -187,6 +189,24 @@ func doTest(t *testing.T, url string) {
t.Fatal("upload in new folder error reaching new file")
}
+ // ~~~~~~~~~~~~~~~~~
+ fmt.Println("\r\n~~~~~~~~~~ test symlink, should succeed: ", symlinkEnabled)
+ bodyStr = get(t, url+"/docker/readme.md")
+ hasReadme := strings.Contains(bodyStr, `the master branch is automatically built and pushed`)
+ if !symlinkEnabled && hasReadme {
+ t.Fatal("error symlink reached where illegal")
+ } else if symlinkEnabled && !hasReadme {
+ t.Fatal("error symlink unreachable")
+ }
+
+ if symlinkEnabled {
+ fmt.Println("\r\n~~~~~~~~~~ test symlink mkdir")
+ bodyStr = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/docker/testfolder"]}`)
+ if !strings.Contains(bodyStr, `ok`) {
+ t.Fatal("error symlink mkdir")
+ }
+ }
+
// ~~~~~~~~~~~~~~~~~
fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup")
bodyStr = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`)
@@ -204,15 +224,26 @@ func doTest(t *testing.T, url string) {
t.Fatal("cleanup errored #2")
}
- fmt.Println("\r\n\r\n\r\n=========")
+ if symlinkEnabled {
+ bodyStr = postJSON(t, url+"rpc", `{"call":"rm","args":["/docker/testfolder"]}`)
+ if !strings.Contains(bodyStr, `ok`) {
+ t.Fatal("error symlink rm")
+ }
+ }
}
+
func TestGetFolder(t *testing.T) {
time.Sleep(6 * time.Second)
fmt.Println("========== testing normal path ============")
- doTest(t, "http://127.0.0.1:8001/")
+ url := "http://127.0.0.1:8001/"
+ doTest(t, url, false)
+ fmt.Printf("\r\n=========\r\n")
time.Sleep(10 * time.Second)
- fancyPath := "/fancy-path/"
- fmt.Println("========== testing at " + fancyPath + " path ============")
- doTest(t, "http://127.0.0.1:8001"+fancyPath)
+
+ url = "http://127.0.0.1:8001/fancy-path/"
+ fmt.Println("========== testing at fancy path ============")
+ doTest(t, url, true)
+
+ fmt.Printf("\r\n=========\r\n")
}
diff --git a/test-fixture/fancy-path/a b/test-fixture/fancy-path/a
index 7898192..e04498f 100644
--- a/test-fixture/fancy-path/a
+++ b/test-fixture/fancy-path/a
@@ -1 +1 @@
-a
+fancy!