contrast-agent 3.8.5 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_array/cs__assess_array.c +1 -1
  3. data/ext/cs__assess_module/cs__assess_module.c +0 -1
  4. data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
  5. data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
  6. data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
  7. data/ext/cs__common/cs__common.c +6 -6
  8. data/ext/cs__common/cs__common.h +3 -1
  9. data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
  10. data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
  11. data/funchook/autom4te.cache/requests +48 -48
  12. data/funchook/config.log +2 -2
  13. data/lib/contrast/agent.rb +15 -5
  14. data/lib/contrast/agent/assess.rb +0 -1
  15. data/lib/contrast/agent/assess/contrast_event.rb +9 -8
  16. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
  17. data/lib/contrast/agent/assess/policy/policy.rb +0 -14
  18. data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
  20. data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
  21. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  22. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
  23. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
  24. data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
  25. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
  26. data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
  27. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
  28. data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
  29. data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
  30. data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
  31. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
  32. data/lib/contrast/agent/assess/properties.rb +29 -0
  33. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
  34. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
  35. data/lib/contrast/agent/class_reopener.rb +98 -55
  36. data/lib/contrast/agent/feature_state.rb +1 -1
  37. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  38. data/lib/contrast/agent/logger_manager.rb +2 -2
  39. data/lib/contrast/agent/middleware.rb +1 -3
  40. data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
  41. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
  42. data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
  43. data/lib/contrast/agent/patching/policy/patch.rb +54 -23
  44. data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
  45. data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
  46. data/lib/contrast/agent/patching/policy/policy.rb +4 -0
  47. data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
  48. data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
  49. data/lib/contrast/agent/protect/policy/policy.rb +6 -6
  50. data/lib/contrast/agent/protect/rule/base.rb +1 -1
  51. data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
  52. data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
  53. data/lib/contrast/agent/railtie.rb +11 -5
  54. data/lib/contrast/agent/request.rb +1 -19
  55. data/lib/contrast/agent/request_context.rb +1 -1
  56. data/lib/contrast/agent/rewriter.rb +4 -3
  57. data/lib/contrast/agent/scope.rb +116 -19
  58. data/lib/contrast/agent/service_heartbeat.rb +5 -2
  59. data/lib/contrast/agent/settings_state.rb +12 -8
  60. data/lib/contrast/agent/version.rb +1 -1
  61. data/lib/contrast/api.rb +1 -0
  62. data/lib/contrast/api/speedracer.rb +2 -2
  63. data/lib/contrast/components/agent.rb +26 -7
  64. data/lib/contrast/components/app_context.rb +8 -45
  65. data/lib/contrast/components/contrast_service.rb +3 -4
  66. data/lib/contrast/components/interface.rb +1 -1
  67. data/lib/contrast/components/scope.rb +56 -26
  68. data/lib/contrast/config/ruby_configuration.rb +8 -3
  69. data/lib/contrast/delegators.rb +9 -0
  70. data/lib/contrast/delegators/application_update.rb +32 -0
  71. data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
  72. data/lib/contrast/extensions/framework/rack/request.rb +24 -0
  73. data/lib/contrast/extensions/framework/rack/response.rb +23 -0
  74. data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
  75. data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
  76. data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
  77. data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
  78. data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
  79. data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
  80. data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
  81. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
  82. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
  83. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
  84. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
  85. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
  86. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
  87. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
  88. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
  89. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
  90. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
  91. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
  92. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
  93. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
  94. data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
  95. data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
  96. data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
  97. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
  98. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
  99. data/lib/contrast/extensions/ruby_core/module.rb +17 -0
  100. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
  101. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
  102. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
  103. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
  104. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
  105. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
  106. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
  107. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
  108. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
  109. data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
  110. data/lib/contrast/framework/base_support.rb +63 -0
  111. data/lib/contrast/framework/manager.rb +109 -0
  112. data/lib/contrast/framework/platform_version.rb +21 -0
  113. data/lib/contrast/framework/rails_support.rb +88 -0
  114. data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
  115. data/lib/contrast/framework/sinatra_support.rb +94 -0
  116. data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
  117. data/lib/contrast/utils/assess/tracking_util.rb +2 -4
  118. data/lib/contrast/utils/class_util.rb +92 -37
  119. data/lib/contrast/utils/duck_utils.rb +59 -39
  120. data/lib/contrast/utils/environment_util.rb +5 -75
  121. data/lib/contrast/utils/freeze_util.rb +3 -7
  122. data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
  123. data/lib/contrast/utils/job_servers_running.rb +39 -0
  124. data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
  125. data/lib/contrast/utils/service_response_util.rb +0 -6
  126. data/lib/contrast/utils/sinatra_helper.rb +6 -0
  127. data/lib/contrast/utils/stack_trace_utils.rb +1 -1
  128. data/resources/assess/policy.json +74 -23
  129. data/resources/inventory/policy.json +1 -1
  130. data/resources/protect/policy.json +11 -9
  131. data/resources/rubocops/object/frozen_cop.rb +1 -1
  132. data/ruby-agent.gemspec +2 -0
  133. data/service_executables/VERSION +1 -1
  134. data/service_executables/linux/contrast-service +0 -0
  135. data/service_executables/mac/contrast-service +0 -0
  136. metadata +94 -57
  137. data/ext/cs__scope/cs__scope.c +0 -96
  138. data/ext/cs__scope/cs__scope.h +0 -33
  139. data/lib/contrast/agent/assess/class_reverter.rb +0 -82
  140. data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
  141. data/lib/contrast/core_extensions/module.rb +0 -42
  142. data/lib/contrast/core_extensions/object.rb +0 -27
  143. data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
  144. data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
  145. data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
  146. data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
  147. data/lib/contrast/rails_extensions/buffer.rb +0 -30
  148. data/lib/contrast/rails_extensions/rack.rb +0 -45
  149. data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
  150. data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
  151. data/lib/contrast/utils/operating_environment.rb +0 -38
  152. data/lib/contrast/utils/path_util.rb +0 -151
  153. data/lib/contrast/utils/scope_util.rb +0 -99
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/agent/assess/policy/source_method'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Assess
9
+ module Policy
10
+ module SourceValidation
11
+ # Validator used to assert a CROSS_SITE tag is actually applicable to
12
+ # the given method before applying the tag to its target
13
+ module CrossSiteValidator
14
+ # prevent the application of a tag if it is from a source known to
15
+ # not apply a tag in a provided context.
16
+ # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/reflected_xss.md
17
+ def self.valid? tag, source_type, source_name
18
+ return true unless tag == 'CROSS_SITE'
19
+ return false if source_type == Contrast::Agent::Assess::Policy::SourceMethod::HEADER_KEY_TYPE
20
+ return true unless source_type == Contrast::Agent::Assess::Policy::SourceMethod::HEADER_TYPE
21
+ return false unless source_name
22
+
23
+ source_name.casecmp?('referer')
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/agent/assess/policy/source_validation/cross_site_validator'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Assess
9
+ module Policy
10
+ # Some of our sources require validation to prevent them from applying
11
+ # certain tags in a given context. This provides a single place from
12
+ # which those validations can be called.
13
+ module SourceValidation
14
+ VALIDATORS = [
15
+ Contrast::Agent::Assess::Policy::SourceValidation::CrossSiteValidator
16
+ ].cs__freeze
17
+
18
+ # Determines if the conditions in which this source was called are
19
+ # valid and should result in the application of a tag
20
+ #
21
+ # @param tag [String] the tag which the Source is attempting to apply
22
+ # @param source_type [String] the type of the source
23
+ # @param source_name [String] the name of the source
24
+ # @return [Boolean] if the conditions are valid for the application
25
+ # of a tag
26
+ def self.valid? tag, source_type, source_name
27
+ VALIDATORS.each do |validator|
28
+ return false unless validator.valid?(tag, source_type, source_name)
29
+ end
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -23,184 +23,269 @@ module Contrast
23
23
  # The level of TeamServer compliance our traces meet
