@@ -18,10 +18,44 @@ func newUriValidationError(msg string, base string, redirect string) UriValidati
18
18
return UriValidationError (fmt .Sprintf ("%s: %s / %s" , msg , base , redirect ))
19
19
}
20
20
21
+ // Parse urls, resolving uri references to base url
22
+ func ParseUrls (baseUrl , redirectUrl string ) (retBaseUrl , retRedirectUrl * url.URL , err error ) {
23
+ var base , redirect * url.URL
24
+ // parse base url
25
+ if base , err = url .Parse (baseUrl ); err != nil {
26
+ return nil , nil , err
27
+ }
28
+
29
+ // parse redirect url
30
+ if redirect , err = url .Parse (redirectUrl ); err != nil {
31
+ return nil , nil , err
32
+ }
33
+
34
+ // must not have fragment
35
+ if base .Fragment != "" || redirect .Fragment != "" {
36
+ return nil , nil , newUriValidationError ("url must not include fragment." , baseUrl , redirectUrl )
37
+ }
38
+
39
+ // Scheme must match
40
+ if redirect .Scheme != base .Scheme {
41
+ return nil , nil , newUriValidationError ("scheme mismatch" , baseUrl , redirectUrl )
42
+ }
43
+
44
+ // Host must match
45
+ if redirect .Host != base .Host {
46
+ return nil , nil , newUriValidationError ("host mismatch" , baseUrl , redirectUrl )
47
+ }
48
+
49
+ // resolve references to base url
50
+ retBaseUrl = (& url.URL {Scheme : base .Scheme , Host : base .Host , Path : "/" }).ResolveReference (& url.URL {Path : base .Path })
51
+ retRedirectUrl = (& url.URL {Scheme : base .Scheme , Host : base .Host , Path : "/" }).ResolveReference (& url.URL {Path : redirect .Path })
52
+ return
53
+ }
54
+
21
55
// ValidateUriList validates that redirectUri is contained in baseUriList.
22
56
// baseUriList may be a string separated by separator.
23
57
// If separator is blank, validate only 1 URI.
24
- func ValidateUriList (baseUriList string , redirectUri string , separator string ) error {
58
+ func ValidateUriList (baseUriList string , redirectUri string , separator string ) ( realRedirectUri string , err error ) {
25
59
// make a list of uris
26
60
var slist []string
27
61
if separator != "" {
@@ -32,71 +66,44 @@ func ValidateUriList(baseUriList string, redirectUri string, separator string) e
32
66
}
33
67
34
68
for _ , sitem := range slist {
35
- err : = ValidateUri (sitem , redirectUri )
69
+ realRedirectUri , err = ValidateUri (sitem , redirectUri )
36
70
// validated, return no error
37
71
if err == nil {
38
- return nil
72
+ return realRedirectUri , nil
39
73
}
40
74
41
75
// if there was an error that is not a validation error, return it
42
76
if _ , iok := err .(UriValidationError ); ! iok {
43
- return err
77
+ return "" , err
44
78
}
45
79
}
46
80
47
- return newUriValidationError ("urls don't validate" , baseUriList , redirectUri )
81
+ return "" , newUriValidationError ("urls don't validate" , baseUriList , redirectUri )
48
82
}
49
83
50
84
// ValidateUri validates that redirectUri is contained in baseUri
51
- func ValidateUri (baseUri string , redirectUri string ) error {
85
+ func ValidateUri (baseUri string , redirectUri string ) ( realRedirectUri string , err error ) {
52
86
if baseUri == "" || redirectUri == "" {
53
- return errors .New ("urls cannot be blank." )
87
+ return "" , errors .New ("urls cannot be blank." )
54
88
}
55
89
56
- // parse base url
57
- base , err := url .Parse (baseUri )
58
- if err != nil {
59
- return err
60
- }
61
-
62
- // parse passed url
63
- redirect , err := url .Parse (redirectUri )
90
+ base , redirect , err := ParseUrls (baseUri , redirectUri )
64
91
if err != nil {
65
- return err
66
- }
67
-
68
- // must not have fragment
69
- if base .Fragment != "" || redirect .Fragment != "" {
70
- return errors .New ("url must not include fragment." )
71
- }
72
-
73
- // check if urls match
74
- if base .Scheme != redirect .Scheme {
75
- return newUriValidationError ("scheme mismatch" , baseUri , redirectUri )
76
- }
77
- if base .Host != redirect .Host {
78
- return newUriValidationError ("host mismatch" , baseUri , redirectUri )
92
+ return "" , err
79
93
}
80
94
81
95
// allow exact path matches
82
96
if base .Path == redirect .Path {
83
- return nil
97
+ return redirect . String (), nil
84
98
}
85
99
86
100
// ensure prefix matches are actually subpaths
87
101
requiredPrefix := strings .TrimRight (base .Path , "/" ) + "/"
88
102
if ! strings .HasPrefix (redirect .Path , requiredPrefix ) {
89
- return newUriValidationError ("path is not a subpath" , baseUri , redirectUri )
90
- }
91
-
92
- // ensure prefix matches don't contain path traversals
93
- for _ , s := range strings .Split (strings .TrimPrefix (redirect .Path , requiredPrefix ), "/" ) {
94
- if s == ".." {
95
- return newUriValidationError ("subpath cannot contain path traversal" , baseUri , redirectUri )
96
- }
103
+ return "" , newUriValidationError ("path prefix doesn't match" , baseUri , redirectUri )
97
104
}
98
105
99
- return nil
106
+ return redirect . String (), nil
100
107
}
101
108
102
109
// Returns the first uri from an uri list
0 commit comments