contrast-agent 3.15.0 → 3.16.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/lib/contrast/agent.rb +2 -9
  3. data/lib/contrast/agent/assess/contrast_event.rb +142 -70
  4. data/lib/contrast/agent/assess/events/source_event.rb +1 -1
  5. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +10 -3
  6. data/lib/contrast/agent/assess/policy/policy_node.rb +15 -10
  7. data/lib/contrast/agent/assess/policy/policy_scanner.rb +7 -1
  8. data/lib/contrast/agent/assess/policy/propagator/insert.rb +1 -1
  9. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +0 -3
  10. data/lib/contrast/agent/assess/policy/propagator/select.rb +1 -3
  11. data/lib/contrast/agent/assess/policy/propagator/splat.rb +0 -5
  12. data/lib/contrast/agent/assess/policy/propagator/split.rb +12 -13
  13. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +21 -14
  14. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +4 -5
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +39 -14
  16. data/lib/contrast/agent/assess/policy/trigger_node.rb +31 -37
  17. data/lib/contrast/agent/assess/property/evented.rb +5 -18
  18. data/lib/contrast/agent/assess/property/tagged.rb +9 -3
  19. data/lib/contrast/agent/assess/property/updated.rb +0 -5
  20. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  21. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +23 -8
  22. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +82 -14
  23. data/lib/contrast/agent/assess/tag.rb +1 -1
  24. data/lib/contrast/agent/at_exit_hook.rb +5 -5
  25. data/lib/contrast/agent/patching/policy/after_load_patch.rb +5 -5
  26. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +20 -20
  27. data/lib/contrast/agent/patching/policy/module_policy.rb +10 -10
  28. data/lib/contrast/agent/patching/policy/policy.rb +16 -2
  29. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +3 -5
  30. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  31. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  32. data/lib/contrast/agent/request.rb +34 -34
  33. data/lib/contrast/agent/static_analysis.rb +6 -6
  34. data/lib/contrast/agent/version.rb +1 -1
  35. data/lib/contrast/api/communication/socket_client.rb +36 -1
  36. data/lib/contrast/api/decorators/address.rb +13 -13
  37. data/lib/contrast/api/decorators/message.rb +1 -0
  38. data/lib/contrast/api/decorators/trace_event.rb +20 -18
  39. data/lib/contrast/components/app_context.rb +39 -30
  40. data/lib/contrast/components/contrast_service.rb +9 -9
  41. data/lib/contrast/components/settings.rb +20 -23
  42. data/lib/contrast/config/service_configuration.rb +4 -2
  43. data/lib/contrast/configuration.rb +1 -1
  44. data/lib/contrast/extension/assess/array.rb +7 -3
  45. data/lib/contrast/extension/assess/erb.rb +5 -0
  46. data/lib/contrast/extension/assess/eval_trigger.rb +6 -6
  47. data/lib/contrast/extension/assess/exec_trigger.rb +1 -1
  48. data/lib/contrast/extension/assess/fiber.rb +3 -3
  49. data/lib/contrast/extension/assess/hash.rb +3 -3
  50. data/lib/contrast/extension/assess/kernel.rb +18 -20
  51. data/lib/contrast/extension/assess/marshal.rb +8 -4
  52. data/lib/contrast/extension/assess/regexp.rb +3 -3
  53. data/lib/contrast/extension/assess/string.rb +13 -11
  54. data/lib/contrast/extension/protect/kernel.rb +3 -3
  55. data/lib/contrast/framework/base_support.rb +1 -1
  56. data/lib/contrast/framework/manager.rb +3 -3
  57. data/lib/contrast/framework/rack/patch/session_cookie.rb +9 -9
  58. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +13 -13
  59. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +10 -10
  60. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  61. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +11 -11
  62. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +12 -12
  63. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -3
  64. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +12 -12
  65. data/lib/contrast/framework/sinatra/patch/base.rb +11 -11
  66. data/lib/contrast/framework/sinatra/support.rb +4 -4
  67. data/lib/contrast/logger/log.rb +7 -2
  68. data/lib/contrast/utils/invalid_configuration_util.rb +2 -5
  69. data/resources/assess/policy.json +31 -12
  70. data/ruby-agent.gemspec +4 -3
  71. data/service_executables/VERSION +1 -1
  72. data/service_executables/linux/contrast-service +0 -0
  73. data/service_executables/mac/contrast-service +0 -0
  74. metadata +31 -17
