gocardless_pro 4.16.0 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b9d2dd26df920771e2ba47eb3c4d6adc895d27ef93e88e1ae94d0fa8422a653
|
|
4
|
+
data.tar.gz: caf7afa2dae19d07abd11037d3d58d6a33d245fcafb2944d4df36270de62e184
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 81d1de6bacdbf95c038b152df47b141fa72871f9f241e5c18b4fcc06553188796d1d5721c6694f91d60f3ff4bd672de02de834b64abee2521c0652b40477735e
|
|
7
|
+
data.tar.gz: c4392b0daee5b4845db700778f10da3aec0c69f126c5dd7b3dc14c07d3eadd94ed24d25a1dc02f1fc063998b52dd8a3550328d9ebef341bb6110c68c5f566641
|
|
@@ -48,7 +48,7 @@ module GoCardlessPro
|
|
|
48
48
|
|
|
49
49
|
options[:headers] ||= {}
|
|
50
50
|
options[:headers] = @headers.merge(options[:headers])
|
|
51
|
-
Request.new(@connection, method, @path_prefix + path, options).request
|
|
51
|
+
Request.new(@connection, method, @path_prefix + validate_path(path), options).request
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# inspect the API Service
|
|
@@ -61,6 +61,39 @@ module GoCardlessPro
|
|
|
61
61
|
|
|
62
62
|
private
|
|
63
63
|
|
|
64
|
+
# Check that a request path cannot move the request off the configured API URL.
|
|
65
|
+
#
|
|
66
|
+
# `path` is documented as a URL without the base domain, but Faraday resolves it against
|
|
67
|
+
# the connection's prefix the way a browser resolves a link: an absolute URL
|
|
68
|
+
# ('https://host/x') or a scheme-relative one ('//host/x') replaces the configured origin
|
|
69
|
+
# outright, while the Authorization header is still attached. A caller that passes
|
|
70
|
+
# untrusted input as `path` would therefore hand the token to a host of someone else's
|
|
71
|
+
# choosing, so anything that is not a relative reference is rejected before it is joined.
|
|
72
|
+
#
|
|
73
|
+
# Parsing with URI.parse is what Faraday's own merge does, so a value cannot be read as
|
|
74
|
+
# relative here and as absolute there. URI.parse is strict and raises on values a looser
|
|
75
|
+
# parser might read as a host, so a parse failure is rejected for the same reason.
|
|
76
|
+
#
|
|
77
|
+
# Dot segments are left alone: they resolve against the base URL and so cannot leave the
|
|
78
|
+
# configured origin, which is the property this guards.
|
|
79
|
+
#
|
|
80
|
+
# @param path [String] the path to validate
|
|
81
|
+
def validate_path(path)
|
|
82
|
+
parsed = begin
|
|
83
|
+
URI.parse(path.to_s)
|
|
84
|
+
rescue URI::InvalidURIError
|
|
85
|
+
raise ArgumentError, "Invalid request path #{path.inspect}: not a valid URL path"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
unless parsed.scheme.nil? && parsed.host.nil?
|
|
89
|
+
raise ArgumentError, "Invalid request path #{path.inspect}: a path may not specify " \
|
|
90
|
+
'a scheme or a host, only a location relative to the configured ' \
|
|
91
|
+
'API URL'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
path
|
|
95
|
+
end
|
|
96
|
+
|
|
64
97
|
def unpack_url(url)
|
|
65
98
|
path = URI.parse(url).path
|
|
66
99
|
[URI.join(url).to_s, path == '/' ? '' : path]
|
|
@@ -283,7 +283,7 @@ module GoCardlessPro
|
|
|
283
283
|
'User-Agent' => "#{user_agent}",
|
|
284
284
|
'Content-Type' => 'application/json',
|
|
285
285
|
'GoCardless-Client-Library' => 'gocardless-pro-ruby',
|
|
286
|
-
'GoCardless-Client-Version' => '4.
|
|
286
|
+
'GoCardless-Client-Version' => '4.17.0',
|
|
287
287
|
},
|
|
288
288
|
}
|
|
289
289
|
end
|
|
@@ -26,13 +26,40 @@ module GoCardlessPro
|
|
|
26
26
|
raise NotImplementedError
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
FORBIDDEN_URL_PARAM_CHARACTERS = %r{[/?\#\x00-\x1f\x7f]}.freeze
|
|
30
|
+
|
|
29
31
|
# take a URL with placeholder params and substitute them out for the actual value
|
|
30
32
|
# @param url [String] the URL with placeholders in
|
|
31
33
|
# @param param_map [Hash] a hash of placeholders and their actual values (which will be escaped)
|
|
32
34
|
def sub_url(url, param_map)
|
|
33
35
|
param_map.reduce(url) do |new_url, (param, value)|
|
|
34
|
-
new_url.gsub(":#{param}",
|
|
36
|
+
new_url.gsub(":#{param}", escape_url_param(param, value))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Escape a value before it is interpolated into a request path. A URL parameter is a
|
|
41
|
+
# single path segment, so values that could move the request to a different endpoint -
|
|
42
|
+
# path separators, control characters, '.', '..' (escaping can't make these safe - a
|
|
43
|
+
# resolver strips them regardless), and empty values - are rejected instead.
|
|
44
|
+
#
|
|
45
|
+
# @param param [String] the name of the URL parameter, used for error messages
|
|
46
|
+
# @param value [String] the value to escape
|
|
47
|
+
def escape_url_param(param, value)
|
|
48
|
+
value = value.to_s
|
|
49
|
+
|
|
50
|
+
raise ArgumentError, "No value provided for URL parameter '#{param}'" if value.empty?
|
|
51
|
+
|
|
52
|
+
if ['.', '..'].include?(value)
|
|
53
|
+
raise ArgumentError, "Invalid value for URL parameter '#{param}': '#{value}' " \
|
|
54
|
+
'would change which endpoint the request is sent to'
|
|
35
55
|
end
|
|
56
|
+
|
|
57
|
+
if FORBIDDEN_URL_PARAM_CHARACTERS.match?(value)
|
|
58
|
+
raise ArgumentError, "Invalid value for URL parameter '#{param}': '#{value}' " \
|
|
59
|
+
'contains a character that is not allowed in a path segment'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
CGI.escape(value)
|
|
36
63
|
end
|
|
37
64
|
end
|
|
38
65
|
end
|