forked from Velocidex/velociraptor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpathspec.go
163 lines (129 loc) · 4.3 KB
/
pathspec.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package accessors
import (
"net/url"
errors "github.com/pkg/errors"
"www.velocidex.com/golang/velociraptor/json"
)
var (
InvalidPathSpec = errors.New("Invalid PathSpec")
)
/*
A PathSpec is a more precise indication of a path to open a source
of data. In Velociraptor, access to data is provided by the use of
"Accessors" - a registered driver capable of reading data from
certain sources.
Accessors can delegate to other accessors using the PathSpec. This
delegation allows an accessor to receive additional information in
order to properly create the filesystem abstraction.
For example, consider the "zip" accessor which is responsible for
reading compressed archives. In order to retrieve a file inside the
zip file, the accessor needs the following pieces of data:
1. A delegate accessor to use to open the underlying zip file.
2. A path to provide to the delegate accessor.
3. The name of the zip member to open.
For example the following path spec:
{"Accessor": "file",
"DelegatePath": "/tmp/file.zip",
"Path": "zip_member.exe"}
Provides all this information.
PathSpecs are supposed to be serialized into strings and passed as
the filename to plugins that require file paths. The PathSpec is
just a more detailed path representation and is treated everywhere
as a plain string (json encoded).
Therefore the following path spec is valid for a recursive path
{"Accessor": "zip",
"DelegatePath": "{\"Accessor\": \"file\", \"DelegatePath\": \"/tmp/file.zip\", \"Path\": \"embedded.zip\"}",
"Path": "zip_member.exe"}
Given to the zip accessor, this PathSpec means to use the "zip"
accessor to open a member "embedded.zip" inside a file
"/tmp/file.zip", then to search within that embedded zip for a
"zip_member.exe"
For convenience, the PathSpec also supports a structured delegate so
the following serialization is also valid.
{"Accessor": "zip",
"Delegate": {
"Accessor": "file",
"DelegatePath": "/tmp/file.zip",
"Path": "embedded.zip"
},
"Path": "zip_member.exe"}
## Note:
In previous versions, the PathSpec abstraction was provided by
mapping URL parts to the fields above. This proved problematic
because URL encoding is lossy and not robust enough for round
tripping of all paths.
It also produces difficult to read paths. The old URL way is
deprecated but still supported - it will eventually be dropped.
*/
type PathSpec struct {
DelegateAccessor string `json:"DelegateAccessor,omitempty"`
DelegatePath string `json:"DelegatePath,omitempty"`
// This standard for DelegatePath above and allows a more
// convenient way to pass recursive pathspecs down.
Delegate *PathSpec `json:"Delegate,omitempty"`
Path string `json:"Path,omitempty"`
// Keep track of if the pathspec came from a URL based for
// backwards compatibility.
url_based bool
}
func (self PathSpec) Copy() *PathSpec {
result := self
if result.Delegate != nil {
result.Delegate = result.Delegate.Copy()
}
return &result
}
func (self PathSpec) GetDelegatePath() string {
// We allow the delegate path to be encoded as a nested pathspec
// for covenience.
if self.Delegate != nil {
return self.Delegate.String()
}
return self.DelegatePath
}
func (self PathSpec) GetDelegateAccessor() string {
return self.DelegateAccessor
}
func (self PathSpec) GetPath() string {
return self.Path
}
func (self PathSpec) String() string {
if self.url_based {
result := url.URL{
Scheme: self.DelegateAccessor,
Path: self.DelegatePath,
Fragment: self.Path,
}
return result.String()
}
return json.MustMarshalString(self)
}
func PathSpecFromString(parsed string) (*PathSpec, error) {
if len(parsed) == 0 {
return &PathSpec{}, nil
}
// It is a serialized JSON object.
if parsed[0] == '{' {
result := &PathSpec{}
err := json.Unmarshal([]byte(parsed), result)
return result, err
}
// It can be a URL
parsed_url, err := url.Parse(parsed)
if err != nil {
return nil, InvalidPathSpec
}
// It looks like a windows path not a URL
if len(parsed_url.Scheme) == 1 {
return &PathSpec{
DelegatePath: parsed,
}, nil
}
// Support urls for backwards compatibility.
return &PathSpec{
DelegateAccessor: parsed_url.Scheme,
DelegatePath: parsed_url.Path,
Path: parsed_url.Fragment,
url_based: true,
}, nil
}