/
path.go
141 lines (124 loc) · 3.61 KB
/
path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2017 Atelier Disko. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
)
var (
PrettyPathRoot string
ErrTreeRootNotFound = errors.New("no tree root found")
ErrRepositoryNotFound = errors.New("no repository found")
)
// Does not include the tree root directort.
func prettyPath(path string) string {
rel, _ := filepath.Rel(filepath.Dir(PrettyPathRoot), path)
return rel
}
// Ensures given path is absolute and below root path, if not will
// return error. Used for preventing path traversal. Accepts absolute and
// relative paths.
//
// Although the Go http stack will resolve all kinds of dotted path
// segments and finally redirect to the non-relative path (i.e. `GET
// ../../etc/shadow` becomes `GET /etc/shadow`), this func is used as
// an additional safety measure. It can also be used on other parts of
// the URL that are not safe by default (i.e. the query string).
func checkSafePath(path string, root string) error {
if !filepath.IsAbs(path) {
path = filepath.Join(root, path)
}
path = filepath.Clean(path)
if path == root {
return nil
}
if strings.HasPrefix(path, root) {
return nil
}
return fmt.Errorf("directory traversal detected, failed check: path %s, root %s", path, root)
}
// Tries to find root directory either by looking at args or the
// current working directory. This function needs the full path to the
// binary as a first argument and optionally an explicitly given path
// as the second argument.
func detectTreeRoot(binary string, given string) (string, error) {
var here string
if given != "" {
here = given
} else {
// When no path is given as an argument, take the path to the
// process itself. This makes sure that when opening the binary from
// Finder the folder it is stored in is used.
here = filepath.Dir(binary)
}
here, err := filepath.Abs(here)
if err != nil {
return here, err
}
p, err := filepath.EvalSymlinks(here)
if err != nil {
return "", err
}
return p, nil
}
// Searches beginning at given path up, until it finds a directory
// containing a ".git" directory. We differentiate between submodules
// having a ".git" file and regular repositories where ".git" is an
// actual directory.
func detectRepository(treeRoot string, searchSubmodule bool) (string, error) {
var path = treeRoot
for path != "." && path != "/" {
s, err := os.Stat(filepath.Join(path, ".git"))
if err == nil {
if searchSubmodule && s.Mode().IsRegular() {
return path, nil
} else if !searchSubmodule && s.Mode().IsDir() {
return path, nil
}
}
path, err = filepath.Abs(path + "/..")
if err != nil {
return "", err
}
}
return "", ErrRepositoryNotFound
}
// Checks if any of the path segments in the given path, matches regexp.
func anyPathSegmentMatches(path string, r *regexp.Regexp) bool {
for path != "." {
b := filepath.Base(path)
if IgnoreNodesRegexp.MatchString(b) {
return true
}
path = filepath.Dir(path)
}
return false
}
// Finds an order number embedded into given path segment and
// returns it. If none is found, returns 0.
func orderNumber(segment string) uint64 {
s := NodePathTitleRegexp.FindStringSubmatch(segment)
if len(s) > 2 {
parsed, _ := strconv.ParseUint(s[0], 10, 64)
return parsed
}
return 0
}
// Removes order numbers from path segment, if present.
func removeOrderNumber(segment string) string {
s := NodePathTitleRegexp.FindStringSubmatch(segment)
if len(s) == 0 {
return segment
}
if len(s) > 2 {
return s[2]
}
return s[1]
}