24
24
  CURRENT_FINDING_VERSION = 2
25
25
 
26
- def self.settings
27
- Contrast::Agent::FeatureState.instance
28
- end
26
+ class << self
27
+ # This is called from within our woven proc. It will be called as if it
28
+ # were inline in the Rack application.
29
+ #
30
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
31
+ # the node that applies to the method being called
32
+ # @param object [Object] the Object on which the method was invoked
33
+ # @param ret [Object] the Return of the invoked method
34
+ # @param args [Array<Object>] the Arguments with which the method
35
+ # was invoked
36
+ def apply_trigger_rule trigger_node, object, ret, args
37
+ return if trigger_node.nil?
38
+
39
+ current_context = Contrast::Agent::REQUEST_TRACKER.current
40
+ return unless current_context&.analyze_request? && ASSESS.enabled?
29
41
 
30
- def self.apply_trigger_rule trigger_node, object, ret, args
31
- return if trigger_node.nil?
32
-
33
- current_context = Contrast::Agent::REQUEST_TRACKER.current
34
- return unless current_context&.analyze_request? && ASSESS.enabled?
35
-
36
- if trigger_node.sources&.any?
37
- trigger_node.sources.each do |marker|
38
- source = determine_source(marker, object, ret, args)
39
- TriggerMethod.cs__apply_trigger(current_context,
40
- trigger_node,
41
- source,
42
- object,
43
- ret,
44
- 1,
45
- *args)
42
+ if trigger_node.sources&.any?
43
+ trigger_node.sources.each do |marker|
44
+ source = determine_source(marker, object, ret, args)
45
+ apply_trigger(current_context,
46
+ trigger_node,
47
+ source,
48
+ object,
49
+ ret,
50
+ 1,
51
+ *args)
52
+ end
53
+ else
54
+ apply_trigger(current_context,
55
+ trigger_node,
56
+ nil,
57
+ object,
58
+ ret,
59
+ 1,
60
+ *args)
46
61
  end
47
- else
48
- TriggerMethod.cs__apply_trigger(current_context,
49
- trigger_node,
50
- nil,
51
- object,
52
- ret,
53
- 1,
54
- *args)
55
62
  end
56
- end
57
63
 
58
- def self.cs__apply_trigger context, trigger_node, source, object, ret, invoked, *args
59
- return unless context && trigger_node
60
- return if trigger_node.rule_disabled?
61
- return if trigger_node.dataflow? && source.nil?
62
-
63
- invoked += 1
64
- if trigger_node.regexp_rule?
65
- apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
66
- elsif trigger_node.custom_trigger?
67
- trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
68
- elsif trigger_node.dataflow?
69
- apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
70
- else # trigger rule - just calling the method is dangerous
71
- build_finding(context, trigger_node, source, object, ret, invoked, *args)
64
+ def apply_eval_trigger context, trigger_node, source, object, ret, invoked, *args
65
+ apply_trigger(context, trigger_node, source, object, ret, invoked, *args)
72
66
  end
73
- rescue StandardError => e
74
- logger.warn(e, 'Unable to apply trigger.')
75
- end
76
67
 
77
- # Given the marker from the trigger_node (the pointer indicating the entity
78
- # from which the taint originated), return the entity on which this
79
- # trigger needs to operate.
80
- #
81
- # In an effort to speed up this lookup, we've changed the marker for
82
- # parameters to be implicit - if it is not a return or an object, it
83
- # must be a parameter, which we can reference either by index or by
84
- # name.
85
- def self.determine_source marker, object, ret, args
86
- case marker
87
- when Contrast::Utils::ObjectShare::RETURN_KEY
88
- ret
89
- when Contrast::Utils::ObjectShare::OBJECT_KEY
90
- object
91
- else # 'P'
92
- if marker.is_a?(Integer)
93
- args[marker]
94
- else
95
- arg = nil
96
- args.each do |search|
97
- next unless search.is_a?(Hash)
68
+ # This converts the source of the finding, and the events leading
69
+ # up to it into a Finding
70
+ #
71
+ # @param context [Contrast::Utils::ThreadTracker] the current request
72
+ # context
73
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
74
+ # the node to direct applying this trigger event
75
+ # @param source [Object] the source of the Trigger Event
76
+ # @param object [Object] the Object on which the method was invoked
77
+ # @param ret [Object] the Return of the invoked method
78
+ # @param invoked [Integer] the depth of this invocation from
79
+ # application code; often a lie.
80
+ # @param args [Array<Object>] the Arguments with which the method
81
+ # was invoked
82
+ # @return [Contrast::Api::Dtm::Finding, nil] the
83
+ # Contrast::Api::Dtm::Finding to send to TeamServer or nil if
84
+ # conditions were not met
85
+ def build_finding context, trigger_node, source, object, ret, invoked, *args
86
+ return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
87
+
88
+ request = context.request
89
+ env = request.env
90
+ return if defined?(ActionController::Live) &&
91
+ env &&
92
+ env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
93
+
94
+ finding = Contrast::Api::Dtm::Finding.new
95
+ finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
96
+ finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
97
+ finding.version = CURRENT_FINDING_VERSION
98
+
99
+ build_from_source(finding, source)
100
+ trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
101
+ finding.events << trigger_event
102
+ build_hash(finding, source)
103
+ build_tags(context)
104
+ finding.routes << context.route if context.route
105
+ context.activity.findings << finding
106
+ logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
107
+ rescue StandardError => e
108
+ logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
109
+ end
110
+
111
+ private
112
+
113
+ def settings
114
+ Contrast::Agent::FeatureState.instance
115
+ end
116
+
117
+ # This is our method that actually checks the taint on the object
118
+ # our trigger_node targets.
119
+ #
120
+ # @param context [Contrast::Utils::ThreadTracker] the current request
121
+ # context
122
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
123
+ # the node to direct applying this trigger event
124
+ # @param source [Object] the source of the Trigger Event
125
+ # @param object [Object] the Object on which the method was invoked
126
+ # @param ret [Object] the Return of the invoked method
127
+ # @param invoked [Integer] the depth of this invocation from
128
+ # application code; often a lie.
129
+ # @param args [Array<Object>] the Arguments with which the method
130
+ # was invoked
131
+ def apply_trigger context, trigger_node, source, object, ret, invoked, *args
132
+ return unless context && trigger_node
133
+ return if trigger_node.rule_disabled?
134
+ return if trigger_node.dataflow? && source.nil?
135
+
136
+ invoked += 1
137
+ if trigger_node.regexp_rule?
138
+ apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
139
+ elsif trigger_node.custom_trigger?
140
+ trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
141
+ elsif trigger_node.dataflow?
142
+ apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
143
+ else # trigger rule - just calling the method is dangerous
144
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
145
+ end
146
+ rescue StandardError => e
147
+ logger.warn(e, 'Unable to apply trigger.')
148
+ end
98
149
 
99
- arg = search[marker]
100
- break if arg
150
+ # Given the marker from the trigger_node (the pointer indicating
151
+ # the entity from which the taint originated), return the entity on
152
+ # which this trigger needs to operate.
153
+ #
154
+ # In an effort to speed up this lookup, we've changed the marker
155
+ # for parameters to be implicit - if it is not a return or an
156
+ # object, it must be a parameter, which we can reference either by
157
+ # index or by name.
158
+ #
159
+ # @param marker [String] the source marker that indicates which
160
+ # Object
161
+ # @param object [Object] the Object on which the method was invoked
162
+ # @param ret [Object] the Return of the invoked method
163
+ # @param args [Array<Object>] the Arguments with which the method
164
+ # was invoked
165
+ # @return [Object] the literal object that this Trigger Event
166
+ # verifies
167
+ def determine_source marker, object, ret, args
168
+ case marker
169
+ when Contrast::Utils::ObjectShare::RETURN_KEY
170
+ ret
171
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
172
+ object
173
+ else # 'P'
174
+ if marker.is_a?(Integer)
175
+ args[marker]
176
+ else
177
+ arg = nil
178
+ args.each do |search|
179
+ next unless search.is_a?(Hash)
180
+
181
+ arg = search[marker]
182
+ break if arg
183
+ end
184
+ arg
101
185
  end
102
- arg
103
186
  end
104
187
  end
105
- end
106
188
 
107
- def self.apply_regexp_rule context, trigger_node, source, object, ret, invoked, *args
108
- return unless source.is_a?(String)
109
- return if trigger_node.good_value && source.match?(trigger_node.good_value)
110
- return if trigger_node.bad_value && source !~ trigger_node.bad_value
189
+ # This is our method that actually checks the taint on the object
190
+ # our trigger_node targets for our Regexp based rules.
191
+ #
192
+ # @param context [Contrast::Utils::ThreadTracker] the current request
193
+ # context
194
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
195
+ # the node to direct applying this trigger event
196
+ # @param source [Object] the source of the Trigger Event
197
+ # @param object [Object] the Object on which the method was invoked
198
+ # @param ret [Object] the Return of the invoked method
199
+ # @param invoked [Integer] the depth of this invocation from
200
+ # application code; often a lie.
201
+ # @param args [Array<Object>] the Arguments with which the method
202
+ # was invoked
203
+ def apply_regexp_rule context, trigger_node, source, object, ret, invoked, *args
204
+ return unless source.is_a?(String)
205
+ return if trigger_node.good_value && source.match?(trigger_node.good_value)
206
+ return if trigger_node.bad_value && source !~ trigger_node.bad_value
111
207
 
112
- invoked += 1
113
- build_finding(context, trigger_node, source, object, ret, invoked, *args)
114
- end
208
+ invoked += 1
209
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
210
+ end
115
211
 
116
- def self.apply_dataflow_rule context, trigger_node, source, object, ret, invoked, *args
117
- return unless source
212
+ # This is our method that actually checks the taint on the object
213
+ # our trigger_node targets for our Dataflow based rules.
214
+ #
215
+ # @param context [Contrast::Utils::ThreadTracker] the current request
216
+ # context
217
+ # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
218
+ # the node to direct applying this trigger event
219
+ # @param source [Object] the source of the Trigger Event
220
+ # @param object [Object] the Object on which the method was invoked
221
+ # @param ret [Object] the Return of the invoked method
222
+ # @param invoked [Integer] the depth of this invocation from
223
+ # application code; often a lie.
224
+ # @param args [Array<Object>] the Arguments with which the method
225
+ # was invoked
226
+ def apply_dataflow_rule context, trigger_node, source, object, ret, invoked, *args
227
+ return unless source
118
228
 
