mirror of
https://github.com/pldubouilh/gossa.git
synced 2026-08-28 12:56:35 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e9b85d4aa |
@@ -45,6 +45,11 @@ var verb = flag.Bool("verb", false, "verbosity")
|
||||
var skipHidden = flag.Bool("k", true, "\nskip hidden files")
|
||||
var ro = flag.Bool("ro", false, "read only mode (no upload, rename, move, etc...)")
|
||||
|
||||
type rpcCall struct {
|
||||
Call string `json:"call"`
|
||||
Args []string `json:"args"`
|
||||
}
|
||||
|
||||
var rootPath = ""
|
||||
var handler http.Handler
|
||||
|
||||
@@ -95,8 +100,9 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
p.Title = template.HTML(html.EscapeString(title))
|
||||
|
||||
for _, el := range files {
|
||||
info, err := el.Info()
|
||||
if err != nil {
|
||||
info, errInfo := el.Info()
|
||||
el, err := os.Stat(fullPath + "/" + el.Name())
|
||||
if err != nil || errInfo != nil {
|
||||
log.Println("error - cant stat a file", err)
|
||||
continue
|
||||
}
|
||||
@@ -104,7 +110,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
if *skipHidden && strings.HasPrefix(el.Name(), ".") {
|
||||
continue // dont print hidden files if we're not allowed
|
||||
}
|
||||
if *symlinks && info.Mode()&os.ModeSymlink != 0 {
|
||||
if !*symlinks && info.Mode()&os.ModeSymlink != 0 {
|
||||
continue // dont follow symlinks if we're not allowed
|
||||
}
|
||||
|
||||
@@ -121,7 +127,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
} else {
|
||||
sl := strings.Split(name, ".")
|
||||
ext := strings.ToLower(sl[len(sl)-1])
|
||||
row := rowTemplate{name, template.URL(href), humanize(info.Size()), ext}
|
||||
row := rowTemplate{name, template.URL(href), humanize(el.Size()), ext}
|
||||
p.RowsFiles = append(p.RowsFiles, row)
|
||||
}
|
||||
}
|
||||
@@ -129,7 +135,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
|
||||
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Header().Add("Content-Encoding", "gzip")
|
||||
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test
|
||||
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test, and only ~30% larger (compression remains still very effective, ~6x)
|
||||
check(err)
|
||||
defer gz.Close()
|
||||
tmpl.Execute(gz, p)
|
||||
@@ -146,8 +152,7 @@ func doContent(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
path := html.UnescapeString(r.URL.Path)
|
||||
defer exitPath(w, "get content", path)
|
||||
fullPath, err := enforcePath(path)
|
||||
check(err)
|
||||
fullPath := enforcePath(path)
|
||||
stat, errStat := os.Stat(fullPath)
|
||||
check(errStat)
|
||||
|
||||
@@ -170,9 +175,7 @@ func upload(w http.ResponseWriter, r *http.Request) {
|
||||
if err != nil && err != io.EOF { // errs EOF when no more parts to process
|
||||
check(err)
|
||||
}
|
||||
path, err = enforcePath(path)
|
||||
check(err)
|
||||
dst, err := os.Create(path)
|
||||
dst, err := os.Create(enforcePath(path))
|
||||
check(err)
|
||||
io.Copy(dst, part)
|
||||
w.Write([]byte("ok"))
|
||||
@@ -182,9 +185,8 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
|
||||
zipPath := r.URL.Query().Get("zipPath")
|
||||
zipName := r.URL.Query().Get("zipName")
|
||||
defer exitPath(w, "zip", zipPath)
|
||||
zipFullPath, err := enforcePath(zipPath)
|
||||
check(err)
|
||||
_, err = os.Lstat(zipFullPath)
|
||||
zipFullPath := enforcePath(zipPath)
|
||||
_, err := os.Lstat(zipFullPath)
|
||||
check(err)
|
||||
w.Header().Add("Content-Disposition", "attachment; filename=\""+zipName+".zip\"")
|
||||
zipWriter := zip.NewWriter(w)
|
||||
@@ -202,7 +204,7 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
|
||||
return nil // hidden files not allowed
|
||||
}
|
||||
if f.Mode()&os.ModeSymlink != 0 {
|
||||
check(errors.New("symlink not allowed in zip downloads")) // filepath.Walk doesnt support symlinks
|
||||
panic(errors.New("symlink not allowed in zip downloads")) // filepath.Walk doesnt support symlinks
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(f)
|
||||
@@ -223,52 +225,39 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func rpc(w http.ResponseWriter, r *http.Request) {
|
||||
type rpcCall struct {
|
||||
Call string `json:"call"`
|
||||
Args []string `json:"args"`
|
||||
}
|
||||
var err error
|
||||
var rpc rpcCall
|
||||
defer exitPath(w, "rpc", rpc)
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
check(err)
|
||||
json.Unmarshal(bodyBytes, &rpc)
|
||||
|
||||
path0, err := enforcePath(rpc.Args[0])
|
||||
path1 := ""
|
||||
check(err)
|
||||
if len(rpc.Args) > 1 {
|
||||
path1, err = enforcePath(rpc.Args[1])
|
||||
check(err)
|
||||
}
|
||||
|
||||
if rpc.Call == "mkdirp" {
|
||||
err = os.MkdirAll(path0, os.ModePerm)
|
||||
} else if rpc.Call == "mv" && len(rpc.Args) == 2 {
|
||||
err = os.Rename(path0, path1)
|
||||
err = os.MkdirAll(enforcePath(rpc.Args[0]), os.ModePerm)
|
||||
} else if rpc.Call == "mv" {
|
||||
err = os.Rename(enforcePath(rpc.Args[0]), enforcePath(rpc.Args[1]))
|
||||
} else if rpc.Call == "rm" {
|
||||
err = os.RemoveAll(path0)
|
||||
} else {
|
||||
err = errors.New("invalid rpc call")
|
||||
err = os.RemoveAll(enforcePath(rpc.Args[0]))
|
||||
}
|
||||
|
||||
check(err)
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
func enforcePath(p string) (string, error) {
|
||||
func enforcePath(p string) string {
|
||||
joined := filepath.Join(rootPath, strings.TrimPrefix(p, *extraPath))
|
||||
fp, err := filepath.Abs(joined)
|
||||
sl, _ := filepath.EvalSymlinks(fp) // err skipped as it would error for inexistent files (RPC check). The actual behaviour is tested below
|
||||
sl, _ := filepath.EvalSymlinks(fp) // err skipped as it would error for unexistent files (RPC check). The actual behaviour is tested below
|
||||
|
||||
// panic if we had a error getting absolute path,
|
||||
// ... or if path doesnt contain the prefix path we expect,
|
||||
// ... or if we're skipping hidden folders, and one is requested,
|
||||
// ... or if we're skipping symlinks, path exists, and a symlink out of bound requested
|
||||
if err != nil || !strings.HasPrefix(fp, rootPath) || *skipHidden && strings.Contains(p, "/.") || !*symlinks && len(sl) > 0 && !strings.HasPrefix(sl, rootPath) {
|
||||
return "", errors.New("invalid path")
|
||||
panic(errors.New("invalid path"))
|
||||
}
|
||||
|
||||
return fp, nil
|
||||
return fp
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -238,6 +238,8 @@ func doTestRegular(t *testing.T, url string, testExtra bool) {
|
||||
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`)
|
||||
body2 = get(t, url)
|
||||
hasMainListing := strings.Contains(body2, `href="support">support/</a>`)
|
||||
|
||||
if !testExtra && hasReadme {
|
||||
t.Fatal("error symlink file reached where illegal")
|
||||
@@ -249,6 +251,11 @@ func doTestRegular(t *testing.T, url string, testExtra bool) {
|
||||
} else if testExtra && !hasListing {
|
||||
t.Fatal("error symlink folder unreachable")
|
||||
}
|
||||
if !testExtra && hasMainListing {
|
||||
t.Fatal("error symlink folder where illegal")
|
||||
} else if testExtra && !hasMainListing {
|
||||
t.Fatal("error symlink folder unreachable")
|
||||
}
|
||||
|
||||
if testExtra {
|
||||
fmt.Println("\r\n~~~~~~~~~~ test symlink mkdir & cleanup")
|
||||
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
B!!!
|
||||
test
|
||||
B!!!
|
||||
|
||||
Vendored
+12
-23
@@ -153,11 +153,8 @@ function rpc (call, args, cb) {
|
||||
xhr.open('POST', location.origin + window.extraPath + '/rpc')
|
||||
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
|
||||
xhr.send(JSON.stringify({ call, args }))
|
||||
xhr.onload = () => cb(false)
|
||||
xhr.onerror = () => {
|
||||
flicker(sadBadge)
|
||||
cb(true)
|
||||
}
|
||||
xhr.onload = cb
|
||||
xhr.onerror = () => flicker(sadBadge)
|
||||
}
|
||||
|
||||
const mkdirCall = (path, cb) => rpc('mkdirp', [prependPath(path)], cb)
|
||||
@@ -318,36 +315,28 @@ const textTypes = ['.txt', '.rtf', '.md', '.markdown', '.log', '.yaml', '.yml']
|
||||
const isTextFile = src => src && textTypes.find(type => src.toLocaleLowerCase().includes(type))
|
||||
let fileEdited
|
||||
|
||||
function saveText (cb) {
|
||||
function saveText (quitting) {
|
||||
const formData = new FormData()
|
||||
formData.append(fileEdited, editor.value)
|
||||
const fname = fileEdited + ".swp"
|
||||
const path = encodeURIComponent(decodeURI(location.pathname) + fname)
|
||||
const path = encodeURIComponent(decodeURI(location.pathname) + fileEdited)
|
||||
upload(0, formData, path, () => {
|
||||
toast.style.display = 'none'
|
||||
cb()
|
||||
if (!quitting) return
|
||||
clearInterval(window.padTimer)
|
||||
window.onbeforeunload = null
|
||||
resetView()
|
||||
softPrev()
|
||||
refresh()
|
||||
}, () => {
|
||||
toast.style.display = 'block'
|
||||
if (!quitting) return
|
||||
alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up')
|
||||
})
|
||||
}
|
||||
|
||||
function padOff () {
|
||||
if (!isEditorMode()) { return }
|
||||
const swapfile = fileEdited + ".swp"
|
||||
saveText(() => {
|
||||
mvCall(prependPath(swapfile), prependPath(fileEdited), err => {
|
||||
if (err) {
|
||||
alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up')
|
||||
return
|
||||
}
|
||||
clearInterval(window.padTimer)
|
||||
window.onbeforeunload = null
|
||||
resetView()
|
||||
softPrev()
|
||||
refresh()
|
||||
})
|
||||
})
|
||||
saveText(true)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user