contrast-agent 6.1.0 → 6.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +1 -1
- data/Rakefile +1 -1
- data/ext/build_funchook.rb +3 -3
- data/ext/cs__assess_basic_object/cs__assess_basic_object.c +5 -1
- data/ext/extconf_common.rb +1 -1
- data/lib/contrast/agent/assess/finalizers/hash.rb +2 -2
- data/lib/contrast/agent/assess/policy/policy.rb +9 -10
- data/lib/contrast/agent/assess/policy/policy_node.rb +9 -10
- data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -3
- data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -3
- data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/buffer.rb +2 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/split.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/trim.rb +1 -1
- data/lib/contrast/agent/assess/policy/source_node.rb +1 -1
- data/lib/contrast/agent/assess/policy/trigger_method.rb +7 -7
- data/lib/contrast/agent/assess/policy/trigger_node.rb +16 -16
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +1 -1
- data/lib/contrast/agent/assess/property/evented.rb +2 -2
- data/lib/contrast/agent/assess/property/tagged.rb +2 -2
- data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +6 -8
- data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +6 -7
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +5 -5
- data/lib/contrast/agent/assess/rule/response/base_rule.rb +2 -3
- data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +8 -9
- data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +6 -6
- data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +4 -4
- data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +3 -4
- data/lib/contrast/agent/assess/tag.rb +13 -14
- data/lib/contrast/agent/at_exit_hook.rb +12 -1
- data/lib/contrast/agent/middleware.rb +6 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -3
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +2 -2
- data/lib/contrast/agent/patching/policy/method_policy_extend.rb +4 -4
- data/lib/contrast/agent/patching/policy/patch.rb +9 -9
- data/lib/contrast/agent/patching/policy/patch_status.rb +10 -3
- data/lib/contrast/agent/patching/policy/policy.rb +13 -15
- data/lib/contrast/agent/patching/policy/policy_node.rb +19 -21
- data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
- data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +125 -125
- data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
- data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
- data/lib/contrast/agent/protect/policy/rule_applicator.rb +4 -4
- data/lib/contrast/agent/protect/rule/base.rb +30 -18
- data/lib/contrast/agent/protect/rule/base_service.rb +31 -14
- data/lib/contrast/agent/protect/rule/cmd_injection.rb +16 -9
- data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +3 -3
- data/lib/contrast/agent/protect/rule/default_scanner.rb +2 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +18 -7
- data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +74 -74
- data/lib/contrast/agent/protect/rule/http_method_tampering.rb +71 -53
- data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +3 -3
- data/lib/contrast/agent/protect/rule/no_sqli.rb +15 -16
- data/lib/contrast/agent/protect/rule/path_traversal.rb +13 -3
- data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +2 -2
- data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +1 -1
- data/lib/contrast/agent/protect/rule/sqli.rb +16 -23
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +61 -61
- data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +29 -29
- data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +32 -32
- data/lib/contrast/agent/protect/rule/xss.rb +17 -0
- data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +14 -13
- data/lib/contrast/agent/protect/rule/xxe.rb +25 -3
- data/lib/contrast/agent/reaction_processor.rb +1 -1
- data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +36 -36
- data/lib/contrast/agent/reporting/masker/masker.rb +10 -10
- data/lib/contrast/agent/reporting/masker/masker_utils.rb +2 -2
- data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +8 -10
- data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +53 -5
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +25 -19
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +129 -17
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +20 -21
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_stack.rb +22 -0
- data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +26 -12
- data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +5 -5
- data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +7 -5
- data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +4 -10
- data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +6 -6
- data/lib/contrast/agent/reporting/reporting_utilities/response.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +7 -7
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +15 -15
- data/lib/contrast/agent/reporting/settings/application_settings.rb +1 -1
- data/lib/contrast/agent/reporting/settings/assess.rb +5 -5
- data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +3 -3
- data/lib/contrast/agent/reporting/settings/exclusions.rb +3 -3
- data/lib/contrast/agent/reporting/settings/protect.rb +20 -5
- data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +5 -5
- data/lib/contrast/agent/reporting/settings/reaction.rb +3 -3
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +2 -2
- data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +2 -2
- data/lib/contrast/agent/reporting/settings/server_features.rb +2 -2
- data/lib/contrast/agent/request.rb +2 -2
- data/lib/contrast/agent/request_context.rb +23 -19
- data/lib/contrast/agent/request_context_extend.rb +10 -23
- data/lib/contrast/agent/request_handler.rb +1 -1
- data/lib/contrast/agent/rule_set.rb +2 -2
- data/lib/contrast/agent/scope.rb +1 -1
- data/lib/contrast/agent/telemetry/base.rb +9 -5
- data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +119 -0
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -2
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +1 -1
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +1 -1
- data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +16 -18
- data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +2 -2
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api/communication/messaging_queue.rb +1 -1
- data/lib/contrast/api/communication/service_lifecycle.rb +1 -1
- data/lib/contrast/api/communication/socket.rb +1 -1
- data/lib/contrast/api/communication/socket_client.rb +1 -1
- data/lib/contrast/api/communication/speedracer.rb +2 -2
- data/lib/contrast/api/decorators/agent_startup.rb +10 -9
- data/lib/contrast/api/decorators/application_settings.rb +1 -1
- data/lib/contrast/api/decorators/application_startup.rb +4 -4
- data/lib/contrast/api/decorators/response_type.rb +4 -17
- data/lib/contrast/components/agent.rb +1 -1
- data/lib/contrast/components/base.rb +1 -1
- data/lib/contrast/components/config.rb +6 -6
- data/lib/contrast/components/contrast_service.rb +4 -1
- data/lib/contrast/components/sampling.rb +1 -1
- data/lib/contrast/components/settings.rb +52 -28
- data/lib/contrast/config/assess_rules_configuration.rb +1 -1
- data/lib/contrast/config/protect_rules_configuration.rb +1 -1
- data/lib/contrast/config/root_configuration.rb +1 -1
- data/lib/contrast/configuration.rb +4 -4
- data/lib/contrast/extension/assess/array.rb +1 -1
- data/lib/contrast/extension/assess/erb.rb +1 -1
- data/lib/contrast/extension/assess/marshal.rb +1 -1
- data/lib/contrast/extension/assess/string.rb +1 -1
- data/lib/contrast/extension/extension.rb +2 -2
- data/lib/contrast/framework/base_support.rb +8 -8
- data/lib/contrast/framework/grape/support.rb +3 -3
- data/lib/contrast/framework/manager.rb +5 -5
- data/lib/contrast/framework/manager_extend.rb +1 -1
- data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
- data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -3
- data/lib/contrast/framework/rails/patch/assess_configuration.rb +3 -3
- data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
- data/lib/contrast/framework/rails/patch/support.rb +1 -1
- data/lib/contrast/framework/rails/support.rb +2 -2
- data/lib/contrast/framework/sinatra/support.rb +1 -1
- data/lib/contrast/logger/aliased_logging.rb +29 -22
- data/lib/contrast/logger/cef_log.rb +14 -14
- data/lib/contrast/logger/format.rb +1 -1
- data/lib/contrast/logger/log.rb +8 -8
- data/lib/contrast/tasks/config.rb +12 -12
- data/lib/contrast/tasks/service.rb +2 -2
- data/lib/contrast/utils/assess/tracking_util.rb +4 -4
- data/lib/contrast/utils/class_util.rb +4 -4
- data/lib/contrast/utils/findings.rb +3 -3
- data/lib/contrast/utils/hash_digest.rb +6 -7
- data/lib/contrast/utils/head_dump_utils_extend.rb +1 -1
- data/lib/contrast/utils/invalid_configuration_util.rb +1 -1
- data/lib/contrast/utils/log_utils.rb +4 -4
- data/lib/contrast/utils/lru_cache.rb +1 -1
- data/lib/contrast/utils/metrics_hash.rb +1 -1
- data/lib/contrast/utils/middleware_utils.rb +5 -5
- data/lib/contrast/utils/net_http_base.rb +4 -4
- data/lib/contrast/utils/os.rb +1 -1
- data/lib/contrast/utils/patching/policy/patch_utils.rb +2 -2
- data/lib/contrast/utils/request_utils.rb +2 -2
- data/lib/contrast/utils/sha256_builder.rb +4 -4
- data/lib/contrast/utils/stack_trace_utils.rb +31 -13
- data/lib/contrast/utils/telemetry.rb +6 -9
- data/lib/contrast/utils/telemetry_client.rb +5 -5
- data/lib/contrast/utils/telemetry_hash.rb +1 -1
- data/lib/contrast/utils/telemetry_identifier.rb +2 -2
- data/lib/contrast/utils/timer.rb +1 -1
- data/resources/assess/policy.json +1 -1
- metadata +15 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e548b3df856f888229a5125c351404c3f5b04fcd7ecb5c91937f480ce91c9d42
|
4
|
+
data.tar.gz: 00c6ee55996e75602d1b80da133a362d3205cb5ef5de0f5c1a35b214528fea2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad8d0f64dc798a22072e977cf0ff83ba47df54a3f24802386a52d2d338f365a02cac5db943868a55ecdf7641e44384f580e55e5d1b05a78522e88c5244013346
|
7
|
+
data.tar.gz: 804a464d38a9e023da53ce82009573571a6197c9f3f00642988457ac32b8ecc8631ea0a3cfd8bc678918df8afbb1cb24c51fa79dff1fe8fb278c5e966bff74e1
|
data/.simplecov
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
SimpleCov.minimum_coverage
|
4
|
+
SimpleCov.minimum_coverage(line: 94)
|
5
5
|
SimpleCov.start do
|
6
6
|
add_filter '/spec/'
|
7
7
|
enable_coverage :branch
|
data/Rakefile
CHANGED
data/ext/build_funchook.rb
CHANGED
@@ -52,13 +52,13 @@ unless find_header('funchook.h', ext_path)
|
|
52
52
|
|
53
53
|
TARGET_PATHS.each do |target_path|
|
54
54
|
unless File.writable?(target_path)
|
55
|
-
puts
|
55
|
+
puts("Unable to copy into #{ target_path } - directory not writable")
|
56
56
|
next
|
57
57
|
end
|
58
|
-
puts
|
58
|
+
puts("Copying #{ source_file_path } into #{ target_path }")
|
59
59
|
FileUtils.cp(source_file_path, target_path)
|
60
60
|
rescue StandardError
|
61
|
-
puts
|
61
|
+
puts("Error while copying #{ source_file } to #{ target_path }")
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -17,6 +17,10 @@
|
|
17
17
|
* }
|
18
18
|
*/
|
19
19
|
|
20
|
+
VALUE contrast_check_and_register_instance_patch(
|
21
|
+
const char *module_name, const char *method_name,
|
22
|
+
VALUE(c_fn)(const int, VALUE *, const VALUE));
|
23
|
+
|
20
24
|
void contrast_assess_instance_eval_trigger_check(VALUE self, VALUE source,
|
21
25
|
VALUE ret) {
|
22
26
|
rb_funcall(basic_eval_trigger, instance_trigger_check_method, 3, self,
|
@@ -61,6 +65,6 @@ void Init_cs__assess_basic_object(void) {
|
|
61
65
|
* but if someone else patched BasicObject#instance_eval,
|
62
66
|
* IDK if this is intentional... noting it. -ajm
|
63
67
|
*/
|
64
|
-
|
68
|
+
contrast_check_and_register_instance_patch("BasicObject", "instance_eval",
|
65
69
|
contrast_assess_basic_object_instance_eval);
|
66
70
|
}
|
data/ext/extconf_common.rb
CHANGED
@@ -22,11 +22,11 @@ module Contrast
|
|
22
22
|
else
|
23
23
|
ObjectSpace.define_finalizer(key, finalizing_proc)
|
24
24
|
end
|
25
|
-
super
|
25
|
+
super(key.__id__, obj)
|
26
26
|
end
|
27
27
|
|
28
28
|
def [] key
|
29
|
-
super
|
29
|
+
super(key.__id__)
|
30
30
|
end
|
31
31
|
|
32
32
|
# Something is trackable if it is not a collection and either not frozen or it was frozen after we put a
|
@@ -18,11 +18,20 @@ module Contrast
|
|
18
18
|
# This is just a holder for our policy. Takes the policy JSON and
|
19
19
|
# converts it into hashes that we can access nicely
|
20
20
|
class Policy < Contrast::Agent::Patching::Policy::Policy
|
21
|
+
PROVIDER_CLASSES = [
|
22
|
+
Contrast::Agent::Assess::Rule::Provider::HardcodedKey,
|
23
|
+
Contrast::Agent::Assess::Rule::Provider::HardcodedPassword
|
24
|
+
].cs__freeze
|
21
25
|
# Indicates the folder in `resources` where this policy lives.
|
22
26
|
def self.policy_folder
|
23
27
|
'assess'
|
24
28
|
end
|
25
29
|
|
30
|
+
def initialize
|
31
|
+
super
|
32
|
+
load_providers
|
33
|
+
end
|
34
|
+
|
26
35
|
# Indicates is this feature has been disabled by the configuration,
|
27
36
|
# read at startup, and therefore can never be enabled.
|
28
37
|
def disabled_globally?
|
@@ -33,11 +42,6 @@ module Contrast
|
|
33
42
|
Contrast::Agent::Assess::Policy::TriggerNode
|
34
43
|
end
|
35
44
|
|
36
|
-
def initialize
|
37
|
-
super
|
38
|
-
load_providers
|
39
|
-
end
|
40
|
-
|
41
45
|
# Our policy for dataflow rules is a 'dope ass' JSON file. Rather than
|
42
46
|
# hard code in a bunch of things to monkey patch, we let the JSON file
|
43
47
|
# define the conditions in which sources, propagators, and triggers are
|
@@ -88,11 +92,6 @@ module Contrast
|
|
88
92
|
providers[instance.rule_id] = instance
|
89
93
|
end
|
90
94
|
end
|
91
|
-
|
92
|
-
PROVIDER_CLASSES = [
|
93
|
-
Contrast::Agent::Assess::Rule::Provider::HardcodedKey,
|
94
|
-
Contrast::Agent::Assess::Rule::Provider::HardcodedPassword
|
95
|
-
].cs__freeze
|
96
95
|
end
|
97
96
|
end
|
98
97
|
end
|
@@ -14,6 +14,14 @@ module Contrast
|
|
14
14
|
# Ruby object, allowing for dynamic patching over hardcoded patching.
|
15
15
|
class PolicyNode < Contrast::Agent::Patching::Policy::PolicyNode
|
16
16
|
include PolicyNodeUtils
|
17
|
+
JSON_TAGS = 'tags'
|
18
|
+
JSON_DATAFLOW = 'dataflow'
|
19
|
+
# The keys used to read from policy.json to create the individual
|
20
|
+
# policy nodes. These are common across node types
|
21
|
+
JSON_SOURCE = 'source'
|
22
|
+
ALL_TYPE = 'A'
|
23
|
+
JSON_TARGET = 'target'
|
24
|
+
TO_MARKER = '2'
|
17
25
|
|
18
26
|
attr_accessor :tags, :type
|
19
27
|
attr_reader :sources, :targets, :source_string, :target_string
|
@@ -45,7 +53,7 @@ module Contrast
|
|
45
53
|
@sources = convert_policy_markers(source_string)
|
46
54
|
@targets = convert_policy_markers(target_string)
|
47
55
|
@_use_original_object = ORIGINAL_OBJECT_METHODS.include?(@method_name)
|
48
|
-
@_use_original_on_bang_method = assign_on_bang_check
|
56
|
+
@_use_original_on_bang_method = assign_on_bang_check(policy_hash)
|
49
57
|
end
|
50
58
|
|
51
59
|
def assign_on_bang_check policy_hash
|
@@ -116,8 +124,6 @@ module Contrast
|
|
116
124
|
end
|
117
125
|
end
|
118
126
|
|
119
|
-
ALL_TYPE = 'A'
|
120
|
-
TO_MARKER = '2'
|
121
127
|
# Convert our action, built from our source and target, into
|
122
128
|
# the TS appropriate action. That's a single source to single
|
123
129
|
# target marker (A,O,P,R)
|
@@ -149,13 +155,6 @@ module Contrast
|
|
149
155
|
@event_action
|
150
156
|
end
|
151
157
|
|
152
|
-
# The keys used to read from policy.json to create the individual
|
153
|
-
# policy nodes. These are common across node types
|
154
|
-
JSON_SOURCE = 'source'
|
155
|
-
JSON_TARGET = 'target'
|
156
|
-
JSON_TAGS = 'tags'
|
157
|
-
JSON_DATAFLOW = 'dataflow'
|
158
|
-
|
159
158
|
# This method will check if a method is fit to use it's original object and
|
160
159
|
# that the method is without bang - it does not change the source, but rather
|
161
160
|
# creates a copy of it.
|
@@ -216,8 +216,8 @@ module Contrast
|
|
216
216
|
# If we are using the original object tracking, the preshift object is not created.
|
217
217
|
# Instead identify the source as the original object itself and propagate with it.
|
218
218
|
source = propagation_node.use_original_object? ? propagation_data.object : preshift
|
219
|
-
handle_propagation
|
220
|
-
update_properties
|
219
|
+
handle_propagation(propagation_class, propagation_node, source, target)
|
220
|
+
update_properties(restore_frozen_state, propagation_node, target, propagation_data, ret)
|
221
221
|
end
|
222
222
|
|
223
223
|
def handle_propagation propagation_class, propagation_node, source, target
|
@@ -230,7 +230,7 @@ module Contrast
|
|
230
230
|
|
231
231
|
def update_properties restore_frozen_state, propagation_node, target, propagation_data, ret
|
232
232
|
if propagation_node.use_original_on_bang_method?
|
233
|
-
properties = use_original_object_properties
|
233
|
+
properties = use_original_object_properties(propagation_data)
|
234
234
|
|
235
235
|
return unless properties
|
236
236
|
else
|
@@ -22,6 +22,8 @@ module Contrast
|
|
22
22
|
attr_reader :untags, :patch_method
|
23
23
|
attr_accessor :action, :patch_class
|
24
24
|
|
25
|
+
TAGGER = 'Tagger'
|
26
|
+
PROPAGATOR = 'Propagator'
|
25
27
|
# Most things here carry over from PolicyNode.
|
26
28
|
# A couple things are new / have new rules
|
27
29
|
#
|
@@ -41,9 +43,6 @@ module Contrast
|
|
41
43
|
validate
|
42
44
|
end
|
43
45
|
|
44
|
-
TAGGER = 'Tagger'
|
45
|
-
PROPAGATOR = 'Propagator'
|
46
|
-
|
47
46
|
def node_class
|
48
47
|
@_node_class ||= tagger? ? TAGGER : PROPAGATOR
|
49
48
|
end
|
@@ -26,7 +26,7 @@ module Contrast
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def propagate _propagation_node, _preshift, _target
|
29
|
-
raise
|
29
|
+
raise(NoMethodError("Expected Base propagator subclass: #{ cs__class } to implement #propagate"))
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -106,7 +106,8 @@ module Contrast
|
|
106
106
|
ret
|
107
107
|
else
|
108
108
|
# SELECT
|
109
|
-
Contrast::Agent::Assess::Policy::Propagator::Select.select_tagger
|
109
|
+
Contrast::Agent::Assess::Policy::Propagator::Select.select_tagger(propagation_node, preshift, ret,
|
110
|
+
nil)
|
110
111
|
end
|
111
112
|
end
|
112
113
|
end
|
@@ -31,7 +31,7 @@ module Contrast
|
|
31
31
|
::Contrast::ASSESS.tainted_columns[class_name] = tainted_columns.keys
|
32
32
|
end
|
33
33
|
|
34
|
-
Contrast::Agent::Assess::Policy::DynamicSourceFactory.create_sources
|
34
|
+
Contrast::Agent::Assess::Policy::DynamicSourceFactory.create_sources(class_type, tainted_columns)
|
35
35
|
end
|
36
36
|
|
37
37
|
private
|
@@ -41,7 +41,7 @@ module Contrast
|
|
41
41
|
source = find_source(propagation_node.sources[0], preshift)
|
42
42
|
return unless (source_properties = Contrast::Agent::Assess::Tracker.properties(source))
|
43
43
|
|
44
|
-
update_element_properties
|
44
|
+
update_element_properties(propagation_node, target, preshift, source_properties)
|
45
45
|
nil
|
46
46
|
end
|
47
47
|
|
@@ -97,7 +97,7 @@ module Contrast
|
|
97
97
|
def instrument_string_split
|
98
98
|
@_instrument_string_split ||= begin
|
99
99
|
if ::Contrast::AGENT.patch_yield? && Funchook.available?
|
100
|
-
require
|
100
|
+
require('cs__assess_yield_track/cs__assess_yield_track')
|
101
101
|
end
|
102
102
|
true
|
103
103
|
rescue StandardError => e
|
@@ -40,7 +40,7 @@ module Contrast
|
|
40
40
|
source = preshift.object
|
41
41
|
args = preshift.args
|
42
42
|
properties.splat_from(source, ret)
|
43
|
-
event_data = Contrast::Agent::Assess::Events::EventData.new
|
43
|
+
event_data = Contrast::Agent::Assess::Events::EventData.new(patcher, ret, source, ret, args)
|
44
44
|
properties.build_event(event_data)
|
45
45
|
ret
|
46
46
|
end
|
@@ -15,13 +15,13 @@ module Contrast
|
|
15
15
|
|
16
16
|
JSON_TYPE = 'type'
|
17
17
|
SOURCE_TAG = 'UNTRUSTED'
|
18
|
+
SOURCE = 'Source'
|
18
19
|
def initialize source_hash = {}
|
19
20
|
super(source_hash)
|
20
21
|
@type = source_hash[JSON_TYPE]
|
21
22
|
@tags << SOURCE_TAG
|
22
23
|
end
|
23
24
|
|
24
|
-
SOURCE = 'Source'
|
25
25
|
def node_class
|
26
26
|
SOURCE
|
27
27
|
end
|
@@ -93,7 +93,7 @@ module Contrast
|
|
93
93
|
content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
|
94
94
|
|
95
95
|
if content_type.nil? && trigger_node.collectable?
|
96
|
-
Contrast::Agent::FINDINGS.collect_finding
|
96
|
+
Contrast::Agent::FINDINGS.collect_finding(trigger_node, source, object, ret, *args)
|
97
97
|
return
|
98
98
|
end
|
99
99
|
|
@@ -106,8 +106,8 @@ module Contrast
|
|
106
106
|
handle_new_finding(trigger_node, source, object, ret, request, *args)
|
107
107
|
else # TODO: RUBY-1438 -- remove
|
108
108
|
finding = Contrast::Api::Dtm::Finding.new
|
109
|
-
event_data = Contrast::Agent::Assess::Events::EventData.new
|
110
|
-
append_to_finding
|
109
|
+
event_data = Contrast::Agent::Assess::Events::EventData.new(trigger_node, source, object, ret, args)
|
110
|
+
append_to_finding(finding, event_data, request)
|
111
111
|
logger.trace('Finding created', node_id: trigger_node.id, source_id: source.__id__,
|
112
112
|
rule: trigger_node.rule_id)
|
113
113
|
report_finding(finding, request)
|
@@ -143,14 +143,14 @@ module Contrast
|
|
143
143
|
|
144
144
|
# If we're out of request context, then we need to report this finding ourselves,
|
145
145
|
# so we'll send it in the one-off activity we created.
|
146
|
-
Contrast::Agent.messaging_queue
|
146
|
+
Contrast::Agent.messaging_queue&.send_event_eventually(activity)
|
147
147
|
end
|
148
148
|
|
149
149
|
def handle_new_finding trigger_node, source, object, ret, request, *args
|
150
150
|
# sent to reporter
|
151
151
|
# here we will generate new type of finding
|
152
|
-
ruby_finding = Contrast::Agent::Reporting::Finding.new
|
153
|
-
ruby_finding.attach_data
|
152
|
+
ruby_finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
|
153
|
+
ruby_finding.attach_data(trigger_node, source, object, ret, request, *args)
|
154
154
|
hash_code = Contrast::Utils::HashDigest.generate_event_hash(ruby_finding, source, request)
|
155
155
|
ruby_finding.hash_code = hash_code
|
156
156
|
|
@@ -209,7 +209,7 @@ module Contrast
|
|
209
209
|
properties = Contrast::Agent::Assess::Tracker.properties(source)
|
210
210
|
return unless properties
|
211
211
|
|
212
|
-
build_events
|
212
|
+
build_events(finding, properties.event) if properties.event
|
213
213
|
|
214
214
|
# Google::Protobuf::Map doesn't support merge!, so we have to do this long form
|
215
215
|
source_props = properties.properties
|
@@ -28,6 +28,21 @@ module Contrast
|
|
28
28
|
|
29
29
|
attr_reader :rule_id, :required_tags, :disallowed_tags, :good_value, :bad_value
|
30
30
|
|
31
|
+
ENCODER_START = 'CUSTOM_ENCODED_'
|
32
|
+
# By default, any rule will be triggered if the source
|
33
|
+
# of the rule event has an untrusted tag range that is
|
34
|
+
# not covered by one of its disallowed tags.
|
35
|
+
UNTRUSTED = 'UNTRUSTED'
|
36
|
+
TRIGGER = 'Trigger'
|
37
|
+
VALIDATOR_START = 'CUSTOM_VALIDATED_'
|
38
|
+
# If a level 1 rule comes from TeamServer, it will have the
|
39
|
+
# tag 'custom-encoder-#{ name }' or 'custom-validator-#{ name }'.
|
40
|
+
# All rules should take this into account.
|
41
|
+
# Additionally, if something is marked 'limited-chars' it means
|
42
|
+
# it has been properly vetted to not contain dangerous input.
|
43
|
+
LIMITED_CHARS = 'LIMITED_CHARS'
|
44
|
+
CUSTOM_ENCODED = 'CUSTOM_ENCODED'
|
45
|
+
CUSTOM_VALIDATED = 'CUSTOM_VALIDATED'
|
31
46
|
def initialize trigger_hash = {}, rule_hash = {}
|
32
47
|
super(trigger_hash)
|
33
48
|
good_value = trigger_hash[JSON_GOOD_VALUE]
|
@@ -46,7 +61,6 @@ module Contrast
|
|
46
61
|
validate
|
47
62
|
end
|
48
63
|
|
49
|
-
TRIGGER = 'Trigger'
|
50
64
|
def node_class
|
51
65
|
TRIGGER
|
52
66
|
end
|
@@ -138,10 +152,6 @@ module Contrast
|
|
138
152
|
|
139
153
|
private
|
140
154
|
|
141
|
-
# By default, any rule will be triggered if the source
|
142
|
-
# of the rule event has an untrusted tag range that is
|
143
|
-
# not covered by one of its disallowed tags.
|
144
|
-
UNTRUSTED = 'UNTRUSTED'
|
145
155
|
def populate_tags required_tags
|
146
156
|
return unless dataflow?
|
147
157
|
|
@@ -150,16 +160,6 @@ module Contrast
|
|
150
160
|
@required_tags << UNTRUSTED
|
151
161
|
end
|
152
162
|
|
153
|
-
ENCODER_START = 'CUSTOM_ENCODED_'
|
154
|
-
VALIDATOR_START = 'CUSTOM_VALIDATED_'
|
155
|
-
# If a level 1 rule comes from TeamServer, it will have the
|
156
|
-
# tag 'custom-encoder-#{ name }' or 'custom-validator-#{ name }'.
|
157
|
-
# All rules should take this into account.
|
158
|
-
# Additionally, if something is marked 'limited-chars' it means
|
159
|
-
# it has been properly vetted to not contain dangerous input.
|
160
|
-
LIMITED_CHARS = 'LIMITED_CHARS'
|
161
|
-
CUSTOM_ENCODED = 'CUSTOM_ENCODED'
|
162
|
-
CUSTOM_VALIDATED = 'CUSTOM_VALIDATED'
|
163
163
|
def populate_disallowed disallowed_tags
|
164
164
|
return unless dataflow?
|
165
165
|
|
@@ -195,7 +195,7 @@ module Contrast
|
|
195
195
|
chunking = false
|
196
196
|
ranges = []
|
197
197
|
# find the start and end range of required tags:
|
198
|
-
search_ranges = find_required_ranges
|
198
|
+
search_ranges = find_required_ranges(properties, required_tags)
|
199
199
|
start_range = search_ranges.first
|
200
200
|
end_range = search_ranges.last + 1
|
201
201
|
|
@@ -42,8 +42,8 @@ module Contrast
|
|
42
42
|
return unless event.source_type
|
43
43
|
return unless (current_request = Contrast::Agent::REQUEST_TRACKER.current)
|
44
44
|
|
45
|
-
append_to_dtm
|
46
|
-
append_to_ruby_object
|
45
|
+
append_to_dtm(current_request, event)
|
46
|
+
append_to_ruby_object(current_request, event)
|
47
47
|
end
|
48
48
|
|
49
49
|
def append_to_dtm current_request, event
|
@@ -91,7 +91,7 @@ module Contrast
|
|
91
91
|
value.each do |tag|
|
92
92
|
comparison = tag.compare_range(range.begin, range.end)
|
93
93
|
# ABOVE and BELOW are not affected by this check
|
94
|
-
tags_remove_comparison
|
94
|
+
tags_remove_comparison(comparison, tag, remove, add, range)
|
95
95
|
end
|
96
96
|
value.delete_if { |tag| remove.include?(tag) }
|
97
97
|
Contrast::Utils::TagUtil.ordered_merge(value, add)
|
@@ -166,7 +166,7 @@ module Contrast
|
|
166
166
|
comparison = tag.compare_range(range.begin, range.end)
|
167
167
|
length = range.end - range.begin
|
168
168
|
# BELOW is not affected by this check
|
169
|
-
shift_tags_comparison
|
169
|
+
shift_tags_comparison(comparison, add, tag, length, range)
|
170
170
|
end
|
171
171
|
Contrast::Utils::TagUtil.ordered_merge(value, add)
|
172
172
|
end
|
@@ -12,28 +12,27 @@ module Contrast
|
|
12
12
|
# 2) the value is a non-empty array of only Fixnums
|
13
13
|
class HardcodedKey
|
14
14
|
include Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule
|
15
|
-
|
15
|
+
REDACTED_MARKER = ' = [**REDACTED**]'
|
16
16
|
NAME = 'hardcoded-key'
|
17
|
-
def rule_id
|
18
|
-
NAME
|
19
|
-
end
|
20
|
-
|
21
17
|
# These are names, determined by the security team (Matt & Ar), that
|
22
18
|
# indicate a field is likely to be a password or secret token of some
|
23
19
|
# sort.
|
24
20
|
KEY_FIELD_NAMES = %w[KEY AES DES IV SECRET].cs__freeze
|
25
|
-
|
26
21
|
# These are markers whose presence indicates that a field is more
|
27
22
|
# likely to be a descriptor or requirement than an actual key.
|
28
23
|
# We should ignore fields that contain them.
|
29
24
|
NON_KEY_PARTIAL_NAMES = %w[CONTENT_CODES RESPONSE_CODES ERROR_CODES].cs__freeze
|
25
|
+
BYTE_HOLDERS = %i[ARRAY LIST].cs__freeze
|
26
|
+
|
27
|
+
def rule_id
|
28
|
+
NAME
|
29
|
+
end
|
30
30
|
|
31
31
|
def name_passes? constant_string
|
32
32
|
KEY_FIELD_NAMES.any? { |name| constant_string.index(name) } &&
|
33
33
|
NON_KEY_PARTIAL_NAMES.none? { |name| constant_string.index(name) }
|
34
34
|
end
|
35
35
|
|
36
|
-
BYTE_HOLDERS = %i[ARRAY LIST].cs__freeze
|
37
36
|
# Determine if the given value node violates the hardcode key rule
|
38
37
|
# @param value_node [RubyVM::AbstractSyntaxTree::Node] the node to
|
39
38
|
# evaluate
|
@@ -66,7 +65,6 @@ module Contrast
|
|
66
65
|
true
|
67
66
|
end
|
68
67
|
|
69
|
-
REDACTED_MARKER = ' = [**REDACTED**]'
|
70
68
|
def redacted_marker
|
71
69
|
REDACTED_MARKER
|
72
70
|
end
|
@@ -15,12 +15,9 @@ module Contrast
|
|
15
15
|
# mixing the characters counts as a violation of this rule
|
16
16
|
class HardcodedPassword
|
17
17
|
include Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule
|
18
|
-
|
19
18
|
NAME = 'hardcoded-password'
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
19
|
+
REDACTED_MARKER = ' = "**REDACTED**"'
|
20
|
+
PROPERTY_NAME_PATTERN = /^[a-z]+[._][._a-z]*[a-z]+$/.cs__freeze
|
24
21
|
# These are names, determined by the security team (Matt & Ar), that
|
25
22
|
# indicate a field is likely to be a password or secret token of some
|
26
23
|
# sort.
|
@@ -34,6 +31,10 @@ module Contrast
|
|
34
31
|
URI
|
35
32
|
].cs__freeze
|
36
33
|
|
34
|
+
def rule_id
|
35
|
+
NAME
|
36
|
+
end
|
37
|
+
|
37
38
|
# If the constant looks like a password and it doesn't look like a
|
38
39
|
# password descriptor, it passes for this rule
|
39
40
|
def name_passes? constant_string
|
@@ -62,12 +63,10 @@ module Contrast
|
|
62
63
|
# characters are probably more likely to appear together in a
|
63
64
|
# default placeholder than in a password. Note this is opposite of
|
64
65
|
# the behavior in Java
|
65
|
-
PROPERTY_NAME_PATTERN = /^[a-z]+[._][._a-z]*[a-z]+$/.cs__freeze
|
66
66
|
def probably_property_name? value
|
67
67
|
value.match?(PROPERTY_NAME_PATTERN)
|
68
68
|
end
|
69
69
|
|
70
|
-
REDACTED_MARKER = ' = "**REDACTED**"'
|
71
70
|
def redacted_marker
|
72
71
|
REDACTED_MARKER
|
73
72
|
end
|
@@ -86,9 +86,9 @@ module Contrast
|
|
86
86
|
return unless value_node_passes?(value)
|
87
87
|
|
88
88
|
if Contrast::Agent::Reporter.enabled?
|
89
|
-
new_finding_and_reporting
|
89
|
+
new_finding_and_reporting(mod, name)
|
90
90
|
else # TODO: RUBY-1438 -- remove
|
91
|
-
build_finding
|
91
|
+
build_finding(mod, name)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
@@ -112,7 +112,7 @@ module Contrast
|
|
112
112
|
def build_finding clazz, constant_string
|
113
113
|
class_name = clazz.cs__name
|
114
114
|
|
115
|
-
finding = assign_finding
|
115
|
+
finding = assign_finding(class_name, constant_string)
|
116
116
|
activity = Contrast::Api::Dtm::Activity.new
|
117
117
|
activity.findings << finding
|
118
118
|
Contrast::Agent.messaging_queue.send_event_eventually(activity, force: true)
|
@@ -153,12 +153,12 @@ module Contrast
|
|
153
153
|
|
154
154
|
# extract to new method
|
155
155
|
# here we will generate new type of finding
|
156
|
-
ruby_finding = Contrast::Agent::Reporting::Finding.new
|
156
|
+
ruby_finding = Contrast::Agent::Reporting::Finding.new(rule_id)
|
157
157
|
ruby_finding.hash_code = hash
|
158
158
|
ruby_finding.properties[SOURCE_KEY] = clazz.cs__name
|
159
159
|
ruby_finding.properties[CONSTANT_NAME_KEY] = constant_string
|
160
160
|
ruby_finding.properties[CODE_SOURCE_KEY] = constant_string + redacted_marker
|
161
|
-
save_and_report_finding
|
161
|
+
save_and_report_finding(ruby_finding, new_preflight)
|
162
162
|
end
|
163
163
|
|
164
164
|
def save_and_report_finding ruby_finding, new_preflight
|
@@ -17,6 +17,7 @@ module Contrast
|
|
17
17
|
# These rules check the content of the HTTP Response to determine if something was set incorrectly or
|
18
18
|
# insecurely in it.
|
19
19
|
class BaseRule
|
20
|
+
DATA = 'data'.cs__freeze
|
20
21
|
# Analyze a given application response to determine if it violates the rule
|
21
22
|
#
|
22
23
|
# TODO: RUBY-999999 either extract the response's body or memoize it to some degree so that it's not
|
@@ -47,8 +48,6 @@ module Contrast
|
|
47
48
|
|
48
49
|
protected
|
49
50
|
|
50
|
-
DATA = 'data'.cs__freeze
|
51
|
-
|
52
51
|
# Rules discern which responses they can/should analyze.
|
53
52
|
#
|
54
53
|
# @param response [Contrast::Agent::Response] the response of the application
|
@@ -82,7 +81,7 @@ module Contrast
|
|
82
81
|
finding.rule_id = rule_id
|
83
82
|
context = Contrast::Agent::REQUEST_TRACKER.current
|
84
83
|
finding.routes << context.route if context&.route
|
85
|
-
build_evidence
|
84
|
+
build_evidence(evidence, finding)
|
86
85
|
hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
|
87
86
|
finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
|
88
87
|
finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
|