@@ -38,15 +38,18 @@ module Contrast
38
38
  NON_PASSWORD_PARTIAL_NAMES.none? { |name| constant_string.index(name) }
39
39
  end
40
40
 
41
- # If the value is a string, it passes for this rule
42
- def value_type_passes? value
43
- value.is_a?(String)
44
- end
41
+ # Determine if the given value node violates the hardcode key rule
42
+ # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to
43
+ # evaluate
44
+ # @return [Boolean]
45
+ def value_node_passes? value_node
46
+ # If it's a freeze call, then evaluate the entity being frozen
47
+ value_node = value_node.children[0] if freeze_call?(value_node)
48
+ return false unless value_node.type == :STR
45
49
 
46
- # If the value probably isn't a property name, it passes for this
47
- # rule
48
- def value_passes? value
49
- !probably_property_name?(value)
50
+ # https://www.rubydoc.info/gems/ruby-internal/Node/STR
51
+ string = value_node.children[0]
52
+ !probably_property_name?(string)
50
53
  end
51
54
 
52
55
  # If a field name matches an expected password field, we'll check it's
@@ -65,6 +68,18 @@ module Contrast
65
68
  def redacted_marker
66
69
  REDACTED_MARKER
67
70
  end
71
+
72
+ # TODO: RUBY-1014 remove `#value_type_passes?` and `#value_passes?`
73
+ # If the value is a string, it passes for this rule
74
+ def value_type_passes? value
75
+ value.is_a?(String)
76
+ end
77
+
78
+ # If the value probably isn't a property name, it passes for this
79
+ # rule
80
+ def value_passes? value
81
+ !probably_property_name?(value)
82
+ end
68
83
  end
69
84
  end
70
85
  end
@@ -1,6 +1,7 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'contrast/agent/assess/policy/trigger_method'
4
5
  require 'contrast/components/interface'
5
6
  require 'contrast/extension/module'
6
7
 
@@ -9,14 +10,13 @@ module Contrast
9
10
  module Assess
10
11
  module Rule
11
12
  module Provider
12
- # Hardcoded rules detect if any secret value has been written directly into the
13
- # sourcecode of the application. To use this base class, a provider must
14
- # implement four methods
13
+ # Hardcoded rules detect if any secret value has been written
14
+ # directly into the sourcecode of the application. To use this base
15
+ # class, a provider must implement three methods:
15
16
  # 1) name_passes? : does the constant name match a given value set
16
- # 2) value_type_passes? : does the type of the value of the constant match the
17
- # type given
18
- # 3) value_passes? : does the value of the constant match a given value set
19
- # 4) redacted_marker : the value to plug in for the obfuscated value
17
+ # 2) value_node_passes? : does the value of the constant match a
18
+ # given value set
19
+ # 3) redacted_marker : the value to plug in for the obfuscated value
20
20
  module HardcodedValueRule
21
21
  include Contrast::Components::Interface
22
22
  access_component :analysis, :app_context, :logging
@@ -25,6 +25,7 @@ module Contrast
25
25
  !ASSESS.enabled? || ASSESS.rule_disabled?(rule_id)
26
26
  end
27
27
 
