contrast-agent 4.9.1 → 4.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rspec_parallel +6 -0
- data/ext/cs__assess_module/cs__assess_module.c +48 -0
- data/ext/cs__assess_module/cs__assess_module.h +7 -0
- data/ext/cs__common/cs__common.c +24 -7
- data/ext/cs__common/cs__common.h +12 -2
- data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -12
- data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -4
- data/ext/cs__os_information/cs__os_information.c +31 -0
- data/ext/cs__os_information/cs__os_information.h +7 -0
- data/ext/{cs__protect_kernel → cs__os_information}/extconf.rb +0 -0
- data/lib/contrast/agent/assess/contrast_event.rb +1 -2
- data/lib/contrast/agent/assess/contrast_object.rb +1 -4
- data/lib/contrast/agent/assess/finalizers/hash.rb +0 -1
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
- 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 +29 -12
- data/lib/contrast/agent/assess/policy/propagation_method.rb +71 -142
- data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
- 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 +15 -88
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +45 -172
- data/lib/contrast/agent/assess/policy/trigger_node.rb +52 -19
- data/lib/contrast/agent/assess/property/evented.rb +2 -1
- data/lib/contrast/agent/assess/property/tagged.rb +15 -132
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +0 -1
- data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
- 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 +7 -5
- data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
- data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
- data/lib/contrast/agent/middleware.rb +23 -0
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -0
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +17 -12
- data/lib/contrast/agent/patching/policy/method_policy.rb +54 -9
- data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
- data/lib/contrast/agent/patching/policy/patch.rb +42 -238
- data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -49
- 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 +9 -4
- data/lib/contrast/agent/request_context.rb +51 -33
- data/lib/contrast/agent/request_handler.rb +7 -3
- data/lib/contrast/agent/rule_set.rb +2 -4
- data/lib/contrast/agent/scope.rb +32 -20
- data/lib/contrast/agent/startup_metrics_telemetry_event.rb +71 -0
- data/lib/contrast/agent/static_analysis.rb +5 -3
- data/lib/contrast/agent/telemetry.rb +129 -0
- data/lib/contrast/agent/telemetry_event.rb +34 -0
- data/lib/contrast/agent/thread_watcher.rb +43 -14
- data/lib/contrast/agent/tracepoint_hook.rb +16 -3
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/agent.rb +6 -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/api.rb +34 -0
- data/lib/contrast/components/app_context.rb +24 -0
- data/lib/contrast/components/assess.rb +13 -3
- data/lib/contrast/components/base.rb +2 -2
- data/lib/contrast/components/config.rb +91 -11
- data/lib/contrast/components/contrast_service.rb +10 -2
- data/lib/contrast/components/logger.rb +13 -8
- data/lib/contrast/components/scope.rb +9 -28
- data/lib/contrast/config/api_configuration.rb +22 -0
- data/lib/contrast/config/assess_configuration.rb +1 -0
- data/lib/contrast/config/base_configuration.rb +14 -6
- data/lib/contrast/config/env_variables.rb +25 -0
- data/lib/contrast/config/root_configuration.rb +1 -0
- data/lib/contrast/config/service_configuration.rb +2 -1
- data/lib/contrast/config.rb +1 -0
- data/lib/contrast/configuration.rb +22 -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/framework/grape/support.rb +174 -0
- data/lib/contrast/framework/manager.rb +56 -18
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +0 -1
- data/lib/contrast/framework/rails/patch/support.rb +35 -30
- 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/application.rb +4 -0
- data/lib/contrast/logger/log.rb +89 -15
- data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
- data/lib/contrast/utils/assess/property/tagged_utils.rb +142 -0
- data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
- data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
- data/lib/contrast/utils/class_util.rb +58 -44
- data/lib/contrast/utils/exclude_key.rb +20 -0
- data/lib/contrast/utils/io_util.rb +43 -35
- data/lib/contrast/utils/lru_cache.rb +45 -0
- data/lib/contrast/utils/metrics_hash.rb +59 -0
- data/lib/contrast/utils/os.rb +23 -0
- data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
- data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
- data/lib/contrast/utils/requests_client.rb +150 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
- data/lib/contrast/utils/tag_util.rb +2 -1
- data/lib/contrast/utils/telemetry.rb +78 -0
- data/lib/contrast/utils/telemetry_identifier.rb +137 -0
- data/lib/contrast.rb +19 -1
- data/resources/assess/policy.json +208 -7
- data/resources/deadzone/policy.json +91 -0
- data/ruby-agent.gemspec +12 -2
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +102 -18
- data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
- data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
- data/lib/contrast/extension/protect/kernel.rb +0 -39
- data/lib/contrast/utils/inventory_util.rb +0 -113
@@ -0,0 +1,138 @@
|
|
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
|
+
module Contrast
|
5
|
+
module Utils
|
6
|
+
module Assess
|
7
|
+
# This module will include all methods for some internal validations/appliers in the TriggerMethod module
|
8
|
+
# and some other module methods from the same place, so we can ease the main module
|
9
|
+
module TriggerMethodUtils
|
10
|
+
# A request is reportable if it is not from ActionController::Live
|
11
|
+
#
|
12
|
+
# @param env [Hash] the env of the Request
|
13
|
+
# @return [Boolean]
|
14
|
+
def reportable? env
|
15
|
+
!(defined?(ActionController::Live) &&
|
16
|
+
env &&
|
17
|
+
env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live))
|
18
|
+
end
|
19
|
+
|
20
|
+
# Find the request for this finding. This assumes, for now, that if there is an active request, then that
|
21
|
+
# is the request to report. Otherwise, we'll use the first request found in the events of the
|
22
|
+
# source_object.
|
23
|
+
#
|
24
|
+
# @param source [Object,nil] some Object used as the source of a trigger event
|
25
|
+
# @return [Contrast::Agent::Request,nil] the request from which the dataflow on the request originated.
|
26
|
+
def find_request source
|
27
|
+
return Contrast::Agent::REQUEST_TRACKER.current.request if Contrast::Agent::REQUEST_TRACKER.current
|
28
|
+
return unless (properties = Contrast::Agent::Assess::Tracker.properties(source))
|
29
|
+
|
30
|
+
find_event_request(properties.event)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Finds the first request along the left most tree of parent events
|
34
|
+
#
|
35
|
+
# @param event [Contrast::Agent::Assess::ContrastEvent|Contrast::Agent::Assess::Events::SourceEvent]
|
36
|
+
# @return [Contrast::Agent::Request, nil]
|
37
|
+
def find_event_request event
|
38
|
+
return event.request if event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
39
|
+
|
40
|
+
idx = 0
|
41
|
+
while idx <= event.parent_events.length
|
42
|
+
found = find_event_request(event.parent_events[idx])
|
43
|
+
return found if found
|
44
|
+
|
45
|
+
idx += 1
|
46
|
+
return event.request if event.request
|
47
|
+
end
|
48
|
+
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
49
|
+
|
50
|
+
event.request
|
51
|
+
end
|
52
|
+
|
53
|
+
# ===== APPLIERS =====
|
54
|
+
# This is our method that actually checks the taint on the object our trigger_node targets.
|
55
|
+
#
|
56
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
57
|
+
# trigger event
|
58
|
+
# @param source [Object] the source of the Trigger Event
|
59
|
+
# @param object [Object] the Object on which the method was invoked
|
60
|
+
# @param ret [Object] the Return of the invoked method
|
61
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
62
|
+
def apply_trigger trigger_node, source, object, ret, *args
|
63
|
+
return unless trigger_node
|
64
|
+
return if trigger_node.rule_disabled?
|
65
|
+
return if trigger_node.dataflow? && source.nil?
|
66
|
+
|
67
|
+
if trigger_node.regexp_rule?
|
68
|
+
apply_regexp_rule(trigger_node, source, object, ret, *args)
|
69
|
+
elsif trigger_node.custom_trigger?
|
70
|
+
trigger_node.apply_custom_trigger(trigger_node, source, object, ret, *args)
|
71
|
+
elsif trigger_node.dataflow?
|
72
|
+
apply_dataflow_rule(trigger_node, source, object, ret, *args)
|
73
|
+
else # trigger rule - just calling the method is dangerous
|
74
|
+
build_finding(trigger_node, source, object, ret, *args)
|
75
|
+
end
|
76
|
+
rescue StandardError => e
|
77
|
+
logger.warn('Unable to apply trigger', e, node_id: trigger_node.id)
|
78
|
+
end
|
79
|
+
|
80
|
+
# This is our method that actually checks the taint on the object our trigger_node targets for our Regexp
|
81
|
+
# based rules.
|
82
|
+
#
|
83
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
84
|
+
# trigger event
|
85
|
+
# @param source [Object] the source of the Trigger Event
|
86
|
+
# @param object [Object] the Object on which the method was invoked
|
87
|
+
# @param ret [Object] the Return of the invoked method
|
88
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
89
|
+
def apply_regexp_rule trigger_node, source, object, ret, *args
|
90
|
+
return unless source.is_a?(String)
|
91
|
+
return if trigger_node.good_value && source.match?(trigger_node.good_value)
|
92
|
+
return if trigger_node.bad_value && source !~ trigger_node.bad_value
|
93
|
+
|
94
|
+
build_finding(trigger_node, source, object, ret, *args)
|
95
|
+
end
|
96
|
+
|
97
|
+
# This is our method that actually checks the taint on the object our trigger_node targets for our Dataflow
|
98
|
+
# based rules.
|
99
|
+
#
|
100
|
+
# @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode] the node to direct applying this
|
101
|
+
# trigger event
|
102
|
+
# @param source [Object] the source of the Trigger Event
|
103
|
+
# @param object [Object] the Object on which the method was invoked
|
104
|
+
# @param ret [Object] the Return of the invoked method
|
105
|
+
# @param args [Array<Object>] the Arguments with which the method was invoked
|
106
|
+
def apply_dataflow_rule trigger_node, source, object, ret, *args
|
107
|
+
return unless source
|
108
|
+
|
109
|
+
if Contrast::Agent::Assess::Tracker.trackable?(source)
|
110
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
111
|
+
return unless trigger_node.violated?(source)
|
112
|
+
|
113
|
+
build_finding(trigger_node, source, object, ret, *args)
|
114
|
+
elsif Contrast::Utils::DuckUtils.iterable_hash?(source)
|
115
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
116
|
+
|
117
|
+
source.each_pair do |key, value|
|
118
|
+
apply_dataflow_rule(trigger_node, key, object, ret, *args)
|
119
|
+
apply_dataflow_rule(trigger_node, value, object, ret, *args)
|
120
|
+
end
|
121
|
+
elsif Contrast::Utils::DuckUtils.iterable_enumerable?(source)
|
122
|
+
return unless Contrast::Agent::Assess::Tracker.tracked?(source)
|
123
|
+
|
124
|
+
source.each do |value|
|
125
|
+
apply_dataflow_rule(trigger_node, value, object, ret, *args)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
logger.debug('Trigger source is untrackable. Unable to inspect.',
|
129
|
+
node_id: trigger_node.id,
|
130
|
+
source_id: source.__id__,
|
131
|
+
source_type: source.cs__class.cs__name,
|
132
|
+
frozen: source.cs__frozen?)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -3,21 +3,21 @@
|
|
3
3
|
|
4
4
|
require 'contrast/extension/module'
|
5
5
|
require 'contrast/utils/object_share'
|
6
|
+
require 'contrast/utils/lru_cache'
|
6
7
|
|
7
8
|
module Contrast
|
8
9
|
module Utils
|
9
10
|
# Utility methods for exploring the complete space of Objects
|
10
11
|
class ClassUtil
|
12
|
+
@lru_cache = LRUCache.new(300)
|
13
|
+
@string_cache = LRUCache.new(300)
|
11
14
|
class << self
|
12
|
-
# some classes have had things prepended to them, like Marshal in Rails
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# patching approaches. As such, we need to know if something has been
|
17
|
-
# prepended to.
|
15
|
+
# some classes have had things prepended to them, like Marshal in Rails 5 and higher. Their
|
16
|
+
# ActiveSupport::MarshalWithAutoloading will break our alias patching approach, as will any other prepend on
|
17
|
+
# something that we touch. Prepend and Alias are inherently incompatible monkey patching approaches. As such,
|
18
|
+
# we need to know if something has been prepended to.
|
18
19
|
#
|
19
|
-
# @param mod [Module] the Module to check to see if it has had something
|
20
|
-
# prepended
|
20
|
+
# @param mod [Module] the Module to check to see if it has had something prepended
|
21
21
|
# @param ancestors [Array<Module>] the array of ancestors for the mod
|
22
22
|
# @return [Boolean] if the mod has been prepended or not
|
23
23
|
def prepended? mod, ancestors = nil
|
@@ -25,8 +25,13 @@ module Contrast
|
|
25
25
|
ancestors[0] != mod
|
26
26
|
end
|
27
27
|
|
28
|
-
# return true if the given method is overwritten by one of the ancestors
|
29
|
-
#
|
28
|
+
# return true if the given method is overwritten by one of the ancestors in the ancestor change that comes
|
29
|
+
# before the given module
|
30
|
+
#
|
31
|
+
# @param mod [Module] the Module to check to see if it has had something prepended
|
32
|
+
# @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that holds the method we
|
33
|
+
# need to check
|
34
|
+
# @return [Boolean] if this method specifically was prepended
|
30
35
|
def prepended_method? mod, method_policy
|
31
36
|
target_module = determine_target_class mod, method_policy.instance_method
|
32
37
|
ancestors = target_module.ancestors
|
@@ -41,44 +46,49 @@ module Contrast
|
|
41
46
|
false
|
42
47
|
end
|
43
48
|
|
44
|
-
# Return a String representing the object invoking this method in the
|
45
|
-
#
|
49
|
+
# Return a String representing the object invoking this method in the form expected by our dataflow events.
|
50
|
+
# After implementing the LRU Cache, we firstly need to check if already had that object cached and if we have
|
51
|
+
# it - we can return it directly; otherwise we'll calculate and store the result before returning.
|
52
|
+
#
|
53
|
+
# TODO: RUBY-1327
|
54
|
+
# Once we move to 2.7+, we can combine the caches using ID b/c the memory location stops being the id
|
46
55
|
#
|
47
56
|
# @param object [Object, nil] the entity to convert to a String
|
48
57
|
# @return [String] the human readable form of the String, as defined by
|
49
58
|
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/vulnerability/capture-snapshot.md
|
50
59
|
def to_contrast_string object
|
51
|
-
# Only treat object like a string if it actually is a string
|
52
|
-
#
|
60
|
+
# Only treat object like a string if it actually is a string+ some subclasses of String override string
|
61
|
+
# methods we depend on
|
53
62
|
if object.cs__class == String
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
object.dup
|
58
|
-
elsif object.nil?
|
59
|
-
Contrast::Utils::ObjectShare::NIL_STRING
|
60
|
-
elsif object.cs__is_a?(Symbol)
|
61
|
-
":#{ object }"
|
62
|
-
elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
|
63
|
-
"#{ object.cs__name }@#{ object.__id__ }"
|
64
|
-
elsif object.cs__is_a?(Regexp)
|
65
|
-
object.source
|
66
|
-
elsif use_to_s?(object)
|
67
|
-
object.to_s
|
63
|
+
return @string_cache[object] if @string_cache.key? object
|
64
|
+
|
65
|
+
@string_cache[object] = to_cached_string(object) || object.dup
|
68
66
|
else
|
69
|
-
|
67
|
+
return @lru_cache[object.__id__] if @lru_cache.key? object.__id__
|
68
|
+
|
69
|
+
@lru_cache[object.__id__] = if object.nil?
|
70
|
+
Contrast::Utils::ObjectShare::NIL_STRING
|
71
|
+
elsif object.cs__is_a?(Symbol)
|
72
|
+
":#{ object }"
|
73
|
+
elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
|
74
|
+
"#{ object.cs__name }@#{ object.__id__ }"
|
75
|
+
elsif object.cs__is_a?(Regexp)
|
76
|
+
object.source
|
77
|
+
elsif use_to_s?(object)
|
78
|
+
object.to_s
|
79
|
+
else
|
80
|
+
"#{ object.cs__class.cs__name }@#{ object.__id__ }"
|
81
|
+
end
|
70
82
|
end
|
71
83
|
end
|
72
84
|
|
73
|
-
# The method const_defined? can cause autoload, which is bad for us.
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# been truly truly defined, meaning it existed before this method was
|
77
|
-
# invoked, not as a result of it.
|
85
|
+
# The method const_defined? can cause autoload, which is bad for us. The method autoload? doesn't traverse
|
86
|
+
# namespaces. This method lets us provide a constant, as a String, and parse it to determine if it has been
|
87
|
+
# truly truly defined, meaning it existed before this method was invoked, not as a result of it.
|
78
88
|
#
|
79
|
-
#
|
80
|
-
# support for 2.6.X, we should remove
|
81
|
-
# https://bugs.ruby-lang.org/issues/10741
|
89
|
+
# TODO: RUBY-1326
|
90
|
+
# This is required to handle a bug in Ruby prior to 2.7.0. When we drop support for 2.6.X, we should remove
|
91
|
+
# this code. https://bugs.ruby-lang.org/issues/10741
|
82
92
|
# @param name [String] the name of the constant to look up
|
83
93
|
# @return [Boolean]
|
84
94
|
def truly_defined? name
|
@@ -101,7 +111,8 @@ module Contrast
|
|
101
111
|
private
|
102
112
|
|
103
113
|
# Some objects have nice to_s that we can use to make them human readable. If they do, we should leverage them.
|
104
|
-
# We used to do this by default, but this opened us up to danger, so we're instead using an allow list
|
114
|
+
# We used to do this by default, but this opened us up to danger, so we're instead using an allow list
|
115
|
+
# approach.
|
105
116
|
#
|
106
117
|
# @param object [Object] something that may have a safe to_s method
|
107
118
|
# @return [Boolean] if we should invoke to_s to represent the object
|
@@ -112,6 +123,11 @@ module Contrast
|
|
112
123
|
false
|
113
124
|
end
|
114
125
|
|
126
|
+
# Find the target class based on the instance, or module, provided. If a module, return it.
|
127
|
+
#
|
128
|
+
# @param mod [Module] the Module, or instance of a Module, that we need to check
|
129
|
+
# @param is_instance [Boolean] is the object provided an instance of a class, requiring lookup by class
|
130
|
+
# @return [Module]
|
115
131
|
def determine_target_class mod, is_instance
|
116
132
|
return mod if mod.singleton_class?
|
117
133
|
|
@@ -120,13 +136,11 @@ module Contrast
|
|
120
136
|
mod
|
121
137
|
end
|
122
138
|
|
123
|
-
# If the String matches a common String in our ObjectShare, return that
|
124
|
-
#
|
125
|
-
# forcing a duplication of the String.
|
139
|
+
# If the String matches a common String in our ObjectShare, return that rather that for use as the
|
140
|
+
# representation of the String rather than forcing a duplication of the String.
|
126
141
|
#
|
127
|
-
# @param string [String] some string of which we want a Contrast
|
128
|
-
#
|
129
|
-
# @return [String,nil] the ObjectShare version of the String or nil
|
142
|
+
# @param string [String] some string of which we want a Contrast representation.
|
143
|
+
# @return [String, nil] the ObjectShare version of the String or nil
|
130
144
|
def to_cached_string string
|
131
145
|
return Contrast::Utils::ObjectShare::EMPTY_STRING if string.empty?
|
132
146
|
return Contrast::Utils::ObjectShare::SLASH if string == Contrast::Utils::ObjectShare::SLASH
|
@@ -0,0 +1,20 @@
|
|
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
|
+
module Contrast
|
5
|
+
module Utils
|
6
|
+
# Determine if configuration keys is excluded from logging
|
7
|
+
module ExcludeKey
|
8
|
+
EXCLUDE_FROM_LOG = %w[api api_key url service_key user_name].cs__freeze
|
9
|
+
class << self
|
10
|
+
# Check if a config key can be logged or not
|
11
|
+
#
|
12
|
+
# @param key [String] key to check
|
13
|
+
# @return[Boolean] true | false
|
14
|
+
def excludable? key
|
15
|
+
EXCLUDE_FROM_LOG.any? { |exclude_key| key.include? exclude_key }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -6,43 +6,51 @@ require 'contrast/components/logger'
|
|
6
6
|
module Contrast
|
7
7
|
module Utils
|
8
8
|
# Util for information about an IO
|
9
|
-
|
9
|
+
module IOUtil
|
10
10
|
extend Contrast::Components::Logger::InstanceMethods
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
|
12
|
+
class << self
|
13
|
+
# We're only going to call rewind on things that we believe are safe to
|
14
|
+
# call it on. This method white lists those cases and returns false in
|
15
|
+
# all others.
|
16
|
+
def should_rewind? potential_io
|
17
|
+
return true if potential_io.is_a?(StringIO)
|
18
|
+
return false unless io?(potential_io)
|
19
|
+
|
20
|
+
should_rewind_io?(potential_io)
|
21
|
+
rescue StandardError => e
|
22
|
+
logger.debug('Encountered an issue determining if rewindable', e, module: potential_io.cs__class.cs__name)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
# A class is IO if it is a decedent or delegate of IO
|
27
|
+
def io? object
|
28
|
+
return true if object.is_a?(IO)
|
29
|
+
|
30
|
+
# DelegateClass, which is a Delegator, defines __getobj__ as a way to
|
31
|
+
# get the object that the class wraps around (delegates to)
|
32
|
+
return false unless object.is_a?(Delegator)
|
33
|
+
|
34
|
+
object.__getobj__.is_a?(IO)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# IO rewind cannot be used with streams such as pipes, ttys, and sockets or for ones which have been closed.
|
40
|
+
#
|
41
|
+
# @param io [IO] the input to check for the ability to rewind
|
42
|
+
# @return [Boolean] if the given IO can be rewound
|
43
|
+
def should_rewind_io? io
|
44
|
+
return false if io.closed?
|
45
|
+
return false if io.tty?
|
46
|
+
|
47
|
+
status = io.stat
|
48
|
+
return false unless status
|
49
|
+
return false if status.pipe?
|
50
|
+
return false if status.socket?
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
@@ -0,0 +1,45 @@
|
|
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
|
+
module Contrast
|
5
|
+
module Utils
|
6
|
+
# A LRU(Least Recently Used) Cache store.
|
7
|
+
class LRUCache
|
8
|
+
def initialize capacity = 500
|
9
|
+
raise StandardError 'Capacity must be bigger than 0' if capacity <= 0
|
10
|
+
|
11
|
+
@capacity = capacity
|
12
|
+
@cache = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def [] key
|
16
|
+
val = @cache.delete(key)
|
17
|
+
@cache[key] = val if val
|
18
|
+
val
|
19
|
+
end
|
20
|
+
|
21
|
+
def []= key, value
|
22
|
+
@cache.delete(key)
|
23
|
+
@cache[key] = value
|
24
|
+
@cache.shift if @cache.size > @capacity
|
25
|
+
value # rubocop:disable Lint/Void
|
26
|
+
end
|
27
|
+
|
28
|
+
def keys
|
29
|
+
@cache.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def key? key
|
33
|
+
@cache.key?(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def values
|
37
|
+
@cache.values
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear
|
41
|
+
@cache.clear
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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 Utils
|
8
|
+
# This is the MetricsHash, which will take data type, so we now what is introduced/included
|
9
|
+
# in the TelemetryEvent
|
10
|
+
class MetricsHash < Hash
|
11
|
+
include Contrast::Components::Logger::InstanceMethods
|
12
|
+
|
13
|
+
attr_reader :data_type
|
14
|
+
|
15
|
+
ERROR_MESSAGES = [
|
16
|
+
'The key is not string or does not meet the requirements.',
|
17
|
+
'The key extends the allowed length.',
|
18
|
+
'VThe provided value is not the right data type'
|
19
|
+
].cs__freeze
|
20
|
+
KEY_REGEXP = /[a-zA-Z0-9_-]{1,63}/.cs__freeze
|
21
|
+
|
22
|
+
def initialize data_type, *several_variants
|
23
|
+
super
|
24
|
+
@data_type = data_type
|
25
|
+
end
|
26
|
+
|
27
|
+
def []= key, value
|
28
|
+
key_val = key.dup
|
29
|
+
value_val = value.dup
|
30
|
+
key_val.strip! if key_val.cs__is_a?(String)
|
31
|
+
value_val.strip! if value_val.cs__is_a?(String)
|
32
|
+
return unless valid_pair? key_val, value_val
|
33
|
+
|
34
|
+
key_val.downcase!
|
35
|
+
key_val.strip!
|
36
|
+
super(key_val, value_val)
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid_pair? key, value
|
40
|
+
if !key.cs__is_a?(String) || (KEY_REGEXP =~ key).nil?
|
41
|
+
logger.warn('The following key will be omitted', key: key, error: ERROR_MESSAGES[0])
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
unless key.length <= 28
|
45
|
+
logger.warn('The following key will be omitted', key: key, error: ERROR_MESSAGES[1])
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
unless value.cs__is_a?(data_type)
|
49
|
+
logger.warn('The following key will be omitted', value: value, error: ERROR_MESSAGES[2])
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
return false if value.cs__is_a?(String) && value.empty?
|
53
|
+
return false if value.cs__is_a?(String) && value.length > 200
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/contrast/utils/os.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'contrast/components/scope'
|
5
|
+
require 'cs__os_information/cs__os_information'
|
5
6
|
|
6
7
|
module Contrast
|
7
8
|
module Utils
|
@@ -31,6 +32,28 @@ module Contrast
|
|
31
32
|
zombie_pid_list.split("\n")
|
32
33
|
end
|
33
34
|
end
|
35
|
+
|
36
|
+
# Check current OS type
|
37
|
+
# returns true if check is correct or false if not
|
38
|
+
def windows?
|
39
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def mac?
|
43
|
+
RUBY_PLATFORM.include? 'darwin'
|
44
|
+
end
|
45
|
+
|
46
|
+
def unix?
|
47
|
+
!windows?
|
48
|
+
end
|
49
|
+
|
50
|
+
def linux?
|
51
|
+
unix? and !mac?
|
52
|
+
end
|
53
|
+
|
54
|
+
def jruby?
|
55
|
+
RUBY_ENGINE == 'jruby'
|
56
|
+
end
|
34
57
|
end
|
35
58
|
end
|
36
59
|
end
|