119
- invoked += 1
120
- if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
121
- return unless source.cs__tracked?
122
- return unless trigger_node.violated?(source)
229
+ invoked += 1
230
+ if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
231
+ return unless source.cs__tracked?
232
+ return unless trigger_node.violated?(source)
123
233
 
124
- build_finding(context, trigger_node, source, object, ret, invoked, *args)
125
- elsif Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(source)
126
- invoked += 2 # the each & the block
127
- source.each_pair do |key, value|
128
- apply_dataflow_rule(context, trigger_node, key, object, ret, invoked, *args)
129
- apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
130
- end
131
- elsif Contrast::Utils::DuckUtils.quacks_like_tracked_enumerable?(source)
132
- invoked += 2 # the each & the block
133
- source.each do |value|
134
- apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
234
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
235
+ elsif Contrast::Utils::DuckUtils.iterable_hash?(source)
236
+ invoked += 2 # the each & the block
237
+ source.each_pair do |key, value|
238
+ apply_dataflow_rule(context, trigger_node, key, object, ret, invoked, *args)
239
+ apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
240
+ end
241
+ elsif Contrast::Utils::DuckUtils.iterable_enumerable?(source)
242
+ invoked += 2 # the each & the block
243
+ source.each do |value|
244
+ apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
245
+ end
246
+ else
247
+ logger.warn(
248
+ nil,
249
+ "Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
250
+ logger.debug(nil, source.to_s[0..99])
135
251
  end
136
- else
137
- logger.warn(
138
- nil,
139
- "Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
140
- logger.debug(nil, source.to_s[0..99])
141
252
  end
142
- end
143
253
 
144
- def self.build_finding context, trigger_node, source, object, ret, invoked, *args
145
- return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
146
-
147
- request = context.request
148
- env = request.env
149
- return if defined?(ActionController::Live) &&
150
- env &&
151
- env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
152
-
153
- finding = Contrast::Api::Dtm::Finding.new
154
- finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
155
- finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
156
- finding.version = CURRENT_FINDING_VERSION
157
-
158
- build_from_source(finding, source)
159
- trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
160
- finding.events << trigger_event
161
- build_hash(finding, source)
162
- build_tags(context)
163
- finding.routes << context.route if context.route
164
- context.activity.findings << finding
165
- logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
166
- rescue StandardError => e
167
- logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
168
- end
254
+ def build_from_source finding, source
255
+ return unless source
256
+ return unless Contrast::Utils::DuckUtils.quacks_to?(
257
+ source,
258
+ :cs__properties)
259
+ return unless source.cs__properties
169
260
 
170
- def self.build_from_source finding, source
171
- return unless source
172
- return unless Contrast::Utils::DuckUtils.quacks_to?(
173
- source,
174
- :cs__properties)
175
- return unless source.cs__properties
176
-
177
- # events could technically be nil, but we would have failed
178
- # the rule check before getting here. not worth the nil check
179
- source.cs__properties.events.each do |event|
180
- finding.events << event.to_dtm_event
181
- end
261
+ # events could technically be nil, but we would have failed
262
+ # the rule check before getting here. not worth the nil check
263
+ source.cs__properties.events.each do |event|
264
+ finding.events << event.to_dtm_event
265
+ end
182
266
 
183
- # Google::Protobuf::Map doesn't support merge!, so we have to do this
184
- # long form
185
- source_props = source.cs__properties.properties
186
- return unless source_props
267
+ # Google::Protobuf::Map doesn't support merge!, so we have to do this
268
+ # long form
269
+ source_props = source.cs__properties.properties
270
+ return unless source_props
187
271
 
188
- source_props.each_pair do |key, value|
189
- key = Contrast::Utils::StringUtils.force_utf8(key)
190
- finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
272
+ source_props.each_pair do |key, value|
273
+ key = Contrast::Utils::StringUtils.force_utf8(key)
274
+ finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
275
+ end
191
276
  end
192
- end
193
277
 
194
- def self.build_hash finding, source
195
- hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
196
- finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
197
- finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
198
- end
278
+ def build_hash finding, source
279
+ hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
280
+ finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
281
+ finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
282
+ end
199
283
 
200
- def self.build_tags context
201
- return unless ASSESS.tags
284
+ def build_tags context
285
+ return unless ASSESS.tags
202
286
 
203
- context.activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags)
287
+ context.activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags)
288
+ end
204
289
  end
205
290
  end
206
291
  end