28
+ # TODO: RUBY-1014 - remove `#analyze`
28
29
  COMMON_CONSTANTS = %i[
29
30
  CONTRAST_ASSESS_POLICY_STATUS
30
31
  VERSION
@@ -69,10 +70,30 @@ module Contrast
69
70
  # if it looks like a placeholder / pointer to a config, skip it
70
71
  next unless value_passes?(value)
71
72
 
72
- report_finding(clazz, constant_string)
73
+ build_finding(clazz, constant_string)
73
74
  end
74
75
  end
75
76
 
77
+ # Parse the file pertaining to the given TracePoint to walk its AST
78
+ # to determine if a Constant is hardcoded. For our purposes, this
79
+ # hard coding means directly set rather than as an interpolated
80
+ # String or through a method call.
81
+ #
82
+ # Note: This is a top layer check, we make no assertions about what
83
+ # the methods or interpolations do. Their presence, even if only
84
+ # calling a hardcoded thing, causes this check to not report.
85
+ #
86
+ # @param trace_point [TracePoint] the TracePoint event created on
87
+ # the :end of a Module being loaded
88
+ def parse trace_point
89
+ return if disabled?
90
+
91
+ ast = RubyVM::AbstractSyntaxTree.parse_file(trace_point.path)
92
+ parse_ast(trace_point.self, ast)
93
+ rescue StandardError => e
94
+ logger.error('Unable to parse AST for hardcoded keys', e, module: trace_point.self)
95
+ end
96
+
76
97
  # Constants can be variable or classes defined in the given
77
98
  # class. We ONLY want the variables, which should be defined in
78
99
  # the MACRO_CASE (upper case & underscore format)
@@ -90,7 +111,57 @@ module Contrast
90
111
 
91
112
  private
92
113
 
93
- def report_finding clazz, constant_string
114
+ # @param mod [Module] the module to which this AST pertains
115
+ # @param ast [RubyVM::AbstractSyntaxTree::Node, Object] a node
116
+ # within the AST, which may be a leaf, so any Object
117
+ def parse_ast mod, ast
118
+ return unless ast.cs__is_a?(RubyVM::AbstractSyntaxTree::Node)
119
+ return unless ast.cs__respond_to?(:children)
120
+
121
+ children = ast.children
122
+ return unless children.any?
123
+
124
+ ast.children.each do |child|
125
+ parse_ast(mod, child)
126
+ end
127
+
128
+ # https://www.rubydoc.info/gems/ruby-internal/Node/CDECL
129
+ return unless ast.type == :CDECL
130
+
131
+ # The CDECL Node has two children, the first being the Constant
132
+ # name as a symbol, the second as the value to assign to that
133
+ # constant
134
+ children = ast.children
135
+ name = children[0].to_s
136
+ # If that constant name doesn't pass our checks, move on.
137
+ return unless name_passes?(name)
138
+
139
+ value = children[1]
140
+ # The assignment node could be a direct value or a call of some
141
+ # sort. We leave it to each rule to properly handle these nodes.
142
+ return unless value_node_passes?(value)
143
+
144
+ build_finding(mod, name)
145
+ end
146
+
147
+ # Constants can be set as frozen directly. We need to account for
148
+ # this change as it means the Node given to the :CDECL call will be
149
+ # a :CALL, not a constant.
150
+ #
151
+ # @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to
152
+ # evaluate
153
+ # @return [Boolean] is this a freeze call or not
154
+ def freeze_call? value_node
155
+ return false unless value_node.type == :CALL
156
+
157
+ children = value_node.children
158
+ return false unless children
159
+ return false unless children.length >= 2
160
+
161
+ children[1] == :freeze
162
+ end
163
+
164
+ def build_finding clazz, constant_string
94
165
  class_name = clazz.cs__name
95
166
 
96
167
  finding = Contrast::Api::Dtm::Finding.new
@@ -104,13 +175,10 @@ module Contrast
104
175
  hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
105
176
  finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
106
177
  finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
107
-
108
- activity = Contrast::Api::Dtm::Activity.new
109
- activity.findings << finding
110
-
111
- Contrast::Agent.messaging_queue.send_event_eventually(activity)
178
+ Contrast::Agent::Assess::Policy::TriggerMethod.report_finding(finding)
112
179
  rescue StandardError => e
113
180
  logger.error('Unable to build a finding for Hardcoded Rule', e)
181
+ nil
114
182
  end
115
183
  end
116
184
  end
@@ -14,7 +14,7 @@ module Contrast
14
14
 
15
15
  # Initialize a new tag
16
16
  #
17
- # @param label [String] the lable of the tag
17
+ # @param label [String] the label of the tag
18
18
  # @param length [Integer] the length of the string described with this
19
19
  # tag
20
20
  # @param start_idx [Integer] (0) the starting position in the string for
@@ -11,11 +11,11 @@ module Contrast
11
11
  access_component :logging
12
12
  def self.exit_hook
13
13
  @_exit_hook ||= begin
14
- at_exit do
15
- on_exit
16
- end
17
- true
18
- end
14
+ at_exit do
15
+ on_exit
16
+ end
17
+ true
18
+ end
19
19
  end
20
20
 
21
21
  # Actions to take when a process exits. Typically called from our
@@ -15,7 +15,7 @@ module Contrast
15
15
  access_component :scope
16
16
  attr_reader :applied, :module_name, :instrumentation_file_path, :method_to_instrument, :instrumenting_module
17
17
 
18
- def initialize module_name, instrumentation_file_path, method_to_instrument: nil, instrumenting_module:
18
+ def initialize module_name, instrumentation_file_path, method_to_instrument: nil, instrumenting_module: nil
19
19
  @applied = false
20
20
  @module_name = module_name
21
21
  @method_to_instrument = method_to_instrument
@@ -71,10 +71,10 @@ module Contrast
71
71
 
72
72
  def module_lookup
73
73
  @_module_lookup ||= begin
74
- Module.cs__const_get module_name
75
- rescue StandardError => _e
76
- nil
77
- end
74
+ Module.cs__const_get module_name
75
+ rescue StandardError => _e
76
+ nil
77
+ end
78
78
  end
79
79
  end
80
80
  end
@@ -30,20 +30,20 @@ module Contrast
30
30
  # extensions.
31
31
  def apply_direct_patches!
32
32
  @_apply_direct_patches ||= begin
33
- Contrast::Extension::Assess::ArrayPropagator.instrument_array_track
34
- Contrast::Extension::Assess::EvalTrigger.instrument_basic_object_track
35
- Contrast::Extension::Assess::EvalTrigger.instrument_module_track
36
- Contrast::Extension::Assess::FiberPropagator.instrument_fiber_track
37
- Contrast::Extension::Assess::HashPropagator.instrument_hash_track
38
- Contrast::Extension::Assess::KernelPropagator.instrument_kernel_track
39
- Contrast::Extension::Assess::MarshalPropagator.instrument_marshal_load
40
- Contrast::Extension::Assess::RegexpPropagator.instrument_regexp_track
41
- Contrast::Extension::Assess::StringPropagator.instrument_string
42
- Contrast::Extension::Assess::StringPropagator.instrument_string_interpolation
33
+ Contrast::Extension::Assess::ArrayPropagator.instrument_array_track
34
+ Contrast::Extension::Assess::EvalTrigger.instrument_basic_object_track
35
+ Contrast::Extension::Assess::EvalTrigger.instrument_module_track
36
+ Contrast::Extension::Assess::FiberPropagator.instrument_fiber_track
37
+ Contrast::Extension::Assess::HashPropagator.instrument_hash_track
38
+ Contrast::Extension::Assess::KernelPropagator.instrument_kernel_track
39
+ Contrast::Extension::Assess::MarshalPropagator.instrument_marshal_load
40
+ Contrast::Extension::Assess::RegexpPropagator.instrument_regexp_track
41
+ Contrast::Extension::Assess::StringPropagator.instrument_string
42
+ Contrast::Extension::Assess::StringPropagator.instrument_string_interpolation
43
43
 
44
- Contrast::Extension::Protect::Kernel.instrument
45
- true
46
- end
44
+ Contrast::Extension::Protect::Kernel.instrument
45
+ true
46
+ end
47
47
  end
48
48
 
49
49
  def apply_load_patches!
@@ -65,13 +65,13 @@ module Contrast
65
65
  # handling
66
66
  def apply_require_patches!
67
67
  @_apply_require_patches ||= begin
68
- require 'contrast/extension/thread'
69
- require 'contrast/extension/kernel'
70
- true
71
- rescue LoadError, StandardError => e
72
- logger.error('failed instrumenting apply_require_patches!', e)
73
- false
74
- end
68
+ require 'contrast/extension/thread'
69
+ require 'contrast/extension/kernel'
70
+ true
71
+ rescue LoadError, StandardError => e
72
+ logger.error('failed instrumenting apply_require_patches!', e)
73
+ false
74
+ end
75
75
  end
76
76
 
77
77
  def after_load_patches
@@ -61,16 +61,16 @@ module Contrast
61
61
  # @return [Integer] count of methods to be patched
62
62
  def num_expected_patches
63
63
  @_num_expected_patches ||= begin
64
- instance_methods = Set.new
65
- singleton_methods = Set.new
66
- sort_method_names(source_nodes, instance_methods, singleton_methods)
67
- sort_method_names(propagator_nodes, instance_methods, singleton_methods)
68
- sort_method_names(trigger_nodes, instance_methods, singleton_methods)
69
- sort_method_names(inventory_nodes, instance_methods, singleton_methods)
70
- sort_method_names(protect_nodes, instance_methods, singleton_methods)
71
- sort_method_names(deadzone_nodes, instance_methods, singleton_methods)
72
- instance_methods.length + singleton_methods.length
73
- end
64
+ instance_methods = Set.new
65
+ singleton_methods = Set.new
66
+ sort_method_names(source_nodes, instance_methods, singleton_methods)
67
+ sort_method_names(propagator_nodes, instance_methods, singleton_methods)
68
+ sort_method_names(trigger_nodes, instance_methods, singleton_methods)
69
+ sort_method_names(inventory_nodes, instance_methods, singleton_methods)
70
+ sort_method_names(protect_nodes, instance_methods, singleton_methods)
71
+ sort_method_names(deadzone_nodes, instance_methods, singleton_methods)
72
+ instance_methods.length + singleton_methods.length
73
+ end
74
74
  end
75
75
 
76
76
  private
@@ -124,12 +124,26 @@ module Contrast
124
124
  end
125
125
 
126
126
  def find_source_node class_name, method_name, instance_method
127
- sources.find { |source| source.class_name == class_name && source.method_name == method_name && source.instance_method == instance_method }
127
+ sources.find do |source|
128
+ source.class_name == class_name &&
129
+ source.method_name == method_name &&
130
+ source.instance_method == instance_method
131
+ end
132
+ end
133
+
134
+ def find_propagator_node class_name, method_name, instance_method
135
+ propagators.find do |propagator|
136
+ propagator.class_name == class_name &&
137
+ propagator.method_name == method_name &&
138
+ propagator.instance_method == instance_method
139
+ end
128
140
  end
129
141
 
130
142
  def find_node rule_id, class_name, method_name, instance_method
131
143
  find_triggers_by_rule(rule_id).find do |node|
132
- node.class_name == class_name && node.method_name == method_name && node.instance_method == instance_method
144
+ node.class_name == class_name &&
145
+ node.method_name == method_name &&
146
+ node.instance_method == instance_method
133
147
  end
134
148
  end
135
149
  end
@@ -30,11 +30,9 @@ module Contrast
30
30
  Contrast::Agent::Protect::Policy::AppliesDeserializationRule.apply_deserialization_command_check(command)
31
31
  return if skip_analysis?
32
32
 
33
- begin
34
- clazz = object.is_a?(Module) ? object : object.cs__class
35
- class_name = clazz.cs__name
36
- rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, class_name, method, command)
37
- end
33
+ clazz = object.is_a?(Module) ? object : object.cs__class
34
+ class_name = clazz.cs__name
35
+ rule.infilter(Contrast::Agent::REQUEST_TRACKER.current, class_name, method, command)
38
36
  end
39
37
 
40
38
  protected
@@ -27,7 +27,7 @@ module Contrast
27
27
  def apply_rule__io method, _exception, _properties, object, args
28
28
  need_rewind = false
29
29
  potential_xml = args[0]
30
- return unless potential_xml&.respond_to?(:rewind)
30
+ return unless potential_xml.cs__respond_to?(:rewind)
31
31
 
32
32
  xml = potential_xml.read
33
33
  need_rewind = true
@@ -16,6 +16,7 @@ module Contrast
16
16
  def start_line_comment? char, index, query
17
17
  if char == Contrast::Utils::ObjectShare::SLASH &&
18
18
  query[index + 1] == Contrast::Utils::ObjectShare::SLASH
19
+
19
20
  return true
20
21
  end
21
22
 
@@ -46,42 +46,42 @@ module Contrast
46
46
  # Should also handle the ;jsessionid.
47
47
  def normalized_uri
48
48
  @_normalized_uri ||= begin
49
- path = rack_request.path
50
- uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0] # remove ;jsessionid
51
- uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0] # remove ?query_string=
52
- uri.gsub(INNER_REST_TOKEN, INNER_NUMBER_MARKER) # replace interior tokens
53
- uri.gsub(LAST_REST_TOKEN, LAST_NUMBER_MARKER) # replace last token
54
- end
49
+ path = rack_request.path
50
+ uri = path.split(Contrast::Utils::ObjectShare::SEMICOLON)[0] # remove ;jsessionid
51
+ uri = uri.split(Contrast::Utils::ObjectShare::QUESTION_MARK)[0] # remove ?query_string=
52
+ uri.gsub(INNER_REST_TOKEN, INNER_NUMBER_MARKER) # replace interior tokens
53
+ uri.gsub(LAST_REST_TOKEN, LAST_NUMBER_MARKER) # replace last token
54
+ end
55
55
  end
