-
Notifications
You must be signed in to change notification settings - Fork 0
/
sanitize.go
160 lines (130 loc) · 4.15 KB
/
sanitize.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
package backend
import (
"path/filepath"
"regexp"
"strings"
"time"
"github.com/gravitational/trace"
"github.com/jonboulle/clockwork"
)
// errorMessage is the error message to return when invalid input is provided by the caller.
const errorMessage = "special characters are not allowed in resource names, please use name composed only from characters, hyphens and dots"
// whitelistPattern is the pattern of allowed characters for each key within
// the path.
var whitelistPattern = regexp.MustCompile(`^[0-9A-Za-z@_:.-]*$`)
// isStringSafe checks if the passed in string conforms to the whitelist.
func isStringSafe(s string) bool {
if strings.Contains(s, "..") {
return false
}
if strings.Contains(s, string(filepath.Separator)) {
return false
}
return whitelistPattern.MatchString(s)
}
// isSliceSafe checks if the passed in slice conforms to the whitelist.
func isSliceSafe(slice []string) bool {
for _, s := range slice {
if !isStringSafe(s) {
return false
}
}
return true
}
// Sanitizer wraps a Backend implementation to make sure all values requested
// of the backend are whitelisted.
type Sanitizer struct {
backend Backend
}
// NewSanitizer returns a new Sanitizer.
func NewSanitizer(backend Backend) *Sanitizer {
return &Sanitizer{
backend: backend,
}
}
// Backend returns the underlying backend. Useful when knowing the type of
// backend is important (for example, can the backend support forking).
func (s *Sanitizer) Backend() Backend {
return s.backend
}
// GetKeys returns a list of keys for a given path.
func (s *Sanitizer) GetKeys(bucket []string) ([]string, error) {
if !isSliceSafe(bucket) {
return nil, trace.BadParameter(errorMessage)
}
return s.backend.GetKeys(bucket)
}
// CreateVal creates value with a given TTL and key in the bucket. If the
// value already exists, returns trace.AlreadyExistsError.
func (s *Sanitizer) CreateVal(bucket []string, key string, val []byte, ttl time.Duration) error {
if !isSliceSafe(bucket) {
return trace.BadParameter(errorMessage)
}
if !isStringSafe(key) {
return trace.BadParameter(errorMessage)
}
return s.backend.CreateVal(bucket, key, val, ttl)
}
// UpsertVal updates or inserts value with a given TTL into a bucket. Use
// backend.ForeverTTL for no TTL.
func (s *Sanitizer) UpsertVal(bucket []string, key string, val []byte, ttl time.Duration) error {
if !isSliceSafe(bucket) {
return trace.BadParameter(errorMessage)
}
if !isStringSafe(key) {
return trace.BadParameter(errorMessage)
}
return s.backend.UpsertVal(bucket, key, val, ttl)
}
// GetVal returns a value for a given key in the bucket.
func (s *Sanitizer) GetVal(bucket []string, key string) ([]byte, error) {
if !isSliceSafe(bucket) {
return nil, trace.BadParameter(errorMessage)
}
if !isStringSafe(key) {
return nil, trace.BadParameter(errorMessage)
}
return s.backend.GetVal(bucket, key)
}
// DeleteKey deletes a key in a bucket.
func (s *Sanitizer) DeleteKey(bucket []string, key string) error {
if !isSliceSafe(bucket) {
return trace.BadParameter(errorMessage)
}
if !isStringSafe(key) {
return trace.BadParameter(errorMessage)
}
return s.backend.DeleteKey(bucket, key)
}
// DeleteBucket deletes the bucket by a given path.
func (s *Sanitizer) DeleteBucket(path []string, bucket string) error {
if !isSliceSafe(path) {
return trace.BadParameter(errorMessage)
}
if !isStringSafe(bucket) {
return trace.BadParameter(errorMessage)
}
return s.backend.DeleteBucket(path, bucket)
}
// AcquireLock grabs a lock that will be released automatically after a TTL.
func (s *Sanitizer) AcquireLock(token string, ttl time.Duration) error {
if !isStringSafe(token) {
return trace.BadParameter(errorMessage)
}
return s.backend.AcquireLock(token, ttl)
}
// ReleaseLock forces lock release before the TTL has expired.
func (s *Sanitizer) ReleaseLock(token string) error {
if !isStringSafe(token) {
return trace.BadParameter(errorMessage)
}
return s.backend.ReleaseLock(token)
}
// Close releases the resources taken up by this backend
func (s *Sanitizer) Close() error {
return s.backend.Close()
}
// Clock returns clock used by this backend
func (s *Sanitizer) Clock() clockwork.Clock {
return s.backend.Clock()
}