openapi_first 4.0.1 → 4.1.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: dc1e6456e7c11e5079255b24e6407f61d9e94677fe07bef54000c26fe1174e05
4
- data.tar.gz: 313d87080b433ca45defd7a47a811f6f2a91b93804a6655b9121b86d95c2e2f7
3
+ metadata.gz: ac70785f7221fecd32d0a1408c8c12ee14ea75f5d45be6c9c1efce38e735d02e
4
+ data.tar.gz: 0cea3e400f9eed814fc9924c551893d78444920d99f7573d59426a7fa7a61068
5
5
  SHA512:
6
- metadata.gz: 6a04433e95586c8cc5ad34f791e1a2fc598584be5e23c7e7518c5eb9bd04d77c96fb3b1fe9330a16e4a9060b119b835e42863a11a1abb77c8f5b2cbba246fdcc
7
- data.tar.gz: bb8a46ea7aaf083819cac30ede78fe6f560f339f2d8282d727e111b3e504dab42aa0a7b82d1372bd060a5217818f694cadad11f71085e15baed7a10c48f5578e
6
+ metadata.gz: 9c8883be1aad4a502ac4596ecb91765b855e2d515ec3e6ad11519a350ed43f29ae591645b71f7f7da709775cd1322c9dd4803f1474032bf5e8004abfd4b0eb51
7
+ data.tar.gz: 21e78cbcc5607436c1a32e108f786b0ddfc7ad434d2d103cee6f506eb90bef74d6dfcc4977ce748b612aeeab0c9e50c3a372c77da66a291275ead799bf677226
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.1.0
6
+
7
+ - Query and cookie parameter values with invalid encoding (like `?search=%C3`) produce a 400 response now. Before such a value passed validation or raised an `ArgumentError`. This applies to documented and undocumented query parameter, but only to documented cookie parameters.
8
+ - A query string with more parameters than Rack accepts (4096 by default) produces a 400 response now instead of raising `Rack::QueryParser::QueryLimitError`. See the [rack documentation](https://rack.github.io/rack/main/index.html#configuration) about how to configure this.
9
+
5
10
  ## 4.0.1
6
11
 
7
12
  - Fix: Reduced memory usage with large API descriptions. Schemas no longer each deep-copy (stringify keys) the whole OAD to build a root schema. This is a json_schemer specific optimization.
@@ -126,10 +126,10 @@ module OpenapiFirst
126
126
  cookie = build_parameters(grouped[:cookie])
127
127
  ParsedParameters.new(
128
128
  all: [*path, *query, *header, *cookie].freeze,
129
- path_parser: grouped[:path] && ParametersParser.new(path),
129
+ path_parser: grouped[:path] && ParametersParser.new(path, check_encoding: false),
130
130
  query_parser: QueryStringParser.new(query),
131
- header_parser: grouped[:header] && ParametersParser.new(header),
132
- cookie_parser: grouped[:cookie] && ParametersParser.new(cookie),
131
+ header_parser: grouped[:header] && ParametersParser.new(header, check_encoding: false),
132
+ cookie_parser: grouped[:cookie] && ParametersParser.new(cookie, check_encoding: true),
133
133
  path_schema: build_parameter_schema(grouped[:path]),
134
134
  query_schema: build_parameter_schema(grouped[:query]),
135
135
  header_schema: build_parameter_schema(grouped[:header]),
@@ -5,8 +5,10 @@ module OpenapiFirst
5
5
  # @visibility private
6
6
  class ParametersParser
7
7
  # @param parameters [Array<Parameter>]
8
- def initialize(parameters)
8
+ # @param check_encoding [Boolean] Raise Rack::Utils::InvalidParameterError if a value has an invalid encoding.
9
+ def initialize(parameters, check_encoding:)
9
10
  @parameters = parameters
11
+ @check_encoding = check_encoding
10
12
  end
11
13
 
12
14
  attr_reader :parameters
@@ -16,8 +18,18 @@ module OpenapiFirst
16
18
  parameters.each_with_object({}) do |parameter, result|
17
19
  next unless parameters_hash.key?(parameter.name)
18
20
 
19
- result[parameter.name] = parameter.unpack_and_convert(parameters_hash[parameter.name])
21
+ value = parameters_hash[parameter.name]
22
+ check_encoding!(value) if @check_encoding
23
+ result[parameter.name] = parameter.unpack_and_convert(value)
20
24
  end
21
25
  end
26
+
27
+ private
28
+
29
+ def check_encoding!(value)
30
+ return if !value.is_a?(::String) || value.valid_encoding?
31
+
32
+ raise Rack::Utils::InvalidParameterError, "invalid encoding (#{value.inspect})"
33
+ end
22
34
  end
23
35
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rack'
4
+ require 'rack/query_parser'
4
5
  require_relative 'parameters_parser'
5
6
 
6
7
  module OpenapiFirst
@@ -10,11 +11,15 @@ module OpenapiFirst
10
11
  DEEP_PROP = '\[([\w-]+)\]$'
11
12
  private_constant :DEEP_PROP
12
13
 
14
+ # Rack < 3.1 does not know about Rack::BadRequest
15
+ BAD_REQUEST = defined?(Rack::BadRequest) ? Rack::BadRequest : Rack::QueryParser::QueryLimitError
16
+ private_constant :BAD_REQUEST
17
+
13
18
  # @param parameters [Array<Parameter>]
14
19
  def initialize(parameters)
15
20
  @parameters = parameters
16
21
  @deep_object_parameters, flat_parameters = parameters.partition(&:deep_object?)
17
- @flat_parser = ParametersParser.new(flat_parameters)
22
+ @flat_parser = ParametersParser.new(flat_parameters, check_encoding: false)
18
23
  @deep_object_properties = {}
19
24
  @deep_object_regex = {}
20
25
  @deep_object_parameters.each do |parameter|
@@ -54,16 +59,23 @@ module OpenapiFirst
54
59
  return if unknown.empty?
55
60
 
56
61
  unknown
62
+ rescue Rack::Utils::InvalidParameterError
63
+ nil
57
64
  end
58
65
 
59
66
  private
60
67
 
61
68
  def parse_query(query_string)
62
69
  Rack::Utils.parse_query(query_string) do |string|
63
- Rack::Utils.unescape(string)
70
+ unescaped = Rack::Utils.unescape(string)
71
+ next unescaped if unescaped.valid_encoding?
72
+
73
+ raise Rack::Utils::InvalidParameterError, "invalid encoding (#{string})"
64
74
  rescue ArgumentError => e
65
75
  raise Rack::Utils::InvalidParameterError, e.message
66
76
  end
77
+ rescue BAD_REQUEST => e
78
+ raise Rack::Utils::InvalidParameterError, e.message
67
79
  end
68
80
 
69
81
  def parse_deep_object(parameter, parsed_query)
@@ -61,6 +61,9 @@ module OpenapiFirst
61
61
  query, query_error = parse_query(request.env[Rack::QUERY_STRING])
62
62
  return [nil, query_error] if query_error
63
63
 
64
+ cookies = parse_cookies(request.env[Rack::HTTP_COOKIE])
65
+ return [nil, cookies] if cookies.is_a?(Failure)
66
+
64
67
  body = @body_parsers&.call(request)
65
68
  return [nil, body] if body.is_a?(Failure)
66
69
 
@@ -68,7 +71,7 @@ module OpenapiFirst
68
71
  path: @path_parser&.unpack(route_params),
69
72
  query:,
70
73
  headers: @header_parser&.unpack(RequestHeaders.new(request.env)),
71
- cookies: @cookie_parser&.unpack(Rack::Utils.parse_cookies_header(request.env[Rack::HTTP_COOKIE])),
74
+ cookies:,
72
75
  body:
73
76
  ), nil]
74
77
  end
@@ -79,6 +82,12 @@ module OpenapiFirst
79
82
  [nil, Failure.new(:invalid_query, message: 'Invalid query parameter.')]
80
83
  end
81
84
 
85
+ def parse_cookies(cookie_header)
86
+ @cookie_parser&.unpack(Rack::Utils.parse_cookies_header(cookie_header))
87
+ rescue Rack::Utils::InvalidParameterError
88
+ Failure.new(:invalid_cookie, message: 'Invalid cookie value.')
89
+ end
90
+
82
91
  def build_body_parser(content_type, encoding)
83
92
  RequestBodyParsers[content_type, { encoding: encoding || {} }]
84
93
  end
@@ -40,7 +40,7 @@ module OpenapiFirst
40
40
  def build_headers_parser(headers)
41
41
  return unless headers&.any?
42
42
 
43
- ParametersParser.new(headers.map(&:parameter))
43
+ ParametersParser.new(headers.map(&:parameter), check_encoding: false)
44
44
  end
45
45
  end
46
46
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '4.0.1'
4
+ VERSION = '4.1.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller