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: 2d65d236ad3d172135d059974b555d35b0bb435707e7577255fd01c653953e62
4
- data.tar.gz: 98f22a67f30a5d41f777a7f8793221ec77da6cb3cf823a615f42ef88c3af8fa0
3
+ metadata.gz: 5b9d2dd26df920771e2ba47eb3c4d6adc895d27ef93e88e1ae94d0fa8422a653
4
+ data.tar.gz: caf7afa2dae19d07abd11037d3d58d6a33d245fcafb2944d4df36270de62e184
5
5
  SHA512:
6
- metadata.gz: b61a81df21d0ad24ae08ed30a1b6d0085d2604e24a678ec967ad68bfde342112590f326083a394a7a51bb2712761c58a8db54cb45a6f09556de6740ec0bc6e4d
7
- data.tar.gz: 63f9d6326253bc5046f2f677d5e1e92d43f979261a75c679d2dc5a0fe62ff1108180dd506a820f1965f48964f386e6111841d718b03a60e42b4618496367d079
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.16.0',
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}", CGI.escape(value))
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
@@ -3,5 +3,5 @@ end
3
3
 
4
4
  module GoCardlessPro
5
5
  # Current version of the GC gem
6
- VERSION = '4.16.0'
6
+ VERSION = '4.17.0'
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gocardless_pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.16.0
4
+ version: 4.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless