contrast-agent 4.3.2 → 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/.simplecov +1 -1
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/Rakefile +2 -3
- data/exe/contrast_service +1 -1
- data/ext/build_funchook.rb +4 -4
- data/ext/cs__assess_active_record_named/cs__active_record_named.c +1 -1
- data/ext/cs__assess_active_record_named/extconf.rb +1 -1
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_array/extconf.rb +1 -1
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +1 -1
- data/ext/cs__assess_basic_object/extconf.rb +1 -1
- data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +1 -1
- data/ext/cs__assess_fiber_track/extconf.rb +1 -1
- data/ext/cs__assess_hash/cs__assess_hash.c +4 -2
- data/ext/cs__assess_hash/extconf.rb +1 -1
- data/ext/cs__assess_kernel/cs__assess_kernel.c +1 -1
- data/ext/cs__assess_kernel/extconf.rb +1 -1
- data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +1 -1
- data/ext/cs__assess_marshal_module/extconf.rb +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +1 -1
- data/ext/cs__assess_module/extconf.rb +1 -1
- data/ext/cs__assess_regexp/cs__assess_regexp.c +1 -1
- data/ext/cs__assess_regexp/extconf.rb +1 -1
- data/ext/cs__assess_string/cs__assess_string.c +1 -1
- data/ext/cs__assess_string/extconf.rb +1 -1
- data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +1 -1
- data/ext/cs__assess_string_interpolation26/extconf.rb +1 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +1 -1
- data/ext/cs__assess_yield_track/extconf.rb +1 -1
- data/ext/cs__common/cs__common.c +5 -5
- data/ext/cs__common/cs__common.h +4 -4
- data/ext/cs__common/extconf.rb +1 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +22 -25
- data/ext/cs__contrast_patch/extconf.rb +1 -1
- data/ext/cs__protect_kernel/cs__protect_kernel.c +1 -1
- data/ext/cs__protect_kernel/extconf.rb +1 -1
- data/ext/extconf_common.rb +2 -6
- data/lib/contrast-agent.rb +1 -1
- data/lib/contrast.rb +20 -1
- data/lib/contrast/agent.rb +6 -4
- data/lib/contrast/agent/assess.rb +2 -11
- data/lib/contrast/agent/assess/contrast_event.rb +54 -71
- data/lib/contrast/agent/assess/contrast_object.rb +7 -4
- data/lib/contrast/agent/assess/events/event_factory.rb +3 -2
- data/lib/contrast/agent/assess/events/source_event.rb +7 -2
- data/lib/contrast/agent/assess/finalizers/freeze.rb +1 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +33 -34
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +34 -16
- data/lib/contrast/agent/assess/policy/patcher.rb +11 -18
- data/lib/contrast/agent/assess/policy/policy.rb +1 -1
- data/lib/contrast/agent/assess/policy/policy_node.rb +26 -34
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagation_method.rb +32 -30
- data/lib/contrast/agent/assess/policy/propagation_node.rb +20 -9
- data/lib/contrast/agent/assess/policy/propagator.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/append.rb +29 -14
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/center.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +22 -17
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
- data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +23 -19
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -13
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +24 -14
- data/lib/contrast/agent/assess/policy/propagator/split.rb +18 -15
- data/lib/contrast/agent/assess/policy/propagator/substitution.rb +32 -22
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +64 -45
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +7 -4
- data/lib/contrast/agent/assess/policy/source_method.rb +92 -81
- data/lib/contrast/agent/assess/policy/source_node.rb +1 -1
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +8 -6
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +2 -4
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -3
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +7 -8
- data/lib/contrast/agent/assess/policy/trigger_method.rb +109 -76
- data/lib/contrast/agent/assess/policy/trigger_node.rb +33 -11
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +60 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +3 -5
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +7 -5
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +4 -13
- data/lib/contrast/agent/assess/properties.rb +1 -3
- data/lib/contrast/agent/assess/property/evented.rb +9 -6
- data/lib/contrast/agent/assess/property/tagged.rb +38 -20
- data/lib/contrast/agent/assess/property/updated.rb +1 -1
- data/lib/contrast/agent/assess/rule/provider.rb +1 -1
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +12 -6
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +5 -2
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +4 -6
- data/lib/contrast/agent/assess/tag.rb +1 -1
- data/lib/contrast/agent/assess/tracker.rb +2 -2
- data/lib/contrast/agent/at_exit_hook.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +4 -2
- data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +1 -1
- data/lib/contrast/agent/deadzone/policy/policy.rb +7 -3
- data/lib/contrast/agent/disable_reaction.rb +2 -4
- data/lib/contrast/agent/exclusion_matcher.rb +6 -12
- data/lib/contrast/agent/inventory.rb +1 -2
- data/lib/contrast/agent/inventory/dependencies.rb +3 -1
- data/lib/contrast/agent/inventory/dependency_analysis.rb +1 -1
- data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +35 -23
- data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/inventory/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/middleware.rb +111 -110
- data/lib/contrast/agent/module_data.rb +4 -4
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +1 -1
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +9 -4
- data/lib/contrast/agent/patching/policy/method_policy.rb +7 -3
- data/lib/contrast/agent/patching/policy/module_policy.rb +15 -8
- data/lib/contrast/agent/patching/policy/patch.rb +23 -29
- data/lib/contrast/agent/patching/policy/patch_status.rb +8 -9
- data/lib/contrast/agent/patching/policy/patcher.rb +72 -64
- data/lib/contrast/agent/patching/policy/policy.rb +14 -21
- data/lib/contrast/agent/patching/policy/policy_node.rb +15 -5
- data/lib/contrast/agent/patching/policy/trigger_node.rb +26 -10
- data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +3 -4
- data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +6 -10
- data/lib/contrast/agent/protect/policy/policy.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +6 -6
- data/lib/contrast/agent/protect/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/protect/rule.rb +1 -1
- data/lib/contrast/agent/protect/rule/base.rb +19 -33
- data/lib/contrast/agent/protect/rule/base_service.rb +10 -6
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +15 -19
- data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +7 -14
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +4 -15
- data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -3
- data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +2 -4
- data/lib/contrast/agent/protect/rule/path_traversal.rb +6 -6
- data/lib/contrast/agent/protect/rule/sqli.rb +19 -13
- data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +1 -1
- data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +1 -1
- data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +2 -2
- data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +1 -1
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +2 -2
- data/lib/contrast/agent/protect/rule/xss.rb +2 -2
- data/lib/contrast/agent/protect/rule/xxe.rb +6 -13
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +2 -3
- data/lib/contrast/agent/railtie.rb +1 -1
- data/lib/contrast/agent/reaction_processor.rb +12 -11
- data/lib/contrast/agent/request.rb +25 -24
- data/lib/contrast/agent/request_context.rb +25 -23
- data/lib/contrast/agent/request_handler.rb +1 -1
- data/lib/contrast/agent/response.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +6 -4
- data/lib/contrast/agent/rule_set.rb +3 -3
- data/lib/contrast/agent/scope.rb +1 -1
- data/lib/contrast/agent/service_heartbeat.rb +3 -4
- data/lib/contrast/agent/static_analysis.rb +1 -1
- data/lib/contrast/agent/thread.rb +2 -2
- data/lib/contrast/agent/thread_watcher.rb +21 -6
- data/lib/contrast/agent/tracepoint_hook.rb +2 -2
- data/lib/contrast/agent/version.rb +2 -2
- data/lib/contrast/agent/worker_thread.rb +1 -1
- data/lib/contrast/api.rb +1 -1
- data/lib/contrast/api/communication.rb +1 -1
- data/lib/contrast/api/communication/connection_status.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +19 -22
- data/lib/contrast/api/communication/response_processor.rb +13 -8
- data/lib/contrast/api/communication/service_lifecycle.rb +5 -3
- data/lib/contrast/api/communication/socket.rb +1 -1
- data/lib/contrast/api/communication/socket_client.rb +30 -35
- data/lib/contrast/api/communication/speedracer.rb +6 -10
- data/lib/contrast/api/communication/tcp_socket.rb +1 -1
- data/lib/contrast/api/communication/unix_socket.rb +1 -1
- data/lib/contrast/api/decorators.rb +3 -1
- data/lib/contrast/api/decorators/address.rb +1 -1
- data/lib/contrast/api/decorators/agent_startup.rb +58 -0
- data/lib/contrast/api/decorators/application_settings.rb +1 -1
- data/lib/contrast/api/decorators/application_startup.rb +57 -0
- data/lib/contrast/api/decorators/application_update.rb +1 -1
- data/lib/contrast/api/decorators/http_request.rb +1 -1
- data/lib/contrast/api/decorators/input_analysis.rb +1 -1
- data/lib/contrast/api/decorators/instrumentation_mode.rb +37 -0
- data/lib/contrast/api/decorators/library.rb +9 -7
- data/lib/contrast/api/decorators/library_usage_update.rb +1 -1
- data/lib/contrast/api/decorators/message.rb +4 -4
- data/lib/contrast/api/decorators/rasp_rule_sample.rb +1 -1
- data/lib/contrast/api/decorators/route_coverage.rb +16 -6
- data/lib/contrast/api/decorators/server_features.rb +1 -1
- data/lib/contrast/api/decorators/trace_event.rb +46 -16
- data/lib/contrast/api/decorators/trace_event_object.rb +2 -4
- data/lib/contrast/api/decorators/trace_event_signature.rb +1 -1
- data/lib/contrast/api/decorators/trace_taint_range.rb +1 -1
- data/lib/contrast/api/decorators/trace_taint_range_tags.rb +2 -7
- data/lib/contrast/api/decorators/user_input.rb +1 -1
- data/lib/contrast/components/agent.rb +16 -15
- data/lib/contrast/components/app_context.rb +11 -29
- data/lib/contrast/components/assess.rb +6 -11
- data/lib/contrast/components/config.rb +3 -2
- data/lib/contrast/components/contrast_service.rb +8 -9
- data/lib/contrast/components/heap_dump.rb +1 -1
- data/lib/contrast/components/interface.rb +4 -3
- data/lib/contrast/components/inventory.rb +1 -1
- data/lib/contrast/components/logger.rb +1 -1
- data/lib/contrast/components/protect.rb +11 -14
- data/lib/contrast/components/sampling.rb +55 -7
- data/lib/contrast/components/scope.rb +2 -1
- data/lib/contrast/components/settings.rb +29 -99
- data/lib/contrast/config.rb +1 -1
- data/lib/contrast/config/agent_configuration.rb +1 -1
- data/lib/contrast/config/application_configuration.rb +1 -1
- data/lib/contrast/config/assess_configuration.rb +1 -1
- data/lib/contrast/config/assess_rules_configuration.rb +2 -4
- data/lib/contrast/config/base_configuration.rb +5 -6
- data/lib/contrast/config/default_value.rb +1 -1
- data/lib/contrast/config/exception_configuration.rb +2 -6
- data/lib/contrast/config/heap_dump_configuration.rb +13 -7
- data/lib/contrast/config/inventory_configuration.rb +1 -1
- data/lib/contrast/config/logger_configuration.rb +2 -6
- data/lib/contrast/config/protect_configuration.rb +1 -1
- data/lib/contrast/config/protect_rule_configuration.rb +23 -1
- data/lib/contrast/config/protect_rules_configuration.rb +1 -1
- data/lib/contrast/config/root_configuration.rb +1 -1
- data/lib/contrast/config/ruby_configuration.rb +1 -1
- data/lib/contrast/config/sampling_configuration.rb +1 -1
- data/lib/contrast/config/server_configuration.rb +1 -1
- data/lib/contrast/config/service_configuration.rb +1 -1
- data/lib/contrast/configuration.rb +4 -15
- data/lib/contrast/delegators/input_analysis.rb +12 -0
- data/lib/contrast/extension/assess.rb +1 -1
- data/lib/contrast/extension/assess/array.rb +2 -7
- data/lib/contrast/extension/assess/erb.rb +2 -8
- data/lib/contrast/extension/assess/eval_trigger.rb +3 -11
- data/lib/contrast/extension/assess/exec_trigger.rb +4 -14
- data/lib/contrast/extension/assess/fiber.rb +3 -13
- data/lib/contrast/extension/assess/hash.rb +1 -1
- data/lib/contrast/extension/assess/kernel.rb +3 -10
- data/lib/contrast/extension/assess/marshal.rb +3 -11
- data/lib/contrast/extension/assess/regexp.rb +2 -7
- data/lib/contrast/extension/assess/string.rb +4 -2
- data/lib/contrast/extension/delegator.rb +1 -1
- data/lib/contrast/extension/inventory.rb +1 -1
- data/lib/contrast/extension/kernel.rb +5 -3
- data/lib/contrast/extension/module.rb +1 -1
- data/lib/contrast/extension/protect.rb +1 -1
- data/lib/contrast/extension/protect/kernel.rb +1 -1
- data/lib/contrast/extension/protect/psych.rb +1 -1
- data/lib/contrast/extension/thread.rb +1 -1
- data/lib/contrast/framework/base_support.rb +1 -1
- data/lib/contrast/framework/manager.rb +14 -17
- data/lib/contrast/framework/platform_version.rb +1 -1
- data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -19
- data/lib/contrast/framework/rack/patch/support.rb +7 -5
- data/lib/contrast/framework/rack/support.rb +1 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +8 -3
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +4 -4
- data/lib/contrast/framework/rails/patch/support.rb +5 -3
- data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +5 -2
- data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +3 -1
- data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -1
- data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +3 -1
- data/lib/contrast/framework/rails/support.rb +45 -46
- data/lib/contrast/framework/sinatra/support.rb +103 -42
- data/lib/contrast/funchook/funchook.rb +2 -6
- data/lib/contrast/logger/application.rb +13 -10
- data/lib/contrast/logger/format.rb +3 -6
- data/lib/contrast/logger/log.rb +36 -19
- data/lib/contrast/logger/request.rb +2 -3
- data/lib/contrast/logger/time.rb +1 -1
- data/lib/contrast/security_exception.rb +2 -2
- data/lib/contrast/tasks/config.rb +1 -1
- data/lib/contrast/tasks/service.rb +6 -2
- data/lib/contrast/utils/assess/sampling_util.rb +1 -1
- data/lib/contrast/utils/assess/tracking_util.rb +2 -3
- data/lib/contrast/utils/class_util.rb +18 -12
- data/lib/contrast/utils/duck_utils.rb +1 -1
- data/lib/contrast/utils/env_configuration_item.rb +1 -1
- data/lib/contrast/utils/hash_digest.rb +16 -24
- data/lib/contrast/utils/heap_dump_util.rb +104 -88
- data/lib/contrast/utils/invalid_configuration_util.rb +22 -13
- data/lib/contrast/utils/inventory_util.rb +1 -1
- data/lib/contrast/utils/io_util.rb +2 -2
- data/lib/contrast/utils/job_servers_running.rb +10 -5
- data/lib/contrast/utils/object_share.rb +1 -1
- data/lib/contrast/utils/os.rb +3 -2
- data/lib/contrast/utils/preflight_util.rb +1 -1
- data/lib/contrast/utils/resource_loader.rb +1 -1
- data/lib/contrast/utils/ruby_ast_rewriter.rb +3 -2
- data/lib/contrast/utils/sha256_builder.rb +1 -1
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/lib/contrast/utils/string_utils.rb +1 -1
- data/lib/contrast/utils/tag_util.rb +1 -1
- data/lib/contrast/utils/thread_tracker.rb +1 -1
- data/lib/contrast/utils/timer.rb +1 -1
- data/resources/assess/policy.json +8 -11
- data/resources/deadzone/policy.json +7 -17
- data/ruby-agent.gemspec +66 -27
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- data/sonar-project.properties +9 -0
- metadata +154 -156
- data/lib/contrast/agent/assess/rule.rb +0 -18
- data/lib/contrast/agent/assess/rule/base.rb +0 -52
- data/lib/contrast/agent/assess/rule/redos.rb +0 -67
- data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +0 -38
- data/lib/contrast/common_agent_configuration.rb +0 -87
- data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
- data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
- data/lib/contrast/utils/prevent_serialization.rb +0 -52
|
@@ -0,0 +1,60 @@
|
|
|
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 Agent
|
|
6
|
+
module Assess
|
|
7
|
+
module Policy
|
|
8
|
+
module TriggerValidation
|
|
9
|
+
# Validator used to assert a REDOS finding is actually vulnerable
|
|
10
|
+
# before serializing that finding as a DTM to report to the service.
|
|
11
|
+
module REDOSValidator
|
|
12
|
+
RULE_NAME = 'redos'
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
def valid? _patcher, object, _ret, args
|
|
16
|
+
# Can arrive here from either:
|
|
17
|
+
# regexp =~ string
|
|
18
|
+
# string =~ regexp
|
|
19
|
+
# regexp.match string
|
|
20
|
+
#
|
|
21
|
+
# Thus object/args[0] can be string/regexp or regexp/string.
|
|
22
|
+
regexp = object.is_a?(Regexp) ? object : args[0]
|
|
23
|
+
|
|
24
|
+
# regexp must be exploitable.
|
|
25
|
+
return false unless regexp_vulnerable?(regexp)
|
|
26
|
+
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected
|
|
31
|
+
|
|
32
|
+
VULNERABLE_PATTERN = /[\[(].*?[\[(].*?[\])][*+?].*?[\])][*+?]/.cs__freeze
|
|
33
|
+
|
|
34
|
+
# Does the regexp
|
|
35
|
+
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/redos.md
|
|
36
|
+
def regexp_vulnerable? regexp
|
|
37
|
+
# A pattern is considered vulnerable if it has 2 or more levels of nested multi-matching.
|
|
38
|
+
# A level is defined as any set of opening and closing control characters immediately followed by a
|
|
39
|
+
# multi match control character.
|
|
40
|
+
# A control character is defined as one of the OPENING_CHARS, CLOSING_CHARS, or MULTI_MATCH_CHARS that
|
|
41
|
+
# is not immediately preceded by an escaping \ character.
|
|
42
|
+
# OPENING_CHARS are ( and [ CLOSING_CHARS are ) and ] MULTI_MATCH_CHARS are +, *, and ?
|
|
43
|
+
|
|
44
|
+
# Nota bene about Regexp#to_s: it doesn't necessarily give you the original Regexp back
|
|
45
|
+
# (in the sense of `my_str == Regexp.new(my_str).to_s`), it gives you a Regexp that
|
|
46
|
+
# will have the same functional characteristics as the original.
|
|
47
|
+
# Regexp#inspect gives you a "more nicely formatted" version than #to_s.
|
|
48
|
+
# Regexp#source will give you the original source.
|
|
49
|
+
|
|
50
|
+
# Use #match? because it doesn't fill out global variables
|
|
51
|
+
# in the way match or =~ do.
|
|
52
|
+
VULNERABLE_PATTERN.match? regexp.source
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -9,9 +9,8 @@ module Contrast
|
|
|
9
9
|
# Validator used to assert a SSRF finding is actually vulnerable
|
|
10
10
|
# before serializing that finding as a DTM to report to the service.
|
|
11
11
|
module SSRFValidator
|
|
12
|
-
|
|
13
|
-
URL_PATTERN =
|
|
14
|
-
%r{(?<protocol>http|https|ftp|sftp|telnet|gopher|rtsp|rtsps|ssh|svn)://(?<host>[^/?]+)(?<path>/?[^?]*)(?<query_string>\?.*)?}i.cs__freeze
|
|
12
|
+
RULE_NAME = 'ssrf'
|
|
13
|
+
URL_PATTERN = %r{(?<protocol>http|https|ftp|sftp|telnet|gopher|rtsp|rtsps|ssh|svn)://(?<host>[^/?]+)(?<path>/?[^?]*)(?<query_string>\?.*)?}i.cs__freeze # rubocop:disable Layout/LineLength
|
|
15
14
|
# The Net::HTTP class validates host format on instantiation. Since
|
|
16
15
|
# our triggers for that class are on the instance, they already
|
|
17
16
|
# have this validation done for them. We do not need to apply the
|
|
@@ -23,7 +22,6 @@ module Contrast
|
|
|
23
22
|
# querystring
|
|
24
23
|
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/server_side_request_forgery.md
|
|
25
24
|
def self.valid? patcher, _object, _ret, args
|
|
26
|
-
return true unless SSRF_RULE == patcher&.rule_id
|
|
27
25
|
return true if patcher.id.to_s.start_with?(PATH_ONLY_PATCH_MARKER)
|
|
28
26
|
|
|
29
27
|
url = args[0].to_s
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/policy/trigger_validation/ssrf_validator'
|
|
5
5
|
require 'contrast/agent/assess/policy/trigger_validation/xss_validator'
|
|
6
|
+
require 'contrast/agent/assess/policy/trigger_validation/redos_validator'
|
|
6
7
|
|
|
7
8
|
module Contrast
|
|
8
9
|
module Agent
|
|
@@ -15,7 +16,8 @@ module Contrast
|
|
|
15
16
|
module TriggerValidation
|
|
16
17
|
VALIDATORS = [
|
|
17
18
|
Contrast::Agent::Assess::Policy::TriggerValidation::SSRFValidator,
|
|
18
|
-
Contrast::Agent::Assess::Policy::TriggerValidation::XSSValidator
|
|
19
|
+
Contrast::Agent::Assess::Policy::TriggerValidation::XSSValidator,
|
|
20
|
+
Contrast::Agent::Assess::Policy::TriggerValidation::REDOSValidator
|
|
19
21
|
].cs__freeze
|
|
20
22
|
|
|
21
23
|
# Determines if the conditions in which this trigger was called are
|
|
@@ -32,9 +34,9 @@ module Contrast
|
|
|
32
34
|
# @return [Boolean] if the conditions are valid for the generation of
|
|
33
35
|
# a Contrast::Api::Dtm::Finding
|
|
34
36
|
def self.valid? patcher, object, ret, args
|
|
35
|
-
VALIDATORS.
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
specific_validator = VALIDATORS.find { |validator| validator::RULE_NAME == patcher&.rule_id }
|
|
38
|
+
return specific_validator.valid?(patcher, object, ret, args) if specific_validator
|
|
39
|
+
|
|
38
40
|
true
|
|
39
41
|
end
|
|
40
42
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -10,22 +10,13 @@ module Contrast
|
|
|
10
10
|
# vulnerable before serializing that finding as a DTM to report to
|
|
11
11
|
# the service.
|
|
12
12
|
module XSSValidator
|
|
13
|
-
|
|
14
|
-
SAFE_CONTENT_TYPES = %w[
|
|
15
|
-
/csv
|
|
16
|
-
/javascript
|
|
17
|
-
/json
|
|
18
|
-
/pdf
|
|
19
|
-
/x-javascript
|
|
20
|
-
/x-json
|
|
21
|
-
].cs__freeze
|
|
13
|
+
RULE_NAME = 'reflected-xss'
|
|
14
|
+
SAFE_CONTENT_TYPES = %w[/csv /javascript /json /pdf /x-javascript /x-json].cs__freeze
|
|
22
15
|
|
|
23
16
|
# A finding is valid for XSS if the response type is not one of
|
|
24
17
|
# those assumed to be safe
|
|
25
18
|
# https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/reflected_xss.md
|
|
26
|
-
def self.valid?
|
|
27
|
-
return true unless XSS_RULE == patcher&.rule_id
|
|
28
|
-
|
|
19
|
+
def self.valid? _patcher, _object, _ret, _args
|
|
29
20
|
content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
|
|
30
21
|
return true unless content_type
|
|
31
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'base64'
|
|
@@ -6,7 +6,6 @@ require 'set'
|
|
|
6
6
|
require 'contrast/agent/assess/property/evented'
|
|
7
7
|
require 'contrast/agent/assess/property/tagged'
|
|
8
8
|
require 'contrast/agent/assess/property/updated'
|
|
9
|
-
require 'contrast/utils/prevent_serialization'
|
|
10
9
|
|
|
11
10
|
module Contrast
|
|
12
11
|
module Agent
|
|
@@ -18,7 +17,6 @@ module Contrast
|
|
|
18
17
|
# to properly convey the events that lead up to the state of the tracked
|
|
19
18
|
# user input.
|
|
20
19
|
class Properties
|
|
21
|
-
include Contrast::Utils::PreventSerialization
|
|
22
20
|
include Contrast::Agent::Assess::Property::Evented
|
|
23
21
|
include Contrast::Agent::Assess::Property::Tagged
|
|
24
22
|
include Contrast::Agent::Assess::Property::Updated
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/events/event_factory'
|
|
@@ -14,7 +14,7 @@ module Contrast
|
|
|
14
14
|
# @attr_reader event [Contrast::Agent::Assess::ContrastEvent] the
|
|
15
15
|
# latest event to track
|
|
16
16
|
module Evented
|
|
17
|
-
|
|
17
|
+
attr_reader :event
|
|
18
18
|
|
|
19
19
|
# Create a new event and add it to the event set.
|
|
20
20
|
#
|
|
@@ -31,7 +31,8 @@ module Contrast
|
|
|
31
31
|
# the key used to accessed if from a map or nil if a type like
|
|
32
32
|
# BODY
|
|
33
33
|
def build_event policy_node, tagged, object, ret, args, source_type = nil, source_name = nil
|
|
34
|
-
@event = Contrast::Agent::Assess::Events::EventFactory.build(policy_node, tagged, object, ret, args,
|
|
34
|
+
@event = Contrast::Agent::Assess::Events::EventFactory.build(policy_node, tagged, object, ret, args,
|
|
35
|
+
source_type, source_name)
|
|
35
36
|
report_sources(tagged, event)
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -46,10 +47,12 @@ module Contrast
|
|
|
46
47
|
return unless tagged && !tagged.to_s.empty?
|
|
47
48
|
return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
|
|
48
49
|
return unless event.source_type
|
|
50
|
+
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
|
51
|
+
|
|
52
|
+
if current_request.observed_route.sources.any? do |source|
|
|
53
|
+
source.type == event.forced_source_type && source.name == event.forced_source_name
|
|
54
|
+
end
|
|
49
55
|
|
|
50
|
-
current_request = Contrast::Agent::REQUEST_TRACKER.current
|
|
51
|
-
return unless current_request
|
|
52
|
-
if current_request.observed_route.sources.any? { |source| source.type == event.forced_source_type && source.name == event.forced_source_name }
|
|
53
56
|
return
|
|
54
57
|
end
|
|
55
58
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/tag'
|
|
@@ -91,28 +91,15 @@ module Contrast
|
|
|
91
91
|
|
|
92
92
|
at = Hash.new { |h, k| h[k] = [] }
|
|
93
93
|
tags.each_pair do |key, value|
|
|
94
|
-
add =
|
|
94
|
+
add = nil
|
|
95
95
|
value.each do |tag|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
when Contrast::Agent::Assess::Tag::LOW_SPAN
|
|
101
|
-
add << Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
102
|
-
# the tag exists in the requested range, figure out the boundaries
|
|
103
|
-
when Contrast::Agent::Assess::Tag::WITHIN
|
|
104
|
-
start = tag.start_idx - range.begin
|
|
105
|
-
finish = range.size - start
|
|
106
|
-
add << Contrast::Agent::Assess::Tag.new(tag.label, finish, start)
|
|
107
|
-
# the tag spans the requested range.
|
|
108
|
-
when Contrast::Agent::Assess::Tag::WITHOUT # rubocop:disable Lint/DuplicateBranch
|
|
109
|
-
add << Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
110
|
-
# part of the tag is being selected
|
|
111
|
-
when Contrast::Agent::Assess::Tag::HIGH_SPAN # rubocop:disable Lint/DuplicateBranch
|
|
112
|
-
add << Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
96
|
+
within_range = resize_to_range(tag, range)
|
|
97
|
+
if within_range
|
|
98
|
+
add ||= []
|
|
99
|
+
add << within_range
|
|
113
100
|
end
|
|
114
101
|
end
|
|
115
|
-
next
|
|
102
|
+
next unless add&.any?
|
|
116
103
|
|
|
117
104
|
at[key] = add
|
|
118
105
|
end
|
|
@@ -344,6 +331,37 @@ module Contrast
|
|
|
344
331
|
Contrast::Utils::TagUtil.ordered_merge(value, add)
|
|
345
332
|
end
|
|
346
333
|
end
|
|
334
|
+
|
|
335
|
+
private
|
|
336
|
+
|
|
337
|
+
# Given a tag, compare it to a given range and, if any part of that tag is within the range, return a new tag
|
|
338
|
+
# covering the union of the original tag and the range. This new tag will start at the
|
|
339
|
+
# max(tag.start, range.start) and end at min(tag.end, range.end)
|
|
340
|
+
#
|
|
341
|
+
# @param tag [Contrast::Agent::Assess::Tag] the Tag that may be in this range
|
|
342
|
+
# @param range [Range] the span to check, inclusive to exclusive
|
|
343
|
+
# @return [Contrast::Agent::Assess::Tag, nil] a new tag, truncated to only span within the given range or nil
|
|
344
|
+
# if no overlap exists
|
|
345
|
+
def resize_to_range tag, range
|
|
346
|
+
comparison = tag.compare_range(range.begin, range.end)
|
|
347
|
+
# BELOW and ABOVE are not applicable to this check and result in nil
|
|
348
|
+
case comparison
|
|
349
|
+
# part of the tag is being selected
|
|
350
|
+
when Contrast::Agent::Assess::Tag::LOW_SPAN
|
|
351
|
+
Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
352
|
+
# the tag exists in the requested range, figure out the boundaries
|
|
353
|
+
when Contrast::Agent::Assess::Tag::WITHIN
|
|
354
|
+
start = tag.start_idx - range.begin
|
|
355
|
+
finish = range.size - start
|
|
356
|
+
Contrast::Agent::Assess::Tag.new(tag.label, finish, start)
|
|
357
|
+
# the tag spans the requested range.
|
|
358
|
+
when Contrast::Agent::Assess::Tag::WITHOUT # rubocop:disable Lint/DuplicateBranch
|
|
359
|
+
Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
360
|
+
# part of the tag is being selected
|
|
361
|
+
when Contrast::Agent::Assess::Tag::HIGH_SPAN # rubocop:disable Lint/DuplicateBranch
|
|
362
|
+
Contrast::Agent::Assess::Tag.new(tag.label, range.size)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
347
365
|
end
|
|
348
366
|
end
|
|
349
367
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/utils/duck_utils'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -55,9 +55,12 @@ module Contrast
|
|
|
55
55
|
value_node.children.each do |child|
|
|
56
56
|
next unless child
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
unless child.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
|
|
59
|
+
child.type == :LIT &&
|
|
60
|
+
child.children[0]&.cs__is_a?(Integer)
|
|
61
|
+
|
|
62
|
+
return false
|
|
63
|
+
end
|
|
61
64
|
end
|
|
62
65
|
|
|
63
66
|
true
|
|
@@ -84,8 +87,11 @@ module Contrast
|
|
|
84
87
|
return false unless children.length >= 2
|
|
85
88
|
|
|
86
89
|
potential_string_node = children[0]
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
unless potential_string_node.cs__is_a?(RubyVM::AbstractSyntaxTree::Node) &&
|
|
91
|
+
potential_string_node.type == :STR
|
|
92
|
+
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
89
95
|
|
|
90
96
|
children[1] == :bytes
|
|
91
97
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -29,7 +29,10 @@ module Contrast
|
|
|
29
29
|
# These are markers whose presence indicates that a field is more
|
|
30
30
|
# likely to be a descriptor or requirement than an actual password.
|
|
31
31
|
# We should ignore fields that contain them.
|
|
32
|
-
NON_PASSWORD_PARTIAL_NAMES = %w[
|
|
32
|
+
NON_PASSWORD_PARTIAL_NAMES = %w[
|
|
33
|
+
DATE FORGOT FORM ENCODE PATTERN PREFIX PROP SUFFIX URL BASE FILE
|
|
34
|
+
URI
|
|
35
|
+
].cs__freeze
|
|
33
36
|
|
|
34
37
|
# If the constant looks like a password and it doesn't look like a
|
|
35
38
|
# password descriptor, it passes for this rule
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/policy/trigger_method'
|
|
@@ -26,10 +26,7 @@ module Contrast
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# TODO: RUBY-1014 - remove `#analyze`
|
|
29
|
-
COMMON_CONSTANTS = %i[
|
|
30
|
-
CONTRAST_ASSESS_POLICY_STATUS
|
|
31
|
-
VERSION
|
|
32
|
-
].cs__freeze
|
|
29
|
+
COMMON_CONSTANTS = %i[CONTRAST_ASSESS_POLICY_STATUS VERSION].cs__freeze
|
|
33
30
|
def analyze clazz
|
|
34
31
|
return if disabled?
|
|
35
32
|
|
|
@@ -171,7 +168,8 @@ module Contrast
|
|
|
171
168
|
|
|
172
169
|
finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name)
|
|
173
170
|
finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string)
|
|
174
|
-
finding.properties[CODE_SOURCE_KEY] =
|
|
171
|
+
finding.properties[CODE_SOURCE_KEY] =
|
|
172
|
+
Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker)
|
|
175
173
|
|
|
176
174
|
hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
|
|
177
175
|
finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Contrast
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/assess/finalizers/hash'
|
|
@@ -16,7 +16,7 @@ module Contrast
|
|
|
16
16
|
class << self
|
|
17
17
|
# Retrieve the properties of the given Object, iff they exist.
|
|
18
18
|
#
|
|
19
|
-
# @param source [Object] the thing for which to look up properties.
|
|
19
|
+
# @param source [Object, nil] the thing for which to look up properties.
|
|
20
20
|
# @return [Contrast::Agent::Assess::Properties, nil]
|
|
21
21
|
def properties source
|
|
22
22
|
PROPERTIES_HASH[source]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/components/interface'
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
|
|
5
|
+
|
|
4
6
|
require 'ripper'
|
|
5
7
|
require 'contrast/extension/module'
|
|
6
8
|
require 'contrast/components/interface'
|
|
@@ -48,7 +50,7 @@ module Contrast
|
|
|
48
50
|
:public_instance_methods, :protected_instance_methods, :private_instance_methods, :locations
|
|
49
51
|
|
|
50
52
|
def initialize module_data
|
|
51
|
-
@class_module_path = module_data.
|
|
53
|
+
@class_module_path = module_data.mod_name
|
|
52
54
|
clazz = module_data.mod
|
|
53
55
|
@is_class = clazz.is_a?(Class)
|
|
54
56
|
@public_instance_methods = []
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
1
|
+
# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'contrast/agent/patching/policy/policy_node'
|