mirror of
https://github.com/pldubouilh/gossa
synced 2025-12-06 08:22:32 +01:00
346 lines
10 KiB
Go
346 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func dieMaybe(t *testing.T, err error) {
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func trimSpaces(str string) string {
|
|
space := regexp.MustCompile(`\s+`)
|
|
return space.ReplaceAllString(str, " ")
|
|
}
|
|
|
|
func getRaw(t *testing.T, url string) []byte {
|
|
resp, err := http.Get(url)
|
|
dieMaybe(t, err)
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
dieMaybe(t, err)
|
|
return body
|
|
}
|
|
|
|
func getZip(t *testing.T, dest string) []*zip.File {
|
|
b := getRaw(t, dest)
|
|
archive, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
|
|
dieMaybe(t, err)
|
|
return archive.File
|
|
}
|
|
|
|
func get(t *testing.T, url string) string {
|
|
body := getRaw(t, url)
|
|
return trimSpaces(string(body))
|
|
}
|
|
|
|
func postDummyFile(t *testing.T, url string, path string, payload string) string {
|
|
// Generated by curl-to-Go: https://mholt.github.io/curl-to-go
|
|
body := strings.NewReader("------WebKitFormBoundarycCRIderiXxJWEUcU\r\nContent-Disposition: form-data; name=\"\u1112\u1161 \u1112\u1161\"; filename=\"\u1112\u1161 \u1112\u1161\"\r\nContent-Type: application/octet-stream\r\n\r\n" + payload)
|
|
req, err := http.NewRequest("POST", url+"post", body)
|
|
dieMaybe(t, err)
|
|
req.Header.Set("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarycCRIderiXxJWEUcU")
|
|
req.Header.Set("Gossa-Path", path)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
dieMaybe(t, err)
|
|
defer resp.Body.Close()
|
|
bodyS, err := ioutil.ReadAll(resp.Body)
|
|
dieMaybe(t, err)
|
|
return trimSpaces(string(bodyS))
|
|
}
|
|
|
|
func postJSON(t *testing.T, url string, what string) string {
|
|
resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(what)))
|
|
dieMaybe(t, err)
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
dieMaybe(t, err)
|
|
return trimSpaces(string(body))
|
|
}
|
|
|
|
func fetchAndTestDefault(t *testing.T, url string) string {
|
|
body0 := get(t, url)
|
|
|
|
if !strings.Contains(body0, `<title>/</title>`) {
|
|
t.Fatal("error title")
|
|
}
|
|
|
|
if !strings.Contains(body0, `<h1 onclick="return titleClick(event)">./</h1>`) {
|
|
t.Fatal("error header")
|
|
}
|
|
|
|
if !strings.Contains(body0, `href="hols">hols/</a>`) {
|
|
t.Fatal("error hols folder")
|
|
}
|
|
|
|
if !strings.Contains(body0, `href="curimit@gmail.com%20%2840%25%29">curimit@gmail.com (40%)/</a>`) {
|
|
t.Fatal("error curimit@gmail.com (40%) folder")
|
|
}
|
|
|
|
if !strings.Contains(body0, `href="%E4%B8%AD%E6%96%87">中文/</a>`) {
|
|
t.Fatal("error 中文 folder")
|
|
}
|
|
|
|
if !strings.Contains(body0, `href="custom_mime_type.types">custom_mime_type.types</a>`) {
|
|
t.Fatal("error row custom_mime_type")
|
|
}
|
|
|
|
return body0
|
|
}
|
|
|
|
func doTestRegular(t *testing.T, url string, testExtra bool) {
|
|
var payload, path, body0, body1, body2 string
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching default path")
|
|
fetchAndTestDefault(t, url)
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching an invalid path - redirected to root")
|
|
fetchAndTestDefault(t, url+"../../")
|
|
fetchAndTestDefault(t, url+"hols/../../")
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching regular files")
|
|
body0 = get(t, url+"subdir_with%20space/file_with%20space.html")
|
|
body1 = get(t, url+"fancy-path/a")
|
|
if body0 != `<b>spacious!!</b> ` || body1 != `fancy! ` {
|
|
t.Fatal("fetching a regular file errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching a invalid file")
|
|
path = "../../../../../../../../../../etc/passwd"
|
|
if !testExtra && get(t, url+path) != `error` {
|
|
t.Fatal("fetching a invalid file didnt errored")
|
|
} else if testExtra {
|
|
fetchAndTestDefault(t, url+path) // extra path will just redirect to root dir
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test zipping of folder 中文")
|
|
files := getZip(t, url+"zip?zipPath=%2F%E4%B8%AD%E6%96%87%2F&zipName=%E4%B8%AD%E6%96%87")
|
|
if len(files) != 1 || files[0].Name != "檔案.html" {
|
|
t.Fatal("invalid zip generated")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test zip invalid path")
|
|
body0 = get(t, url+"zip?zipPath=%2Ftmp&zipName=subdir")
|
|
println(body0)
|
|
if body0 != `error` {
|
|
t.Fatal("zip passed for invalid path")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/AAA"]}`)
|
|
if body0 != `ok` {
|
|
t.Fatal("mkdir rpc errored")
|
|
}
|
|
|
|
body0 = fetchAndTestDefault(t, url)
|
|
if !strings.Contains(body0, `href="AAA">AAA/</a>`) {
|
|
t.Fatal("mkdir rpc folder not created")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test invalid mkdir rpc")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["../BBB"]}`)
|
|
if body0 != `error` {
|
|
t.Fatal("invalid mkdir rpc didnt errored #0")
|
|
}
|
|
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/../BBB"]}`)
|
|
if body0 != `error` {
|
|
t.Fatal("invalid mkdir rpc didnt errored #1")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test post file")
|
|
path = "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1" // "하 하" encoded
|
|
payload = "123 하"
|
|
body0 = postDummyFile(t, url, path, payload)
|
|
body1 = get(t, url+path)
|
|
body2 = fetchAndTestDefault(t, url)
|
|
if body0 != `ok` || body1 != payload || !strings.Contains(body2, `href="%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1">하 하</a>`) {
|
|
t.Fatal("post file errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test post file incorrect path")
|
|
body0 = postDummyFile(t, url, "%2E%2E"+path, payload)
|
|
if !strings.Contains(body0, `err`) {
|
|
t.Fatal("post file incorrect path didnt errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test mv rpc")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`)
|
|
body1 = fetchAndTestDefault(t, url)
|
|
if body0 != `ok` || strings.Contains(body1, `href="AAA">AAA/</a></td> </tr>`) {
|
|
t.Fatal("mv rpc errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test upload in new folder")
|
|
payload = "test"
|
|
body0 = postDummyFile(t, url, "%2Fhols%2FAAA%2Fabcdef", payload)
|
|
body1 = get(t, url+"hols/AAA/abcdef")
|
|
if body0 != `ok` || body1 != payload {
|
|
t.Fatal("upload in new folder errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test symlink, should succeed: ", testExtra)
|
|
body0 = get(t, url+"/support/")
|
|
hasListing := strings.Contains(body0, `readme.md`)
|
|
body1 = get(t, url+"/support/readme.md")
|
|
hasReadme := strings.Contains(body1, `the master branch is automatically built and pushed`)
|
|
|
|
if !testExtra && hasReadme {
|
|
t.Fatal("error symlink file reached where illegal")
|
|
} else if testExtra && !hasReadme {
|
|
t.Fatal("error symlink file unreachable")
|
|
}
|
|
if !testExtra && hasListing {
|
|
t.Fatal("error symlink folder reached where illegal")
|
|
} else if testExtra && !hasListing {
|
|
t.Fatal("error symlink folder unreachable")
|
|
}
|
|
|
|
if testExtra {
|
|
fmt.Println("\r\n~~~~~~~~~~ test symlink mkdir & cleanup")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/support/testfolder"]}`)
|
|
if body0 != `ok` {
|
|
t.Fatal("error symlink mkdir")
|
|
}
|
|
|
|
body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/support/testfolder"]}`)
|
|
if body0 != `ok` {
|
|
t.Fatal("error symlink rm")
|
|
}
|
|
}
|
|
|
|
fmt.Println("\r\n~~~~~~~~~~ test hidden file, should succeed: ", testExtra)
|
|
body0 = get(t, url+"/.testhidden")
|
|
hasHidden := strings.Contains(body0, `test`)
|
|
if !testExtra && hasHidden {
|
|
t.Fatal("error hidden file reached where illegal")
|
|
} else if testExtra && !hasHidden {
|
|
t.Fatal("error hidden file unreachable")
|
|
}
|
|
|
|
//
|
|
fmt.Println("\r\n~~~~~~~~~~ test upload in new folder")
|
|
payload = "test"
|
|
body0 = postDummyFile(t, url, "%2Fhols%2FAAA%2Fabcdef", payload)
|
|
body1 = get(t, url+"hols/AAA/abcdef")
|
|
if body0 != `ok` || body1 != payload {
|
|
t.Fatal("upload in new folder errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`)
|
|
if body0 != `ok` {
|
|
t.Fatal("cleanup errored #0")
|
|
}
|
|
|
|
body0 = get(t, url+"hols/AAA")
|
|
if !strings.Contains(body0, `error`) {
|
|
t.Fatal("cleanup errored #1")
|
|
}
|
|
|
|
body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/하 하"]}`)
|
|
if body0 != `ok` {
|
|
t.Fatal("cleanup errored #2")
|
|
}
|
|
|
|
fmt.Printf("\r\n=========\r\n")
|
|
}
|
|
|
|
func doTestReadonly(t *testing.T, url string) {
|
|
var payload, path, body0, body1 string
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching default path")
|
|
fetchAndTestDefault(t, url)
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching an invalid path - redirected to root")
|
|
fetchAndTestDefault(t, url+"../../")
|
|
fetchAndTestDefault(t, url+"hols/../../")
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching regular files")
|
|
body0 = get(t, url+"subdir_with%20space/file_with%20space.html")
|
|
body1 = get(t, url+"fancy-path/a")
|
|
if body0 != `<b>spacious!!</b> ` || body1 != `fancy! ` {
|
|
t.Fatal("fetching a regular file errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test fetching a invalid file")
|
|
path = "../../../../../../../../../../etc/passwd"
|
|
if get(t, url+path) != `error` {
|
|
t.Fatal("fetching a invalid file didnt errored")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/AAA"]}`)
|
|
if body0 == `ok` {
|
|
t.Fatal("mkdir rpc passed - should not be allowed")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test post file")
|
|
path = "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1" // "하 하" encoded
|
|
payload = "123 하"
|
|
body0 = postDummyFile(t, url, path, payload)
|
|
get(t, url+path)
|
|
if body0 == `ok` {
|
|
t.Fatal("post file passed - should not be allowed")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test mv rpc")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`)
|
|
fetchAndTestDefault(t, url)
|
|
if body0 == `ok` {
|
|
t.Fatal("mv rpc passed - should not be allowed")
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~
|
|
fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup")
|
|
body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`)
|
|
if body0 == `ok` {
|
|
t.Fatal("cleanup passed - should not be allowed")
|
|
}
|
|
|
|
fmt.Printf("\r\n=========\r\n")
|
|
}
|
|
|
|
func TestNormal(t *testing.T) {
|
|
fmt.Println("========== testing normal path ============")
|
|
doTestRegular(t, "http://127.0.0.1:8001/", false)
|
|
}
|
|
|
|
func TestExtra(t *testing.T) {
|
|
fmt.Println("========== testing extras options ============")
|
|
doTestRegular(t, "http://127.0.0.1:8001/fancy-path/", true)
|
|
}
|
|
|
|
func TestRo(t *testing.T) {
|
|
fmt.Println("========== testing read only ============")
|
|
doTestReadonly(t, "http://127.0.0.1:8001/")
|
|
}
|