contrast-agent 4.9.1 → 4.10.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/.rspec +0 -1
- data/.rspec_parallel +6 -0
- data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
- data/lib/contrast/agent/assess/contrast_event.rb +0 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
- data/lib/contrast/agent/assess/policy/patcher.rb +0 -1
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +0 -2
- data/lib/contrast/agent/assess/policy/preshift.rb +8 -5
- data/lib/contrast/agent/assess/policy/propagation_method.rb +100 -57
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +0 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/split.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +1 -0
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +0 -1
- data/lib/contrast/agent/assess/policy/source_method.rb +13 -17
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +59 -83
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
- data/lib/contrast/agent/disable_reaction.rb +1 -1
- data/lib/contrast/agent/exclusion_matcher.rb +0 -4
- data/lib/contrast/agent/inventory/database_config.rb +117 -0
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +5 -4
- data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +18 -12
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +5 -0
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +8 -8
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
- data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
- data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/request.rb +5 -2
- data/lib/contrast/agent/request_context.rb +19 -22
- data/lib/contrast/agent/static_analysis.rb +1 -1
- data/lib/contrast/agent/tracepoint_hook.rb +6 -1
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +12 -6
- data/lib/contrast/api/communication/service_lifecycle.rb +4 -1
- data/lib/contrast/api/communication/socket_client.rb +4 -4
- data/lib/contrast/api/decorators/agent_startup.rb +4 -4
- data/lib/contrast/api/decorators/application_startup.rb +6 -5
- data/lib/contrast/api/decorators/route_coverage.rb +24 -1
- data/lib/contrast/components/agent.rb +5 -2
- data/lib/contrast/components/assess.rb +6 -3
- data/lib/contrast/components/base.rb +2 -2
- data/lib/contrast/components/config.rb +1 -0
- data/lib/contrast/components/contrast_service.rb +4 -2
- data/lib/contrast/components/logger.rb +13 -8
- data/lib/contrast/components/scope.rb +9 -28
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/configuration.rb +19 -15
- data/lib/contrast/extension/assess/array.rb +1 -11
- data/lib/contrast/extension/assess/eval_trigger.rb +0 -20
- data/lib/contrast/extension/assess/fiber.rb +0 -11
- data/lib/contrast/extension/assess/hash.rb +0 -10
- data/lib/contrast/extension/assess/kernel.rb +1 -10
- data/lib/contrast/extension/assess/marshal.rb +3 -11
- data/lib/contrast/extension/assess/regexp.rb +0 -11
- data/lib/contrast/extension/assess/string.rb +1 -26
- data/lib/contrast/extension/extension.rb +61 -0
- data/lib/contrast/extension/protect/kernel.rb +0 -10
- data/lib/contrast/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +42 -6
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
- data/lib/contrast/framework/rails/patch/support.rb +6 -3
- data/lib/contrast/framework/rails/railtie.rb +1 -1
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +1 -0
- data/lib/contrast/framework/rails/support.rb +60 -13
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/logger/log.rb +89 -15
- data/lib/contrast/utils/io_util.rb +1 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/tag_util.rb +2 -1
- data/resources/assess/policy.json +197 -2
- data/resources/deadzone/policy.json +10 -0
- data/ruby-agent.gemspec +10 -1
- metadata +78 -12
- data/lib/contrast/utils/inventory_util.rb +0 -113
@@ -7,12 +7,9 @@ require 'contrast/agent/scope'
|
|
7
7
|
|
8
8
|
# This is the Scope component.
|
9
9
|
#
|
10
|
-
# It tracks /Contrast/ scope. That is, "are we currently doing assess
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# Separately from this component, there is also require scope, which is an
|
15
|
-
# optimization on how we implement patching to `require`.
|
10
|
+
# It tracks /Contrast/ scope. That is, "are we currently doing assess or protect stuff within a patched method?" --
|
11
|
+
# this is how we avoid doing Contrast stuff on Contrast code or creating infinite loops -- or "are we in some other
|
12
|
+
# execution context for which we need to special case?".
|
16
13
|
module Contrast
|
17
14
|
module Components
|
18
15
|
module Scope # :nodoc:
|
@@ -25,10 +22,8 @@ module Contrast
|
|
25
22
|
EXECUTION_CONTEXT[Fiber.current] = Contrast::Agent::Scope.new
|
26
23
|
end
|
27
24
|
|
28
|
-
# This returns the scope governing the current execution context.
|
29
|
-
#
|
30
|
-
# access and query scope, rather than interacting with the scope
|
31
|
-
# object directly.
|
25
|
+
# This returns the scope governing the current execution context. Use this sparingly, preferring the instance
|
26
|
+
# & class methods to access and query scope, rather than interacting with the scope object directly.
|
32
27
|
def scope_for_current_ec
|
33
28
|
MONITOR.synchronize do
|
34
29
|
return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new
|
@@ -37,9 +32,7 @@ module Contrast
|
|
37
32
|
end
|
38
33
|
|
39
34
|
module InstanceMethods # :nodoc:
|
40
|
-
# For each instance method on a scope, define a forwarder
|
41
|
-
# to the scope on the current execution context's scope.
|
42
|
-
|
35
|
+
# For each instance method on a scope, define a forwarder to the scope on the current execution context's scope.
|
43
36
|
def scope_for_current_ec
|
44
37
|
MONITOR.synchronize do
|
45
38
|
return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new
|
@@ -118,24 +111,12 @@ module Contrast
|
|
118
111
|
ensure
|
119
112
|
scope_for_current_ec.exit_split_scope!
|
120
113
|
end
|
121
|
-
|
122
|
-
# TODO: RUBY-572
|
123
|
-
#
|
124
|
-
# Current behavior is to no-op if we're not "in a request context".
|
125
|
-
# Our C functions were previously checking to see if we had a scope, because
|
126
|
-
# scope was tacked on to a request context -- so "we have a scope, therefore,
|
127
|
-
# we have a request context." We've decoupled scopes from request contexts,
|
128
|
-
# so now it checks "do we have a request context."
|
129
|
-
# RUBY-290 should remove all of that, including this method.
|
130
|
-
def in_request_context?
|
131
|
-
!!Contrast::Agent::REQUEST_TRACKER.current
|
132
|
-
end
|
133
114
|
end
|
134
115
|
|
135
116
|
def self.sweep_dead_ecs
|
136
|
-
# TODO: RUBY-
|
137
|
-
#
|
138
|
-
#
|
117
|
+
# TODO: RUBY-534, #sweep_dead_ecs compensates for a lack of weak tables. when we can use WeakRef, we should
|
118
|
+
# investigate removing this call and instead use the WeakRef for the Execution Context's Keys or using our
|
119
|
+
# Finalizers Hash for Fibers
|
139
120
|
MONITOR.synchronize do
|
140
121
|
EXECUTION_CONTEXT.delete_if do |ec, _scope|
|
141
122
|
!ec.alive?
|
@@ -12,7 +12,7 @@ module Contrast
|
|
12
12
|
class BaseConfiguration
|
13
13
|
extend Forwardable
|
14
14
|
|
15
|
-
|
15
|
+
STRING_BOOLEANS = %w[false true].cs__freeze
|
16
16
|
|
17
17
|
attr_reader :map
|
18
18
|
|
@@ -73,8 +73,18 @@ module Contrast
|
|
73
73
|
spec_value.new(user_provided_value)
|
74
74
|
elsif spec_value.is_a?(Contrast::Config::DefaultValue) && user_provided_value == EMPTY_VALUE
|
75
75
|
spec_value.value
|
76
|
-
elsif
|
77
|
-
user_provided_value.
|
76
|
+
elsif user_provided_value.cs__is_a?(String)
|
77
|
+
value = user_provided_value.downcase
|
78
|
+
# converts string values to 'true' => true or 'false' => false
|
79
|
+
case value
|
80
|
+
when STRING_BOOLEANS[1]
|
81
|
+
true
|
82
|
+
when STRING_BOOLEANS[0]
|
83
|
+
false
|
84
|
+
else
|
85
|
+
# returns non boolean string values
|
86
|
+
user_provided_value
|
87
|
+
end
|
78
88
|
else
|
79
89
|
user_provided_value
|
80
90
|
end
|
@@ -95,9 +105,7 @@ module Contrast
|
|
95
105
|
|
96
106
|
def define_setter str_key
|
97
107
|
define_singleton_method "#{ str_key }=".to_sym do |new_value|
|
98
|
-
|
99
|
-
boolean_value ||= new_value == false
|
100
|
-
@map[str_key] = boolean_value ? new_value.to_s : new_value
|
108
|
+
@map[str_key] = new_value
|
101
109
|
end
|
102
110
|
end
|
103
111
|
end
|
@@ -48,9 +48,7 @@ module Contrast
|
|
48
48
|
# in an infinite loop on the to_sym method used later.
|
49
49
|
def method_missing symbol, *args
|
50
50
|
with_contrast_scope do
|
51
|
-
root.public_send(symbol, *args)
|
52
|
-
rescue NoMethodError => _e
|
53
|
-
super
|
51
|
+
root.public_send(symbol, *args) if root.cs__respond_to?(symbol)
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
@@ -101,8 +99,7 @@ module Contrast
|
|
101
99
|
{}
|
102
100
|
end
|
103
101
|
|
104
|
-
# We're updating properties loaded from the configuration
|
105
|
-
# files to match the new agreed upon standard configuration
|
102
|
+
# We're updating properties loaded from the configuration files to match the new agreed upon standard configuration
|
106
103
|
# names, so that one file works for all agents
|
107
104
|
def update_prop_keys config
|
108
105
|
CONVERSION.each_pair do |old_method, new_method|
|
@@ -120,16 +117,7 @@ module Contrast
|
|
120
117
|
# We changed the seconds values into ms values. Multiply them accordingly
|
121
118
|
old_value = old_value.to_i * 1000 if new_method.end_with?(MILLISECOND_MARKER)
|
122
119
|
new_value = config
|
123
|
-
|
124
|
-
new_keys.each_with_index do |new_key, index|
|
125
|
-
if index == end_idx
|
126
|
-
new_value[new_key] = old_value if new_value[new_key].nil?
|
127
|
-
else
|
128
|
-
new_value = {} if new_value.nil?
|
129
|
-
new_value[new_key] = {} if new_value[new_key].nil?
|
130
|
-
new_value = new_value[new_key]
|
131
|
-
end
|
132
|
-
end
|
120
|
+
replace_props(new_keys, new_value, old_value)
|
133
121
|
end
|
134
122
|
|
135
123
|
config
|
@@ -237,5 +225,21 @@ module Contrast
|
|
237
225
|
convert
|
238
226
|
end
|
239
227
|
end
|
228
|
+
|
229
|
+
def replace_props new_keys, new_value, old_value
|
230
|
+
idx = 0
|
231
|
+
end_idx = new_keys.length - 1
|
232
|
+
while idx < new_keys.length
|
233
|
+
new_key = new_keys[idx]
|
234
|
+
if idx == end_idx
|
235
|
+
new_value[new_key] = old_value if new_value[new_key].nil?
|
236
|
+
else
|
237
|
+
new_value = {} if new_value.nil?
|
238
|
+
new_value[new_key] = {} if new_value[new_key].nil?
|
239
|
+
new_value = new_value[new_key]
|
240
|
+
end
|
241
|
+
idx += 1
|
242
|
+
end
|
243
|
+
end
|
240
244
|
end
|
241
245
|
end
|
@@ -11,7 +11,7 @@ module Contrast
|
|
11
11
|
# This is our patch of the Array class required to handle propagation
|
12
12
|
# Disclaimer: there may be a better way, but we're in a 'get it work' state.
|
13
13
|
# Hopefully, we'll be in a 'get it right' state soon.
|
14
|
-
class ArrayPropagator
|
14
|
+
class ArrayPropagator # rubocop:disable Style/StaticClass
|
15
15
|
extend Contrast::Components::Scope::InstanceMethods
|
16
16
|
|
17
17
|
ARRAY_JOIN_HASH = {
|
@@ -59,16 +59,6 @@ module Contrast
|
|
59
59
|
ret
|
60
60
|
end
|
61
61
|
end
|
62
|
-
|
63
|
-
def instrument_array_track
|
64
|
-
@_instrument_array_track ||= begin
|
65
|
-
require 'cs__assess_array/cs__assess_array'
|
66
|
-
true
|
67
|
-
end
|
68
|
-
rescue StandardError, LoadError => e
|
69
|
-
logger.error('Error loading assess track patch', e)
|
70
|
-
false
|
71
|
-
end
|
72
62
|
end
|
73
63
|
end
|
74
64
|
end
|
@@ -34,26 +34,6 @@ module Contrast
|
|
34
34
|
ret, source)
|
35
35
|
end
|
36
36
|
|
37
|
-
def instrument_basic_object_track
|
38
|
-
@_instrument_basic_object_track ||= begin
|
39
|
-
require 'cs__assess_basic_object/cs__assess_basic_object'
|
40
|
-
true
|
41
|
-
end
|
42
|
-
rescue StandardError, LoadError => e
|
43
|
-
logger.error('Error loading basic object track patch', e)
|
44
|
-
false
|
45
|
-
end
|
46
|
-
|
47
|
-
def instrument_module_track
|
48
|
-
@_instrument_module_track ||= begin
|
49
|
-
require 'cs__assess_module/cs__assess_module'
|
50
|
-
true
|
51
|
-
end
|
52
|
-
rescue StandardError, LoadError => e
|
53
|
-
logger.error('Error loading module track patch', e)
|
54
|
-
false
|
55
|
-
end
|
56
|
-
|
57
37
|
private
|
58
38
|
|
59
39
|
def trigger_node clazz, method
|
@@ -20,7 +20,6 @@ module Contrast
|
|
20
20
|
extend Contrast::Components::Logger::InstanceMethods
|
21
21
|
extend Contrast::Components::Scope::InstanceMethods
|
22
22
|
|
23
|
-
|
24
23
|
# we use funchook to patch rb_fiber_new the initialize method is not exposed by Ruby core
|
25
24
|
FIBER_NEW_NODE_HASH = {
|
26
25
|
'class_name' => 'Fiber',
|
@@ -86,16 +85,6 @@ module Contrast
|
|
86
85
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
87
86
|
logger.error('Unable to propagate during Fiber.new', e)
|
88
87
|
end
|
89
|
-
|
90
|
-
def instrument_fiber_track
|
91
|
-
@_instrument_fiber_variables ||= begin
|
92
|
-
require 'cs__assess_fiber_track/cs__assess_fiber_track' if Funchook.available?
|
93
|
-
true
|
94
|
-
end
|
95
|
-
rescue StandardError, LoadError => e
|
96
|
-
logger.error('Error loading fiber track patch', e)
|
97
|
-
false
|
98
|
-
end
|
99
88
|
end
|
100
89
|
end
|
101
90
|
end
|
@@ -25,16 +25,6 @@ module Contrast
|
|
25
25
|
# result in a seg fault
|
26
26
|
object
|
27
27
|
end
|
28
|
-
|
29
|
-
def instrument_hash_track
|
30
|
-
@_instrument_hash_track ||= begin
|
31
|
-
require 'cs__assess_hash/cs__assess_hash'
|
32
|
-
true
|
33
|
-
end
|
34
|
-
rescue StandardError, LoadError => e
|
35
|
-
logger.error('Error loading hash track patch', e)
|
36
|
-
false
|
37
|
-
end
|
38
28
|
end
|
39
29
|
end
|
40
30
|
end
|
@@ -14,6 +14,7 @@ module Contrast
|
|
14
14
|
module KernelPropagator
|
15
15
|
class << self
|
16
16
|
extend Contrast::Components::Logger::InstanceMethods
|
17
|
+
include Contrast::Components::Logger::InstanceMethods
|
17
18
|
include Contrast::Extension::Assess::ExecTrigger
|
18
19
|
|
19
20
|
# We're 'tracking' sprintf now, meaning if anything is tracked on the way
|
@@ -65,16 +66,6 @@ module Contrast
|
|
65
66
|
logger.error('Unable to track dataflow through sprintf', e)
|
66
67
|
end
|
67
68
|
|
68
|
-
def instrument_kernel_track
|
69
|
-
@_instrument_fiber_variables ||= begin
|
70
|
-
require 'cs__assess_kernel/cs__assess_kernel'
|
71
|
-
true
|
72
|
-
end
|
73
|
-
rescue StandardError, LoadError => e
|
74
|
-
logger.error('Error loading kernel track patch', e)
|
75
|
-
false
|
76
|
-
end
|
77
|
-
|
78
69
|
private
|
79
70
|
|
80
71
|
def handle_sprintf_value value, result, parent_events
|
@@ -12,10 +12,12 @@ module Contrast
|
|
12
12
|
# Hopefully, we'll be in a 'get it right' state soon.
|
13
13
|
# This module is used for our Marshal.load patches
|
14
14
|
class MarshalPropagator
|
15
|
-
extend Contrast::Components::Logger::InstanceMethods
|
16
15
|
extend Contrast::Components::Scope::InstanceMethods
|
17
16
|
|
18
17
|
class << self
|
18
|
+
extend Contrast::Components::Logger::InstanceMethods
|
19
|
+
include Contrast::Components::Logger::InstanceMethods
|
20
|
+
|
19
21
|
def cs__load_protect arg
|
20
22
|
return if in_contrast_scope?
|
21
23
|
|
@@ -44,16 +46,6 @@ module Contrast
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
def instrument_marshal_load
|
48
|
-
@_instrument_marshal_load ||= begin
|
49
|
-
require 'cs__assess_marshal_module/cs__assess_marshal_module'
|
50
|
-
true
|
51
|
-
end
|
52
|
-
rescue StandardError, LoadError => e
|
53
|
-
logger.error('Error loading marshal load patch', e)
|
54
|
-
false
|
55
|
-
end
|
56
|
-
|
57
49
|
def trigger_node clazz, method
|
58
50
|
triggers = Contrast::Agent::Assess::Policy::Policy.instance.triggers
|
59
51
|
return unless triggers
|
@@ -16,7 +16,6 @@ module Contrast
|
|
16
16
|
extend Contrast::Components::Logger::InstanceMethods
|
17
17
|
extend Contrast::Components::Scope::InstanceMethods
|
18
18
|
|
19
|
-
|
20
19
|
REGEXP_EQUAL_SQUIGGLE_HASH = {
|
21
20
|
'id' => 'regexp_100',
|
22
21
|
'class_name' => 'Regexp',
|
@@ -59,16 +58,6 @@ module Contrast
|
|
59
58
|
rescue Exception => e # rubocop:disable Lint/RescueException
|
60
59
|
logger.error('Unable to propagate during Regexp#=~', e)
|
61
60
|
end
|
62
|
-
|
63
|
-
def instrument_regexp_track
|
64
|
-
@_instrument_regexp_track ||= begin
|
65
|
-
require 'cs__assess_regexp/cs__assess_regexp'
|
66
|
-
true
|
67
|
-
end
|
68
|
-
rescue StandardError, LoadError => e
|
69
|
-
logger.error('Error loading regexp track patch', e)
|
70
|
-
false
|
71
|
-
end
|
72
61
|
end
|
73
62
|
end
|
74
63
|
end
|
@@ -12,7 +12,7 @@ module Contrast
|
|
12
12
|
# methods which are too complex to fit into one of the standard
|
13
13
|
# Contrast::Agent::Assess::Policy::Propagator molds without cluttering up the
|
14
14
|
# String Class or exposing our methods there.
|
15
|
-
class StringPropagator
|
15
|
+
class StringPropagator # rubocop:disable Style/StaticClass
|
16
16
|
extend Contrast::Components::Logger::InstanceMethods
|
17
17
|
extend Contrast::Components::Scope::InstanceMethods
|
18
18
|
|
@@ -52,31 +52,6 @@ module Contrast
|
|
52
52
|
rescue StandardError => e
|
53
53
|
logger.error('Unable to track interpolation', e)
|
54
54
|
end
|
55
|
-
|
56
|
-
def instrument_string
|
57
|
-
@_instrument_string ||= begin
|
58
|
-
require 'cs__assess_string/cs__assess_string'
|
59
|
-
true
|
60
|
-
end
|
61
|
-
rescue StandardError, LoadError => e
|
62
|
-
logger.error('Error loading hash track patch', e)
|
63
|
-
false
|
64
|
-
end
|
65
|
-
|
66
|
-
def instrument_string_interpolation
|
67
|
-
if @_instrument_string_interpolation.nil?
|
68
|
-
@_instrument_string_interpolation = begin
|
69
|
-
if ::Contrast::AGENT.patch_interpolation? && Funchook.available?
|
70
|
-
require 'cs__assess_string_interpolation26/cs__assess_string_interpolation26'
|
71
|
-
end
|
72
|
-
true
|
73
|
-
rescue StandardError, LoadError => e
|
74
|
-
logger.error('Error loading interpolation patch', e)
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
@_instrument_string_interpolation
|
79
|
-
end
|
80
55
|
end
|
81
56
|
end
|
82
57
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'contrast/components/logger'
|
5
|
+
|
6
|
+
module Contrast
|
7
|
+
module Extension
|
8
|
+
# Our top level Assess namespace in the Core Extension section of our
|
9
|
+
# code. These patches are those that are invoked directly from a patched
|
10
|
+
# Class.
|
11
|
+
#
|
12
|
+
module Assess
|
13
|
+
# This is the main instrument helper giving the method of requiring C patches
|
14
|
+
#
|
15
|
+
module InstrumentHelper
|
16
|
+
class << self
|
17
|
+
include Contrast::Components::Logger::InstanceMethods
|
18
|
+
|
19
|
+
# Unites the different require methods into one, using only
|
20
|
+
# the provided path for the C patches
|
21
|
+
# parameters
|
22
|
+
# @param path[String] Path to the required patch
|
23
|
+
#
|
24
|
+
def instrument path
|
25
|
+
var_name, extracted_name = gen_name(path)
|
26
|
+
return if instance_variable_get(var_name) == true
|
27
|
+
|
28
|
+
instance_variable_set(var_name, assign_value(path))
|
29
|
+
rescue StandardError, LoadError => e
|
30
|
+
logger.error("Error loading #{ extracted_name&.nil? ? '' : extracted_name } patch", e)
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Some of the requires have some extra conditions for them to require
|
35
|
+
# the C patches, so this method is helping us move the logic by making some
|
36
|
+
# conditions
|
37
|
+
def assign_value path
|
38
|
+
case path
|
39
|
+
when /fiber/
|
40
|
+
require path if Funchook.available?
|
41
|
+
when /interpolation26/
|
42
|
+
require path if ::Contrast::AGENT.patch_interpolation? && Funchook.available?
|
43
|
+
else
|
44
|
+
require path
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generate the needed instance variable name and return the extracted name
|
50
|
+
def gen_name path
|
51
|
+
extracted_name = path.split(%r{[\s_/]})&.uniq&.delete_if do |s|
|
52
|
+
s.empty? || s == 'cs' || s == 'assess' || s == 'track'
|
53
|
+
end
|
54
|
+
extracted_name = (extracted_name&.length || 0) > 1 ? extracted_name&.join('_') : extracted_name&.pop
|
55
|
+
["@_instrument_#{ extracted_name }_track", extracted_name]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|