56
56
 
57
57
  def document_type
58
58
  @_document_type ||= begin
59
- if /xml/i.match?(content_type) || body&.start_with?('<?xml')
60
- :XML
61
- elsif /json/i.match?(content_type) || body&.match?(/\s*[{\[]/)
62
- :JSON
63
- else
64
- :NORMAL
65
- end
66
- end
59
+ if /xml/i.match?(content_type) || body&.start_with?('<?xml')
60
+ :XML
61
+ elsif /json/i.match?(content_type) || body&.match?(/\s*[{\[]/)
62
+ :JSON
63
+ else
64
+ :NORMAL
65
+ end
66
+ end
67
67
  end
68
68
 
69
69
  # Header keys upcased and any underscores replaced with dashes
70
70
  def headers
71
71
  @_headers ||= begin
72
- with_contrast_scope do
73
- hash = {}
74
- env.each do |key, value|
75
- next unless key
76
-
77
- name = key.to_s
78
- next unless name.start_with?(Contrast::Utils::ObjectShare::HTTP_SCORE)
79
-
80
- hash[Contrast::Utils::StringUtils.normalized_key(name)] = value
81
- end
82
- hash
83
- end
84
- end
72
+ with_contrast_scope do
73
+ hash = {}
74
+ env.each do |key, value|
75
+ next unless key
76
+
77
+ name = key.to_s
78
+ next unless name.start_with?(Contrast::Utils::ObjectShare::HTTP_SCORE)
79
+
80
+ hash[Contrast::Utils::StringUtils.normalized_key(name)] = value
81
+ end
82
+ hash
83
+ end
84
+ end
85
85
  end
86
86
 
87
87
  def body
@@ -121,13 +121,13 @@ module Contrast
121
121
 
122
122
  def file_names
123
123
  @_file_names ||= begin
124
- names = {}
125
- parsed_data = Rack::Multipart.parse_multipart(rack_request.env)
126
- traverse_parsed_multipart(parsed_data, names)
127
- rescue StandardError => _e
128
- logger.warn('Unable to parse multipart request!')
129
- {}
130
- end
124
+ names = {}
125
+ parsed_data = Rack::Multipart.parse_multipart(rack_request.env)
126
+ traverse_parsed_multipart(parsed_data, names)
127
+ rescue StandardError => _e
128
+ logger.warn('Unable to parse multipart request!')
129
+ {}
130
+ end
131
131
  end
132
132
 
133
133
  def hash_id