@@ -6,7 +6,10 @@ package getter
6
6
import (
7
7
"fmt"
8
8
"net/url"
9
+ "path"
10
+ "regexp"
9
11
"strings"
12
+ "unicode"
10
13
)
11
14
12
15
// GCSDetector implements Detector to detect GCS URLs and turn
@@ -18,23 +21,39 @@ func (d *GCSDetector) Detect(src, _ string) (string, bool, error) {
18
21
return "" , false , nil
19
22
}
20
23
21
- if strings .Contains (src , "googleapis.com/" ) {
24
+ if strings .Contains (src , ". googleapis.com/" ) {
22
25
return d .detectHTTP (src )
23
26
}
24
27
25
28
return "" , false , nil
26
29
}
27
30
28
31
func (d * GCSDetector ) detectHTTP (src string ) (string , bool , error ) {
32
+ src = path .Clean (src )
29
33
30
34
parts := strings .Split (src , "/" )
31
35
if len (parts ) < 5 {
32
36
return "" , false , fmt .Errorf (
33
37
"URL is not a valid GCS URL" )
34
38
}
39
+
35
40
version := parts [2 ]
41
+ if ! isValidGCSVersion (version ) {
42
+ return "" , false , fmt .Errorf (
43
+ "GCS URL version is not valid" )
44
+ }
45
+
36
46
bucket := parts [3 ]
47
+ if ! isValidGCSBucketName (bucket ) {
48
+ return "" , false , fmt .Errorf (
49
+ "GCS URL bucket name is not valid" )
50
+ }
51
+
37
52
object := strings .Join (parts [4 :], "/" )
53
+ if ! isValidGCSObjectName (object ) {
54
+ return "" , false , fmt .Errorf (
55
+ "GCS URL object name is not valid" )
56
+ }
38
57
39
58
url , err := url .Parse (fmt .Sprintf ("https://www.googleapis.com/storage/%s/%s/%s" ,
40
59
version , bucket , object ))
@@ -44,3 +63,92 @@ func (d *GCSDetector) detectHTTP(src string) (string, bool, error) {
44
63
45
64
return "gcs::" + url .String (), true , nil
46
65
}
66
+
67
+ func isValidGCSVersion (version string ) bool {
68
+ versionPattern := `^v\d+$`
69
+ if matched , _ := regexp .MatchString (versionPattern , version ); ! matched {
70
+ return false
71
+ }
72
+ return true
73
+ }
74
+
75
+ // Validate the bucket name using the following rules: https://cloud.google.com/storage/docs/naming-buckets
76
+ func isValidGCSBucketName (bucket string ) bool {
77
+ // Rule 1: Must be between 3 and 63 characters (or up to 222 if it contains dots, each component up to 63 chars)
78
+ if len (bucket ) < 3 || len (bucket ) > 63 {
79
+ if len (bucket ) > 63 && len (bucket ) <= 222 {
80
+ // If it contains dots, each segment between dots must be <= 63 chars
81
+ components := strings .Split (bucket , "." )
82
+ for _ , component := range components {
83
+ if len (component ) > 63 {
84
+ return false
85
+ }
86
+ }
87
+ } else {
88
+ return false
89
+ }
90
+ }
91
+
92
+ // Rule 2: Bucket name cannot start or end with a hyphen, dot, or underscore
93
+ if bucket [0 ] == '-' || bucket [0 ] == '.' || bucket [len (bucket )- 1 ] == '-' || bucket [len (bucket )- 1 ] == '.' || bucket [len (bucket )- 1 ] == '_' {
94
+ return false
95
+ }
96
+
97
+ // Rule 3: Bucket name cannot contain spaces
98
+ if strings .Contains (bucket , " " ) {
99
+ return false
100
+ }
101
+
102
+ // Rule 4: Bucket name cannot be an IP address (only digits and dots, e.g., 192.168.5.4)
103
+ ipPattern := `^(\d{1,3}\.){3}\d{1,3}$`
104
+ if matched , _ := regexp .MatchString (ipPattern , bucket ); matched {
105
+ return false
106
+ }
107
+
108
+ // Rule 5: Bucket name cannot start with "goog"
109
+ if strings .HasPrefix (bucket , "goog" ) {
110
+ return false
111
+ }
112
+
113
+ // Rule 6: Bucket name cannot contain "google" or common misspellings like "g00gle"
114
+ googlePattern := `google|g00gle`
115
+ if matched , _ := regexp .MatchString (googlePattern , bucket ); matched {
116
+ return false
117
+ }
118
+
119
+ // Rule 7: Bucket name can only contain lowercase letters, digits, dashes, underscores, and dots
120
+ bucketPattern := `^[a-z0-9\-_\.]+$`
121
+ if matched , _ := regexp .MatchString (bucketPattern , bucket ); ! matched {
122
+ return false
123
+ }
124
+
125
+ return true
126
+ }
127
+
128
+ // Validate the object name using the following rules: https://cloud.google.com/storage/docs/naming-objects
129
+ func isValidGCSObjectName (object string ) bool {
130
+ // Rule 1: Object names cannot contain Carriage Return (\r) or Line Feed (\n) characters
131
+ if strings .Contains (object , "\r " ) || strings .Contains (object , "\n " ) {
132
+ return false
133
+ }
134
+
135
+ // Rule 2: Object names cannot start with '.well-known/acme-challenge/'
136
+ if strings .HasPrefix (object , ".well-known/acme-challenge/" ) {
137
+ return false
138
+ }
139
+
140
+ // Rule 3: Object names cannot be exactly '.' or '..'
141
+ if object == "." || object == ".." {
142
+ return false
143
+ }
144
+
145
+ // Rule 4: Ensure that the object name contains only valid Unicode characters
146
+ // (for simplicity, let's ensure it's not empty and does not contain any forbidden control characters)
147
+ for _ , r := range object {
148
+ if ! unicode .IsPrint (r ) && ! unicode .IsSpace (r ) && r != '.' && r != '-' && r != '/' {
149
+ return false
150
+ }
151
+ }
152
+
153
+ return true
154
+ }
0 commit comments