39 lines
1.3 KiB
Go
39 lines
1.3 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ErrOutsideIngestRoot is the one refusal for every bad ingest path. It never
|
|
// carries the underlying error: "no such file" versus "permission denied"
|
|
// would let a caller probe which server files exist (PR #152 review).
|
|
var ErrOutsideIngestRoot = errors.New("ingest path must be an existing file or directory inside brain/raw/")
|
|
|
|
// ResolveIngestPath confines a caller-supplied ingest path to brain/raw/, the
|
|
// inbox the watcher already owns (hyperguild#149). Relative paths resolve
|
|
// against that root; symlinks are evaluated before the containment check, so
|
|
// a link inside raw/ that points elsewhere is refused. The path must exist.
|
|
func ResolveIngestPath(brainDir, p string) (string, error) {
|
|
if strings.TrimSpace(p) == "" {
|
|
return "", ErrOutsideIngestRoot
|
|
}
|
|
root, err := filepath.EvalSymlinks(filepath.Join(brainDir, "raw"))
|
|
if err != nil {
|
|
return "", ErrOutsideIngestRoot
|
|
}
|
|
if !filepath.IsAbs(p) {
|
|
p = filepath.Join(root, p)
|
|
}
|
|
real, err := filepath.EvalSymlinks(filepath.Clean(p))
|
|
if err != nil {
|
|
return "", ErrOutsideIngestRoot
|
|
}
|
|
rel, err := filepath.Rel(root, real)
|
|
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || filepath.IsAbs(rel) {
|
|
return "", ErrOutsideIngestRoot
|
|
}
|
|
return real, nil
|
|
}
|