contrast-agent 4.6.0 → 4.7.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 +4 -4
- data/.gitmodules +1 -1
- data/Rakefile +1 -2
- data/ext/build_funchook.rb +3 -3
- data/ext/extconf_common.rb +1 -5
- data/lib/contrast/agent/assess.rb +1 -1
- data/lib/contrast/agent/assess/contrast_object.rb +2 -2
- data/lib/contrast/agent/assess/events/event_factory.rb +2 -1
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +6 -3
- data/lib/contrast/agent/assess/policy/patcher.rb +10 -17
- data/lib/contrast/agent/assess/policy/policy_node.rb +25 -33
- data/lib/contrast/agent/assess/policy/preshift.rb +3 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +6 -15
- data/lib/contrast/agent/assess/policy/propagation_node.rb +19 -8
- data/lib/contrast/agent/assess/policy/propagator/center.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +3 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/select.rb +2 -12
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -7
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -7
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +3 -15
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -3
- data/lib/contrast/agent/assess/policy/source_method.rb +6 -6
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +1 -3
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +5 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +6 -15
- data/lib/contrast/agent/assess/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +4 -3
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +1 -2
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -8
- data/lib/contrast/agent/assess/property/evented.rb +8 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +11 -5
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +4 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -5
- data/lib/contrast/agent/class_reopener.rb +3 -1
- data/lib/contrast/agent/disable_reaction.rb +1 -3
- data/lib/contrast/agent/exclusion_matcher.rb +5 -11
- data/lib/contrast/agent/inventory/dependencies.rb +2 -0
- data/lib/contrast/agent/middleware.rb +3 -5
- data/lib/contrast/agent/module_data.rb +3 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +6 -5
- data/lib/contrast/agent/patching/policy/method_policy.rb +6 -2
- data/lib/contrast/agent/patching/policy/module_policy.rb +14 -7
- data/lib/contrast/agent/patching/policy/patch.rb +11 -16
- data/lib/contrast/agent/patching/policy/patch_status.rb +6 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +15 -12
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -4
- data/lib/contrast/agent/patching/policy/trigger_node.rb +21 -8
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +2 -3
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +5 -9
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +5 -5
- data/lib/contrast/agent/protect/rule/base.rb +13 -16
- data/lib/contrast/agent/protect/rule/base_service.rb +9 -5
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +14 -18
- data/lib/contrast/agent/protect/rule/deserialization.rb +6 -13
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +3 -14
- data/lib/contrast/agent/protect/rule/no_sqli.rb +6 -2
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -3
- data/lib/contrast/agent/protect/rule/path_traversal.rb +5 -5
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +1 -1
- data/lib/contrast/agent/protect/rule/xss.rb +1 -1
- data/lib/contrast/agent/protect/rule/xxe.rb +5 -12
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +1 -2
- data/lib/contrast/agent/reaction_processor.rb +11 -10
- data/lib/contrast/agent/request.rb +23 -23
- data/lib/contrast/agent/request_context.rb +9 -14
- data/lib/contrast/agent/rewriter.rb +5 -3
- data/lib/contrast/agent/service_heartbeat.rb +2 -3
- data/lib/contrast/agent/tracepoint_hook.rb +1 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/response_processor.rb +2 -4
- data/lib/contrast/api/communication/service_lifecycle.rb +4 -2
- data/lib/contrast/api/communication/socket_client.rb +8 -21
- data/lib/contrast/api/communication/speedracer.rb +3 -7
- data/lib/contrast/api/decorators/application_startup.rb +6 -2
- data/lib/contrast/api/decorators/library.rb +8 -6
- data/lib/contrast/api/decorators/message.rb +3 -3
- data/lib/contrast/api/decorators/trace_event.rb +3 -1
- data/lib/contrast/api/decorators/trace_event_object.rb +1 -3
- data/lib/contrast/api/decorators/trace_taint_range_tags.rb +1 -6
- data/lib/contrast/components/agent.rb +9 -4
- data/lib/contrast/components/app_context.rb +6 -6
- data/lib/contrast/components/config.rb +2 -1
- data/lib/contrast/components/contrast_service.rb +7 -8
- data/lib/contrast/components/interface.rb +3 -2
- data/lib/contrast/components/sampling.rb +8 -2
- data/lib/contrast/components/settings.rb +4 -2
- data/lib/contrast/config/assess_rules_configuration.rb +1 -3
- data/lib/contrast/config/base_configuration.rb +4 -5
- data/lib/contrast/config/exception_configuration.rb +1 -5
- data/lib/contrast/config/heap_dump_configuration.rb +12 -6
- data/lib/contrast/config/logger_configuration.rb +1 -5
- data/lib/contrast/configuration.rb +3 -14
- data/lib/contrast/extension/assess/array.rb +1 -6
- data/lib/contrast/extension/assess/erb.rb +1 -7
- data/lib/contrast/extension/assess/eval_trigger.rb +2 -6
- data/lib/contrast/extension/assess/exec_trigger.rb +3 -9
- data/lib/contrast/extension/assess/fiber.rb +2 -12
- data/lib/contrast/extension/assess/kernel.rb +2 -9
- data/lib/contrast/extension/assess/marshal.rb +2 -6
- data/lib/contrast/extension/assess/regexp.rb +1 -6
- data/lib/contrast/extension/assess/string.rb +3 -1
- data/lib/contrast/extension/kernel.rb +4 -2
- data/lib/contrast/framework/manager.rb +1 -2
- data/lib/contrast/framework/rack/patch/session_cookie.rb +5 -18
- data/lib/contrast/framework/rack/patch/support.rb +6 -4
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +7 -2
- data/lib/contrast/framework/rails/patch/support.rb +4 -2
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +4 -1
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +2 -0
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +2 -0
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +2 -0
- data/lib/contrast/framework/rails/support.rb +2 -2
- data/lib/contrast/framework/sinatra/support.rb +3 -1
- data/lib/contrast/funchook/funchook.rb +1 -5
- data/lib/contrast/logger/application.rb +12 -9
- data/lib/contrast/logger/format.rb +2 -5
- data/lib/contrast/logger/log.rb +4 -3
- data/lib/contrast/logger/request.rb +1 -2
- data/lib/contrast/security_exception.rb +1 -1
- data/lib/contrast/tasks/service.rb +5 -1
- data/lib/contrast/utils/assess/tracking_util.rb +1 -2
- data/lib/contrast/utils/class_util.rb +0 -8
- data/lib/contrast/utils/hash_digest.rb +2 -5
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/job_servers_running.rb +9 -4
- data/lib/contrast/utils/os.rb +2 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -1
- data/ruby-agent.gemspec +13 -14
- data/sonar-project.properties +9 -0
- metadata +37 -36
|
@@ -6,11 +6,11 @@ module Contrast
|
|
|
6
6
|
# A simple wrapper around a Module and a call to its name, used to avoid
|
|
7
7
|
# calling the Module#name method and generating extra Strings
|
|
8
8
|
class ModuleData
|
|
9
|
-
attr_reader :mod, :
|
|
9
|
+
attr_reader :mod, :mod_name
|
|
10
10
|
|
|
11
|
-
def initialize mod,
|
|
11
|
+
def initialize mod, mod_name = nil
|
|
12
12
|
@mod = mod
|
|
13
|
-
@
|
|
13
|
+
@mod_name = mod_name || mod.cs__name
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -51,14 +51,15 @@ module Contrast
|
|
|
51
51
|
next unless after_load_patch.target_defined?
|
|
52
52
|
next if AGENT.skip_instrumentation?(after_load_patch.module_name)
|
|
53
53
|
|
|
54
|
-
logger.trace(
|
|
55
|
-
|
|
56
|
-
module: after_load_patch.module_name)
|
|
54
|
+
logger.trace('Catching up on already loaded afterload patch - applying instrumentation',
|
|
55
|
+
module: after_load_patch.module_name)
|
|
57
56
|
after_load_patch.instrument!
|
|
58
57
|
rescue NameError => e
|
|
59
|
-
logger.error('Method undefined in afterload patch', e, module: after_load_patch.module_name,
|
|
58
|
+
logger.error('Method undefined in afterload patch', e, module: after_load_patch.module_name,
|
|
59
|
+
method: after_load_patch.method_to_instrument)
|
|
60
60
|
rescue StandardError => e
|
|
61
|
-
logger.error('Afterload patch failed to apply', e, module: after_load_patch.module_name,
|
|
61
|
+
logger.error('Afterload patch failed to apply', e, module: after_load_patch.module_name,
|
|
62
|
+
method: after_load_patch.method_to_instrument)
|
|
62
63
|
end
|
|
63
64
|
after_load_patches.delete_if(&:applied?)
|
|
64
65
|
end
|
|
@@ -49,7 +49,10 @@ module Contrast
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def nodes
|
|
52
|
-
@_nodes ||= [
|
|
52
|
+
@_nodes ||= [
|
|
53
|
+
source_node, propagation_node, trigger_node, inventory_node, protect_node,
|
|
54
|
+
deadzone_node
|
|
55
|
+
].compact
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
def method_scopes
|
|
@@ -76,7 +79,8 @@ module Contrast
|
|
|
76
79
|
protect_node = find_method_node(module_policy.protect_nodes, method_name, instance_method)
|
|
77
80
|
inventory_node = find_method_node(module_policy.inventory_nodes, method_name, instance_method)
|
|
78
81
|
deadzone_node = find_method_node(module_policy.deadzone_nodes, method_name, instance_method)
|
|
79
|
-
method_visibility = find_visibility(source_node, propagation_node, trigger_node, protect_node,
|
|
82
|
+
method_visibility = find_visibility(source_node, propagation_node, trigger_node, protect_node,
|
|
83
|
+
inventory_node, deadzone_node)
|
|
80
84
|
MethodPolicy.new(method_name: method_name,
|
|
81
85
|
method_visibility: method_visibility,
|
|
82
86
|
instance_method: instance_method,
|
|
@@ -20,12 +20,18 @@ module Contrast
|
|
|
20
20
|
# @return [Contrast::Agent::Patching::Policy::ModulePolicy]
|
|
21
21
|
def create_module_policy module_name
|
|
22
22
|
module_policy = Contrast::Agent::Patching::Policy::ModulePolicy.new
|
|
23
|
-
module_policy.source_nodes = nodes_for_module(
|
|
24
|
-
|
|
25
|
-
module_policy.
|
|
26
|
-
|
|
27
|
-
module_policy.
|
|
28
|
-
|
|
23
|
+
module_policy.source_nodes = nodes_for_module(
|
|
24
|
+
Contrast::Agent::Assess::Policy::Policy.instance.sources, module_name)
|
|
25
|
+
module_policy.propagator_nodes = nodes_for_module(
|
|
26
|
+
Contrast::Agent::Assess::Policy::Policy.instance.propagators, module_name)
|
|
27
|
+
module_policy.trigger_nodes = nodes_for_module(
|
|
28
|
+
Contrast::Agent::Assess::Policy::Policy.instance.triggers, module_name)
|
|
29
|
+
module_policy.protect_nodes = nodes_for_module(
|
|
30
|
+
Contrast::Agent::Protect::Policy::Policy.instance.triggers, module_name)
|
|
31
|
+
module_policy.inventory_nodes = nodes_for_module(
|
|
32
|
+
Contrast::Agent::Inventory::Policy::Policy.instance.triggers, module_name)
|
|
33
|
+
module_policy.deadzone_nodes = nodes_for_module(
|
|
34
|
+
Contrast::Agent::Deadzone::Policy::Policy.instance.deadzones, module_name)
|
|
29
35
|
module_policy
|
|
30
36
|
end
|
|
31
37
|
|
|
@@ -42,7 +48,8 @@ module Contrast
|
|
|
42
48
|
end
|
|
43
49
|
end
|
|
44
50
|
|
|
45
|
-
attr_accessor :source_nodes, :propagator_nodes, :trigger_nodes, :inventory_nodes, :protect_nodes,
|
|
51
|
+
attr_accessor :source_nodes, :propagator_nodes, :trigger_nodes, :inventory_nodes, :protect_nodes,
|
|
52
|
+
:deadzone_nodes
|
|
46
53
|
|
|
47
54
|
def empty?
|
|
48
55
|
return false if source_nodes.any?
|
|
@@ -130,11 +130,7 @@ module Contrast
|
|
|
130
130
|
return unless AGENT.enabled?
|
|
131
131
|
return unless PROTECT.enabled?
|
|
132
132
|
|
|
133
|
-
apply_trigger_only(method_policy&.protect_node,
|
|
134
|
-
method,
|
|
135
|
-
exception,
|
|
136
|
-
object,
|
|
137
|
-
args)
|
|
133
|
+
apply_trigger_only(method_policy&.protect_node, method, exception, object, args)
|
|
138
134
|
end
|
|
139
135
|
|
|
140
136
|
# Apply the Inventory patch which applies to the given method.
|
|
@@ -151,11 +147,7 @@ module Contrast
|
|
|
151
147
|
def apply_inventory method_policy, method, exception, object, args
|
|
152
148
|
return unless INVENTORY.enabled?
|
|
153
149
|
|
|
154
|
-
apply_trigger_only(method_policy&.inventory_node,
|
|
155
|
-
method,
|
|
156
|
-
exception,
|
|
157
|
-
object,
|
|
158
|
-
args)
|
|
150
|
+
apply_trigger_only(method_policy&.inventory_node, method, exception, object, args)
|
|
159
151
|
end
|
|
160
152
|
|
|
161
153
|
# Apply the Assess patches which apply to the given method.
|
|
@@ -181,12 +173,15 @@ module Contrast
|
|
|
181
173
|
return ret if current_context && !current_context.analyze_request?
|
|
182
174
|
|
|
183
175
|
trigger_node = method_policy.trigger_node
|
|
184
|
-
|
|
176
|
+
if trigger_node
|
|
177
|
+
Contrast::Agent::Assess::Policy::TriggerMethod.apply_trigger_rule(trigger_node, object, ret, args)
|
|
178
|
+
end
|
|
185
179
|
if method_policy.source_node
|
|
186
180
|
# If we were given a frozen return, and it was the target of a
|
|
187
181
|
# source, and we have frozen sources enabled, we'll need to
|
|
188
182
|
# replace the return. Note, this is not the default case.
|
|
189
|
-
source_ret = Contrast::Agent::Assess::Policy::SourceMethod.source_patchers(method_policy, object, ret,
|
|
183
|
+
source_ret = Contrast::Agent::Assess::Policy::SourceMethod.source_patchers(method_policy, object, ret,
|
|
184
|
+
args)
|
|
190
185
|
end
|
|
191
186
|
if method_policy.propagation_node
|
|
192
187
|
propagated_ret = Contrast::Agent::Assess::Policy::PropagationMethod.apply_propagation(
|
|
@@ -277,9 +272,9 @@ module Contrast
|
|
|
277
272
|
# <method_start>_unbound_<method_name>
|
|
278
273
|
def build_unbound_method_name patcher_method
|
|
279
274
|
(Contrast::Utils::ObjectShare::CONTRAST_PATCHED_METHOD_START +
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
275
|
+
'unbound' +
|
|
276
|
+
Contrast::Utils::ObjectShare::UNDERSCORE +
|
|
277
|
+
patcher_method.to_s).to_sym
|
|
283
278
|
end
|
|
284
279
|
|
|
285
280
|
# @param mod [Module] the module in which the patch should be
|
|
@@ -339,7 +334,7 @@ module Contrast
|
|
|
339
334
|
# @return [Symbol] new alias for the underlying method (presumably, so the patched method can call it)
|
|
340
335
|
def register_c_patch target_module_name, unbound_method, impl = :alias_instance
|
|
341
336
|
# These could be set as AfterLoadPatches.
|
|
342
|
-
method_name = unbound_method.name.to_sym
|
|
337
|
+
method_name = unbound_method.name.to_sym # rubocop:disable Security/Module/Name -- ruby built in attribute.
|
|
343
338
|
underlying_method_name = build_unbound_method_name(method_name).to_sym
|
|
344
339
|
|
|
345
340
|
target_module = Module.cs__const_get(target_module_name)
|
|
@@ -68,7 +68,10 @@ module Contrast
|
|
|
68
68
|
def set_info_for mod, method, method_policy, is_instance_method, cs_method
|
|
69
69
|
mod.instance_variable_set(method_info_key, {}) unless mod.instance_variable_defined?(method_info_key)
|
|
70
70
|
holder = mod.instance_variable_get(method_info_key)
|
|
71
|
-
|
|
71
|
+
# if we already have this information, then we don't need to set it as we'll update the info on access
|
|
72
|
+
return if holder.key?(method)
|
|
73
|
+
|
|
74
|
+
holder[method_name_key(method, is_instance_method)] = [method_policy, cs_method]
|
|
72
75
|
end
|
|
73
76
|
|
|
74
77
|
private
|
|
@@ -153,9 +156,7 @@ module Contrast
|
|
|
153
156
|
end
|
|
154
157
|
|
|
155
158
|
def patched?
|
|
156
|
-
@patch_status == :PATCHED ||
|
|
157
|
-
@patch_status == :NONE ||
|
|
158
|
-
@patch_status == :FAILED
|
|
159
|
+
@patch_status == :PATCHED || @patch_status == :NONE || @patch_status == :FAILED
|
|
159
160
|
end
|
|
160
161
|
|
|
161
162
|
def rewriting!
|
|
@@ -179,9 +180,7 @@ module Contrast
|
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
def rewritten?
|
|
182
|
-
@rewrite_status == :REWRITTEN ||
|
|
183
|
-
@rewrite_status == :NO_REWRITE ||
|
|
184
|
-
@rewrite_status == :FAILED_REWRITE
|
|
183
|
+
@rewrite_status == :REWRITTEN || @rewrite_status == :NO_REWRITE || @rewrite_status == :FAILED_REWRITE
|
|
185
184
|
end
|
|
186
185
|
end
|
|
187
186
|
end
|
|
@@ -13,7 +13,8 @@ require 'contrast/utils/class_util'
|
|
|
13
13
|
# assess
|
|
14
14
|
require 'contrast/agent/assess/policy/policy'
|
|
15
15
|
require 'contrast/agent/assess/policy/policy_scanner'
|
|
16
|
-
|
|
16
|
+
# TODO: RUBY-714 remove guard w/ EOL of 2.5
|
|
17
|
+
require 'contrast/agent/assess/policy/rewriter_patch' if RUBY_VERSION < '2.6.0'
|
|
17
18
|
require 'contrast/agent/assess/policy/source_method'
|
|
18
19
|
require 'contrast/agent/assess/policy/trigger_method'
|
|
19
20
|
|
|
@@ -53,7 +54,7 @@ module Contrast
|
|
|
53
54
|
def patch
|
|
54
55
|
catchup_after_load_patches
|
|
55
56
|
catchup_loaded_methods
|
|
56
|
-
Contrast::Agent::Assess::Policy::RewriterPatch.rewrite_interpolations
|
|
57
|
+
Contrast::Agent::Assess::Policy::RewriterPatch.rewrite_interpolations if RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
# Hook to only monkeypatch Contrast. This will not trigger any
|
|
@@ -85,7 +86,7 @@ module Contrast
|
|
|
85
86
|
|
|
86
87
|
load_patches_for_module(mod_name)
|
|
87
88
|
|
|
88
|
-
return unless all_module_names.any?
|
|
89
|
+
return unless all_module_names.any?(mod_name)
|
|
89
90
|
|
|
90
91
|
module_data = Contrast::Agent::ModuleData.new(mod, mod_name)
|
|
91
92
|
patch_into_module(module_data)
|
|
@@ -143,7 +144,8 @@ module Contrast
|
|
|
143
144
|
# @return [Array<String>] the names of all the Modules for which
|
|
144
145
|
# there patches in our policies
|
|
145
146
|
def all_module_names
|
|
146
|
-
@_all_module_names ||=
|
|
147
|
+
@_all_module_names ||=
|
|
148
|
+
POLICIES.each_with_object(Set.new) { |policy, set| set.merge(policy.instance.module_names) }
|
|
147
149
|
end
|
|
148
150
|
|
|
149
151
|
# Hook to only monkeypatch Contrast. This will not trigger any
|
|
@@ -179,7 +181,7 @@ module Contrast
|
|
|
179
181
|
|
|
180
182
|
# Begin patching our sources into the given module. Any patcher that has the name of the module will be
|
|
181
183
|
# evaluated for patching. Find all the patchers that apply to this class, sorted by type.
|
|
182
|
-
module_policy = Contrast::Agent::Patching::Policy::ModulePolicy.create_module_policy(module_data.
|
|
184
|
+
module_policy = Contrast::Agent::Patching::Policy::ModulePolicy.create_module_policy(module_data.mod_name)
|
|
183
185
|
# If there's nothing to match, then set that status and exit
|
|
184
186
|
if module_policy.empty?
|
|
185
187
|
status.no_patch!
|
|
@@ -189,8 +191,7 @@ module Contrast
|
|
|
189
191
|
status.patching!
|
|
190
192
|
num_applied_patches = patch_into_instance_methods(module_data, module_policy)
|
|
191
193
|
num_applied_patches += patch_into_singleton_methods(module_data, module_policy)
|
|
192
|
-
if adjust_for_prepend(module_data) ||
|
|
193
|
-
module_policy.num_expected_patches == num_applied_patches
|
|
194
|
+
if adjust_for_prepend(module_data) || module_policy.num_expected_patches == num_applied_patches
|
|
194
195
|
|
|
195
196
|
status.patched!
|
|
196
197
|
else
|
|
@@ -198,12 +199,12 @@ module Contrast
|
|
|
198
199
|
end
|
|
199
200
|
rescue StandardError => e
|
|
200
201
|
status&.failed_patch!
|
|
201
|
-
logger.warn('Patching failed', e, module: module_data.
|
|
202
|
+
logger.warn('Patching failed', e, module: module_data.mod_name)
|
|
202
203
|
ensure
|
|
203
204
|
logger.trace('Patching complete',
|
|
204
|
-
module: module_data.
|
|
205
|
-
result:
|
|
206
|
-
|
|
205
|
+
module: module_data.mod_name,
|
|
206
|
+
result:
|
|
207
|
+
Contrast::Agent::Patching::Policy::PatchStatus.get_status(module_data.mod).patch_status)
|
|
207
208
|
end
|
|
208
209
|
|
|
209
210
|
# Get all of the instance methods on the given module, excluding
|
|
@@ -270,7 +271,9 @@ module Contrast
|
|
|
270
271
|
def patch_into_methods mod, methods, module_policy, is_instance_method
|
|
271
272
|
count = 0
|
|
272
273
|
methods.each do |method|
|
|
273
|
-
method_policy = Contrast::Agent::Patching::Policy::MethodPolicy.build_method_policy(method,
|
|
274
|
+
method_policy = Contrast::Agent::Patching::Policy::MethodPolicy.build_method_policy(method,
|
|
275
|
+
module_policy,
|
|
276
|
+
is_instance_method)
|
|
274
277
|
next if method_policy.empty?
|
|
275
278
|
|
|
276
279
|
patched = patch_method(mod, methods, method_policy)
|
|
@@ -44,10 +44,20 @@ module Contrast
|
|
|
44
44
|
# later on. Really, if they don't have these things, they couldn't have
|
|
45
45
|
# done their jobs anyway.
|
|
46
46
|
def validate
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
unless class_name
|
|
48
|
+
raise(ArgumentError, "#{ node_class } #{ id } did not have a proper class name. Unable to create.")
|
|
49
|
+
end
|
|
50
|
+
unless method_name
|
|
51
|
+
raise(ArgumentError, "#{ node_class } #{ id } did not have a proper method name. Unable to create.")
|
|
52
|
+
end
|
|
53
|
+
unless method_name.is_a?(Symbol)
|
|
54
|
+
raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_name value. Unable to create.")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless method_visibility.is_a?(Symbol)
|
|
58
|
+
raise(ArgumentError,
|
|
59
|
+
"#{ node_class } #{ id } has a non symbol @method_visibility value. Unable to create.")
|
|
60
|
+
end
|
|
51
61
|
unless method_scope.nil? || Contrast::Agent::Scope.valid_scope?(method_scope)
|
|
52
62
|
raise(ArgumentError, "#{ node_class } #{ id } requires an undefined scope. Unable to create.")
|
|
53
63
|
end
|
|
@@ -21,7 +21,8 @@ module Contrast
|
|
|
21
21
|
JSON_OPTIONAL_PROPS = 'optional_properties'
|
|
22
22
|
JSON_ON_EXCEPTION = 'on_exception'
|
|
23
23
|
|
|
24
|
-
attr_reader :applicator, :applicator_method, :on_exception, :optional_properties, :required_properties,
|
|
24
|
+
attr_reader :applicator, :applicator_method, :on_exception, :optional_properties, :required_properties,
|
|
25
|
+
:rule_id
|
|
25
26
|
|
|
26
27
|
def initialize trigger_hash = {}, rule_hash = {}
|
|
27
28
|
super(trigger_hash)
|
|
@@ -42,9 +43,10 @@ module Contrast
|
|
|
42
43
|
|
|
43
44
|
def validate
|
|
44
45
|
super
|
|
45
|
-
unless applicator.public_methods(false).any?
|
|
46
|
+
unless applicator.public_methods(false).any?(applicator_method)
|
|
46
47
|
raise(ArgumentError,
|
|
47
|
-
"#{ id } did not have a proper applicator method:
|
|
48
|
+
"#{ id } did not have a proper applicator method: "\
|
|
49
|
+
"#{ applicator } does not respond to #{ applicator_method }. Unable to create.")
|
|
48
50
|
end
|
|
49
51
|
validate_properties
|
|
50
52
|
validate_rule
|
|
@@ -52,20 +54,31 @@ module Contrast
|
|
|
52
54
|
|
|
53
55
|
def validate_properties
|
|
54
56
|
if (required_properties & optional_properties).any?
|
|
55
|
-
raise(ArgumentError,
|
|
57
|
+
raise(ArgumentError,
|
|
58
|
+
"#{ rule_id } had overlapping elements between required and optional properties. Unable to create.")
|
|
56
59
|
end
|
|
57
60
|
if (properties.keys - (required_properties | optional_properties)).any?
|
|
58
61
|
raise(ArgumentError, "#{ id } had an unexpected property. Unable to create.")
|
|
59
62
|
end
|
|
60
|
-
|
|
63
|
+
|
|
64
|
+
return unless (required_properties - properties.keys).any?
|
|
65
|
+
|
|
66
|
+
raise(ArgumentError, "#{ id } did not have a required property. Unable to create.")
|
|
61
67
|
end
|
|
62
68
|
|
|
63
69
|
def validate_rule
|
|
64
70
|
raise(ArgumentError, 'Unknown rule did not have a proper name. Unable to create.') unless rule_id
|
|
65
71
|
raise(ArgumentError, "#{ id } did not have a proper applicator. Unable to create.") unless applicator
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
|
|
73
|
+
unless applicator_method
|
|
74
|
+
raise(ArgumentError, "#{ id } did not have a proper applicator method. Unable to create.")
|
|
75
|
+
end
|
|
76
|
+
unless required_properties
|
|
77
|
+
raise(ArgumentError, "#{ id } did not have a proper set of required properties. Unable to create.")
|
|
78
|
+
end
|
|
79
|
+
return if optional_properties
|
|
80
|
+
|
|
81
|
+
raise(ArgumentError, "#{ id } did not have a proper set of optional properties. Unable to create.")
|
|
69
82
|
end
|
|
70
83
|
|
|
71
84
|
private
|
|
@@ -42,15 +42,14 @@ module Contrast
|
|
|
42
42
|
|
|
43
43
|
protected
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def rule_name
|
|
46
46
|
Contrast::Agent::Protect::Rule::PathTraversal::NAME
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def possible_write? input
|
|
52
|
-
input.cs__respond_to?(:to_s) &&
|
|
53
|
-
input.to_s.include?(Contrast::Utils::ObjectShare::WRITE_FLAG)
|
|
52
|
+
input.cs__respond_to?(:to_s) && input.to_s.include?(Contrast::Utils::ObjectShare::WRITE_FLAG)
|
|
54
53
|
end
|
|
55
54
|
|
|
56
55
|
READ = 'read'
|
|
@@ -49,16 +49,15 @@ module Contrast
|
|
|
49
49
|
|
|
50
50
|
protected
|
|
51
51
|
|
|
52
|
-
def
|
|
52
|
+
def rule_name
|
|
53
53
|
Contrast::Agent::Protect::Rule::Xxe::NAME
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
-
DATA_KEY =
|
|
58
|
+
DATA_KEY = :@data
|
|
59
59
|
def valid_data_input? object
|
|
60
|
-
object.instance_variable_defined?(DATA_KEY) &&
|
|
61
|
-
object.instance_variable_get(DATA_KEY)
|
|
60
|
+
object.instance_variable_defined?(DATA_KEY) && object.instance_variable_get(DATA_KEY)
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
NOKOGIRI_MARKER = 'Nokogiri::'
|
|
@@ -115,11 +114,8 @@ module Contrast
|
|
|
115
114
|
raise e
|
|
116
115
|
rescue StandardError => e
|
|
117
116
|
parser ||= Contrast::Utils::ObjectShare::UNKNOWN
|
|
118
|
-
logger.error(
|
|
119
|
-
|
|
120
|
-
e,
|
|
121
|
-
module: potential_parser.cs__class.cs__name,
|
|
122
|
-
method: method, parser: parser)
|
|
117
|
+
logger.error('Error applying xxe', e, module: potential_parser.cs__class.cs__name, method: method,
|
|
118
|
+
parser: parser)
|
|
123
119
|
end
|
|
124
120
|
end
|
|
125
121
|
end
|