You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
package core
|
|
|
|
import (
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
// Path safely holds a file path
|
|
type Path struct {
|
|
root string // root dir
|
|
rel string // relative path
|
|
sel string // selector path
|
|
}
|
|
|
|
// NewPath returns a new Path structure based on supplied root and relative path
|
|
func NewPath(root, rel string) *Path {
|
|
return &Path{root, rel, formatSelector(rel)}
|
|
}
|
|
|
|
// newSanitizedPath returns a new sanitized Path structure based on root and relative path
|
|
func newSanitizedPath(root, rel string) *Path {
|
|
return NewPath(root, sanitizeRawPath(root, rel))
|
|
}
|
|
|
|
// Remap remaps a Path to a new relative path, keeping previous selector
|
|
func (p *Path) Remap(newRel string) {
|
|
p.rel = sanitizeRawPath(p.root, newRel)
|
|
}
|
|
|
|
// Root returns file's root directory
|
|
func (p *Path) Root() string {
|
|
return p.root
|
|
}
|
|
|
|
// Relative returns the relative path
|
|
func (p *Path) Relative() string {
|
|
return p.rel
|
|
}
|
|
|
|
// Absolute returns the absolute path
|
|
func (p *Path) Absolute() string {
|
|
return path.Join(p.root, p.rel)
|
|
}
|
|
|
|
// Selector returns the formatted selector path
|
|
func (p *Path) Selector() string {
|
|
return p.sel
|
|
}
|
|
|
|
// RelativeDir returns the residing dir of the relative path
|
|
func (p *Path) RelativeDir() string {
|
|
return path.Dir(p.rel)
|
|
}
|
|
|
|
// SelectorDir returns the residing dir of the selector path
|
|
func (p *Path) SelectorDir() string {
|
|
return path.Dir(p.sel)
|
|
}
|
|
|
|
// Dir returns a Path object at the residing dir of the calling object (keeping separate selector intact)
|
|
func (p *Path) Dir() *Path {
|
|
return &Path{p.root, p.RelativeDir(), p.SelectorDir()}
|
|
}
|
|
|
|
// JoinRelative returns a string appended to the current relative path
|
|
func (p *Path) JoinRelative(newRel string) string {
|
|
return path.Join(p.rel, newRel)
|
|
}
|
|
|
|
// JoinPath appends the supplied string to the Path's relative and selector paths
|
|
func (p *Path) JoinPath(toJoin string) *Path {
|
|
return &Path{p.root, path.Join(p.rel, toJoin), path.Join(p.sel, toJoin)}
|
|
}
|
|
|
|
// formatSelector formats a relative path to a valid selector path
|
|
func formatSelector(rel string) string {
|
|
switch len(rel) {
|
|
case 0:
|
|
return "/"
|
|
case 1:
|
|
if rel[0] == '.' {
|
|
return "/"
|
|
}
|
|
return "/" + rel
|
|
default:
|
|
if rel[0] == '/' {
|
|
return rel
|
|
}
|
|
return "/" + rel
|
|
}
|
|
}
|
|
|
|
// sanitizeRawPath takes a root and relative path, and returns a sanitized relative path
|
|
func sanitizeRawPath(root, rel string) string {
|
|
// Start by cleaning
|
|
rel = path.Clean(rel)
|
|
|
|
if path.IsAbs(rel) {
|
|
// Absolute path, try trimming root and leading '/'
|
|
rel = strings.TrimPrefix(strings.TrimPrefix(rel, root), "/")
|
|
} else {
|
|
// Relative path, if back dir traversal give them server root
|
|
if strings.HasPrefix(rel, "..") {
|
|
rel = ""
|
|
}
|
|
}
|
|
|
|
return rel
|
|
}
|
|
|
|
// sanitizerUserRoot takes a generated user root directory and sanitizes it, returning a bool as to whether it's safe
|
|
func sanitizeUserRoot(root string) (string, bool) {
|
|
root = path.Clean(root)
|
|
if !strings.HasPrefix(root, "/home/") && strings.HasSuffix(root, "/"+userDir) {
|
|
return "", false
|
|
}
|
|
return root, true
|
|
}
|