Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/scrubber/sanitisation.go
2492 views
1
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package scrubber
6
7
import (
8
"crypto/md5"
9
"fmt"
10
"net/url"
11
"strconv"
12
"strings"
13
)
14
15
// sanitiserOption provides additional options to a sanitiser
16
type SanitiserOption func(*sanitiserOptions)
17
18
// SanitiseWithKeyName adds the keyname as metadata to the sanitised value
19
func SanitiseWithKeyName(keyName string) SanitiserOption {
20
return func(so *sanitiserOptions) {
21
so.keyName = keyName
22
}
23
}
24
25
type sanitiserOptions struct {
26
keyName string
27
}
28
29
// Sanitiser turns a potentially sensitive value into a non-sensitive value
30
type Sanitisatiser func(value string, opts ...SanitiserOption) string
31
32
// SanitiseRedact sanitises a single value by replacing it with a fixed string
33
func SanitiseRedact(value string, opts ...SanitiserOption) string {
34
options := mergeSanitiserOpts(opts)
35
if options.keyName != "" {
36
return "[redacted:" + options.keyName + "]"
37
}
38
return "[redacted]"
39
}
40
41
// SanitiseHash sanitises a single value by hashing it using MD5
42
func SanitiseHash(value string, opts ...SanitiserOption) string {
43
options := mergeSanitiserOpts(opts)
44
45
hash := md5.New()
46
_, _ = hash.Write([]byte(value))
47
48
res := fmt.Sprintf("[redacted:md5:%x", hash.Sum(nil))
49
if options.keyName != "" {
50
res += ":" + options.keyName
51
}
52
res += "]"
53
return res
54
}
55
56
// SanitiseHashURLPathSegments hashes the URL paths separately using SanitiseHash
57
func SanitiseHashURLPathSegments(value string, opts ...SanitiserOption) string {
58
options := mergeSanitiserOpts(opts)
59
60
u, err := url.Parse(value)
61
if err != nil {
62
// cannot parse as URL, treat as string
63
return SanitiseHash(value, opts...)
64
}
65
66
path := u.Path
67
query := u.RawQuery
68
u.Path = ""
69
u.RawQuery = ""
70
u.Fragment = ""
71
72
pathSegmentAllowList := []string{
73
"-",
74
"blob",
75
"blobs",
76
"commit",
77
"commits",
78
"issue",
79
"issues",
80
"merge_request",
81
"merge_requests",
82
"pull-request",
83
"pull-requests",
84
"pull",
85
"release",
86
"releases",
87
"src",
88
"tag",
89
"tags",
90
"tree",
91
// Bitbucket ENT-126
92
"users",
93
"projects",
94
"scm",
95
"repos",
96
"browse",
97
"branches",
98
}
99
100
var pathSegements []string
101
SEGMENTS:
102
for _, p := range strings.Split(path, "/") {
103
104
if len(p) <= 0 {
105
continue SEGMENTS
106
}
107
108
if _, err := strconv.Atoi(p); err == nil {
109
// it's a number, don't hash it
110
pathSegements = append(pathSegements, p)
111
continue SEGMENTS
112
}
113
114
p = strings.TrimPrefix(p, "~")
115
p = strings.TrimSuffix(p, ".git")
116
117
for _, a := range pathSegmentAllowList {
118
if p == a {
119
pathSegements = append(pathSegements, p)
120
continue SEGMENTS
121
}
122
}
123
124
pathSegements = append(pathSegements, SanitiseHash(p))
125
}
126
res := fmt.Sprintf("%s/%s", SanitiseHash(u.String()), strings.Join(pathSegements, "/"))
127
if len(query) > 0 {
128
res += fmt.Sprintf("?%s", SanitiseHash(query))
129
}
130
131
if options.keyName != "" {
132
res += " [" + options.keyName + "]"
133
}
134
return res
135
}
136
137
func mergeSanitiserOpts(opts []SanitiserOption) sanitiserOptions {
138
var res sanitiserOptions
139
for _, opt := range opts {
140
opt(&res)
141
}
142
return res
143
}
144
145