newrelic_security 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/lib/newrelic_security/agent/agent.rb +6 -2
- data/lib/newrelic_security/agent/configuration/manager.rb +2 -3
- data/lib/newrelic_security/agent/control/app_info.rb +2 -0
- data/lib/newrelic_security/agent/control/application_runtime_error.rb +95 -0
- data/lib/newrelic_security/agent/control/application_url_mappings.rb +2 -0
- data/lib/newrelic_security/agent/control/control_command.rb +2 -1
- data/lib/newrelic_security/agent/control/critical_message.rb +2 -0
- data/lib/newrelic_security/agent/control/error_reporting.rb +74 -0
- data/lib/newrelic_security/agent/control/event.rb +13 -4
- data/lib/newrelic_security/agent/control/event_processor.rb +21 -0
- data/lib/newrelic_security/agent/control/exit_event.rb +2 -0
- data/lib/newrelic_security/agent/control/grpc_context.rb +2 -1
- data/lib/newrelic_security/agent/control/health_check.rb +2 -0
- data/lib/newrelic_security/agent/control/http_context.rb +8 -2
- data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +2 -0
- data/lib/newrelic_security/agent/control/websocket_client.rb +5 -0
- data/lib/newrelic_security/agent/utils/agent_utils.rb +3 -2
- data/lib/newrelic_security/constants.rb +1 -0
- data/lib/newrelic_security/instrumentation-security/grape/chain.rb +7 -2
- data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +2 -1
- data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +7 -1
- data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +17 -0
- data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +14 -2
- data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +12 -0
- data/lib/newrelic_security/instrumentation-security/rails/chain.rb +26 -2
- data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +28 -3
- data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/roda/chain.rb +7 -2
- data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +2 -1
- data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +7 -1
- data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +6 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +8 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +4 -0
- data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +4 -4
- data/lib/newrelic_security/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b1160c4b821177b6ce0400848a72bf09899780ff83abadb60b3e60f35b8338b
|
4
|
+
data.tar.gz: 98cfb5a7d513e842690dce03aa515a7e148a252a5f0666f16e40899e2ad511d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56dea910383ac11f0b87a29f304e0f5042336c142c1d8ca60a2cdaf15bf172102000e28e8f382fd99781afebd061a0693e3f5ff8ea9fba981cdc4de128abdc20
|
7
|
+
data.tar.gz: 04bb28178707141c66ac94158fdb3cd632ccb22a1a9c4b4f5f7d9438989ee3c6fba2124abc89ffbf135d34e249b5921cd5d4b924e50768f07363b219567867bb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# New Relic Ruby Security Agent Release Notes
|
2
2
|
|
3
|
+
## v0.2.0
|
4
|
+
|
5
|
+
Version 0.2.0 introuduces Error reporting as part of security. Any unhandled or 5xx errors in application runtime will now be visible in IAST capability UI. Updated json_version: **1.2.4**
|
6
|
+
|
7
|
+
- Feature: Unhandled and 5xx error reproting [PR#134](https://github.com/newrelic/csec-ruby-agent/pull/134)
|
8
|
+
|
9
|
+
- Bugfix: Fix for API route not present in rails7 [PR#127](https://github.com/newrelic/csec-ruby-agent/pull/127)
|
10
|
+
|
11
|
+
- Bugfix: Fix for Sqlite3 parameters sent in wrong fromat [PR#130](https://github.com/newrelic/csec-ruby-agent/pull/130)
|
12
|
+
|
13
|
+
- Bugfix: Fix for multiple events have same id [PR#135](https://github.com/newrelic/csec-ruby-agent/pull/135)
|
14
|
+
|
15
|
+
- Bugfix: Fix for NR_CSEC_VALIDATOR_HOME_TMP placeholder value not replaced during File Access fuzzing [PR#138](https://github.com/newrelic/csec-ruby-agent/pull/138)
|
16
|
+
|
17
|
+
- Bugfix: Fix for appServerInfo fields are not present in File Operation events [PR#139](https://github.com/newrelic/csec-ruby-agent/pull/139)
|
18
|
+
|
19
|
+
- Sending security agent critical errors to APM error inbox [PR#137](https://github.com/newrelic/csec-ruby-agent/pull/137)
|
20
|
+
|
21
|
+
- Added key identifiers in entityGuid and acccountId in all json reporting [PR#101](https://github.com/newrelic/csec-ruby-agent/pull/101)
|
22
|
+
|
23
|
+
|
3
24
|
## v0.1.0
|
4
25
|
|
5
26
|
Version 0.1.0 introduces `newrelic_security` agent for public preview under Newrelic pre-release software notice.
|
@@ -17,13 +17,15 @@ require 'newrelic_security/agent/control/critical_message'
|
|
17
17
|
require 'newrelic_security/agent/control/event_counter'
|
18
18
|
require 'newrelic_security/agent/control/event_stats'
|
19
19
|
require 'newrelic_security/agent/control/exit_event'
|
20
|
+
require 'newrelic_security/agent/control/application_runtime_error'
|
21
|
+
require 'newrelic_security/agent/control/error_reporting'
|
20
22
|
require 'newrelic_security/instrumentation-security/instrumentation_loader'
|
21
23
|
|
22
24
|
module NewRelic::Security
|
23
25
|
module Agent
|
24
26
|
class Agent
|
25
27
|
|
26
|
-
attr_accessor :websocket_client, :event_processor, :iast_client, :http_request_count, :event_processed_count, :event_sent_count, :event_drop_count, :route_map, :iast_event_stats, :rasp_event_stats, :exit_event_stats
|
28
|
+
attr_accessor :websocket_client, :event_processor, :iast_client, :http_request_count, :event_processed_count, :event_sent_count, :event_drop_count, :route_map, :iast_event_stats, :rasp_event_stats, :exit_event_stats, :error_reporting
|
27
29
|
|
28
30
|
def initialize
|
29
31
|
NewRelic::Security::Agent.config
|
@@ -41,6 +43,7 @@ module NewRelic::Security
|
|
41
43
|
@iast_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
42
44
|
@rasp_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
43
45
|
@exit_event_stats = NewRelic::Security::Agent::Control::EventStats.new
|
46
|
+
@error_reporting = NewRelic::Security::Agent::Control::ErrorReporting.new
|
44
47
|
end
|
45
48
|
|
46
49
|
def init
|
@@ -93,7 +96,8 @@ module NewRelic::Security
|
|
93
96
|
def find_or_create_file_path(path)
|
94
97
|
::FileUtils.mkdir_p(path) unless ::File.directory?(path)
|
95
98
|
::File.directory?(path)
|
96
|
-
rescue
|
99
|
+
rescue => e
|
100
|
+
::NewRelic::Agent.notice_error(e)
|
97
101
|
return false
|
98
102
|
end
|
99
103
|
|
@@ -39,7 +39,7 @@ module NewRelic::Security
|
|
39
39
|
@cache[:listen_port] = nil
|
40
40
|
@cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
|
41
41
|
@cache[:jruby_objectspace_enabled] = false
|
42
|
-
@cache[:json_version] = :'1.2.
|
42
|
+
@cache[:json_version] = :'1.2.4'
|
43
43
|
|
44
44
|
@environment_source = NewRelic::Security::Agent::Configuration::EnvironmentSource.new
|
45
45
|
@server_source = NewRelic::Security::Agent::Configuration::ServerSource.new
|
@@ -47,8 +47,7 @@ module NewRelic::Security
|
|
47
47
|
@yaml_source = NewRelic::Security::Agent::Configuration::YamlSource.new
|
48
48
|
@default_source = NewRelic::Security::Agent::Configuration::DefaultSource.new
|
49
49
|
rescue Exception => exception
|
50
|
-
|
51
|
-
puts "Exception in Configuration::Manager.initialize : #{exception.inspect} #{exception.backtrace}"
|
50
|
+
::NewRelic::Agent.notice_error(exception)
|
52
51
|
end
|
53
52
|
|
54
53
|
def [](key)
|
@@ -26,6 +26,8 @@ module NewRelic::Security
|
|
26
26
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
27
27
|
@startTime = current_time_millis
|
28
28
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
29
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
30
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
29
31
|
@framework = NewRelic::Security::Agent.config[:framework]
|
30
32
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
31
33
|
@userProvidedApplicationInfo = Hash.new
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
module NewRelic::Security
|
7
|
+
module Agent
|
8
|
+
module Control
|
9
|
+
class ApplicationRuntimeError
|
10
|
+
attr_reader :jsonName, :exception
|
11
|
+
attr_accessor :counter
|
12
|
+
|
13
|
+
def initialize(exception, ctxt, response_code, category)
|
14
|
+
@collectorType = RUBY
|
15
|
+
@language = Ruby
|
16
|
+
@jsonName = :'application-runtime-error'
|
17
|
+
@eventType = :'application-runtime-error'
|
18
|
+
@collectorVersion = NewRelic::Security::VERSION
|
19
|
+
@buildNumber = nil
|
20
|
+
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
21
|
+
@timestamp = current_time_millis
|
22
|
+
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
23
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
24
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
25
|
+
@framework = NewRelic::Security::Agent.config[:framework]
|
26
|
+
@groupName = NewRelic::Security::Agent.config[:mode]
|
27
|
+
@policyVersion = nil
|
28
|
+
@linkingMetadata = add_linking_metadata
|
29
|
+
@httpRequest = get_http_request_data(ctxt)
|
30
|
+
@exception = exception
|
31
|
+
@counter = 1
|
32
|
+
@responseCode = response_code
|
33
|
+
@category = category
|
34
|
+
@traceId = generate_trace_id(ctxt, category)
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_json
|
38
|
+
instance_variables.map! do |ivar|
|
39
|
+
[ivar[1..-1].to_sym, instance_variable_get(ivar)]
|
40
|
+
end.to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(*_args)
|
44
|
+
as_json.to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def current_time_millis
|
50
|
+
(Time.now.to_f * 1000).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_linking_metadata
|
54
|
+
linking_metadata = {}
|
55
|
+
linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
|
56
|
+
linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
|
57
|
+
# TODO: add other fields as well in linking metadata, for event and heathcheck as well
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_http_request_data(ctxt)
|
61
|
+
return if ctxt.nil?
|
62
|
+
http_request = {}
|
63
|
+
http_request[:parameterMap] = {}
|
64
|
+
http_request[:body] = ctxt.body
|
65
|
+
http_request[:generationTime] = ctxt.time_stamp
|
66
|
+
http_request[:dataTruncated] = false
|
67
|
+
http_request[:method] = ctxt.method
|
68
|
+
http_request[:route] = ctxt.route.split(AT_THE_RATE)[1] if ctxt.route
|
69
|
+
http_request[:url] = URI(ctxt.req[REQUEST_URI]).respond_to?(:request_uri) ? URI(ctxt.req[REQUEST_URI]).request_uri : ctxt.req[REQUEST_URI]
|
70
|
+
http_request[:requestURI] = "#{ctxt.req[RACK_URL_SCHEME]}://#{ctxt.req[HTTP_HOST]}#{ctxt.req[PATH_INFO]}"
|
71
|
+
http_request[:clientIP] = ctxt.headers.key?(X_FORWARDED_FOR) ? ctxt.headers[X_FORWARDED_FOR].split(COMMA)[0].to_s : ctxt.req[REMOTE_ADDR].to_s
|
72
|
+
http_request[:serverPort] = ctxt.req[SERVER_PORT].to_i
|
73
|
+
http_request[:protocol] = ctxt.req[RACK_URL_SCHEME]
|
74
|
+
http_request[:contextPath] = ROOT_PATH
|
75
|
+
http_request[:headers] = ctxt.headers
|
76
|
+
http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.key?(CONTENT_TYPE)
|
77
|
+
http_request[:headers][CONTENT_TYPE1] = ctxt.req[CONTENT_TYPE] if ctxt.req.key?(CONTENT_TYPE)
|
78
|
+
http_request[:dataTruncated] = ctxt.data_truncated
|
79
|
+
http_request
|
80
|
+
end
|
81
|
+
|
82
|
+
def generate_trace_id(ctxt, category)
|
83
|
+
@exception[:stackTrace]
|
84
|
+
method, route = ctxt.route.split(AT_THE_RATE) if ctxt.route
|
85
|
+
if @exception[:stackTrace]
|
86
|
+
::Digest::SHA256.hexdigest("#{@exception[:stackTrace].join(PIPE)}#{category}#{route}#{method}").to_s
|
87
|
+
else
|
88
|
+
::Digest::SHA256.hexdigest("#{category}#{route}#{method}").to_s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -21,6 +21,8 @@ module NewRelic::Security
|
|
21
21
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
22
22
|
@timestamp = current_time_millis
|
23
23
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
24
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
25
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
24
26
|
@framework = NewRelic::Security::Agent.config[:framework]
|
25
27
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
26
28
|
@policyVersion = nil
|
@@ -32,7 +32,7 @@ module NewRelic::Security
|
|
32
32
|
fuzz_request = NewRelic::Security::Agent::Control::FuzzRequest.new(message_object[:id])
|
33
33
|
fuzz_request.request = prepare_fuzz_request(message_object)
|
34
34
|
fuzz_request.case_type = message_object[:arguments][1]
|
35
|
-
fuzz_request.reflected_metadata =
|
35
|
+
fuzz_request.reflected_metadata = message_object[:reflectedMetaData]
|
36
36
|
NewRelic::Security::Agent.agent.iast_client.pending_request_ids << message_object[:id]
|
37
37
|
NewRelic::Security::Agent.agent.iast_client.enqueue(fuzz_request)
|
38
38
|
fuzz_request = nil
|
@@ -104,6 +104,7 @@ module NewRelic::Security
|
|
104
104
|
|
105
105
|
def prepare_fuzz_request(message_object)
|
106
106
|
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_HOME_TMP, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
107
|
+
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
107
108
|
message_object[:arguments][0].gsub!(NR_CSEC_VALIDATOR_FILE_SEPARATOR, ::File::SEPARATOR)
|
108
109
|
prepared_fuzz_request = ::JSON.parse(message_object[:arguments][0])
|
109
110
|
prepared_fuzz_request[HEADERS][NR_CSEC_PARENT_ID] = message_object[:id]
|
@@ -21,6 +21,8 @@ module NewRelic::Security
|
|
21
21
|
@buildNumber = nil
|
22
22
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
23
23
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
24
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
25
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
24
26
|
@linkingMetadata = add_linking_metadata
|
25
27
|
@timestamp = current_time_millis
|
26
28
|
@message = message
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module NewRelic::Security
|
2
|
+
module Agent
|
3
|
+
module Control
|
4
|
+
class ErrorReporting
|
5
|
+
|
6
|
+
STATUS_CODES_5XX = {
|
7
|
+
500 => "Internal Server Error",
|
8
|
+
501 => "Not Implemented",
|
9
|
+
502 => "Bad Gateway",
|
10
|
+
503 => "Service Unavailable",
|
11
|
+
504 => "Gateway Timeout",
|
12
|
+
505 => "HTTP Version Not Supported",
|
13
|
+
506 => "Variant Also Negotiates",
|
14
|
+
507 => "Insufficient Storage",
|
15
|
+
508 => "Loop Detected",
|
16
|
+
509 => "Bandwidth Limit Exceeded",
|
17
|
+
510 => "Not Extended",
|
18
|
+
511 => "Network Authentication Required"
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
attr_accessor :exceptions_map
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@exceptions_map = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_unhandled_exception(noticed_error, ctxt, response_code)
|
28
|
+
unhandled_exception = {}
|
29
|
+
category = nil
|
30
|
+
if noticed_error
|
31
|
+
unhandled_exception[:message] = noticed_error.message
|
32
|
+
unhandled_exception[:cause] = nil
|
33
|
+
unhandled_exception[:type] = noticed_error.exception_class_name
|
34
|
+
unhandled_exception[:stackTrace] = noticed_error.stack_trace
|
35
|
+
category = noticed_error.exception_class_name
|
36
|
+
end
|
37
|
+
category = STATUS_CODES_5XX[response_code] if response_code
|
38
|
+
application_runtime_error = NewRelic::Security::Agent::Control::ApplicationRuntimeError.new(unhandled_exception, ctxt, response_code, category)
|
39
|
+
key = if response_code
|
40
|
+
# TODO: when do refactoring of ctxt.route, use both route and method to generate key
|
41
|
+
ctxt.route&.+ response_code.to_s
|
42
|
+
else
|
43
|
+
application_runtime_error.exception[:type] + application_runtime_error.exception[:stackTrace][0]
|
44
|
+
end
|
45
|
+
application_runtime_error.counter = @exceptions_map[key].counter + 1 if @exceptions_map.key?(key)
|
46
|
+
@exceptions_map[key] = application_runtime_error
|
47
|
+
rescue Exception => exception
|
48
|
+
NewRelic::Security::Agent.logger.error "Exception in generating unhandled exception: #{exception.inspect} #{exception.backtrace}\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_noticed_error(current_transaction, ctxt, response_code)
|
52
|
+
# TODO: Below operation is expensive, talk to APM to get optimized way to do this
|
53
|
+
current_transaction.exceptions.each do |_, span|
|
54
|
+
current_transaction.segments.each do |segment|
|
55
|
+
generate_unhandled_exception(segment.noticed_error, ctxt, response_code) if span[:span_id] == segment.guid
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue Exception => exception
|
59
|
+
NewRelic::Security::Agent.logger.error "Exception in extract_noticed_error: #{exception.inspect} #{exception.backtrace}\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
def report_unhandled_or_5xx_exceptions(current_transaction, ctxt, response_code = nil)
|
63
|
+
http_response_code = response_code || current_transaction&.http_response_code
|
64
|
+
if current_transaction.exceptions.empty? && http_response_code&.between?(500, 599)
|
65
|
+
generate_unhandled_exception(nil, ctxt, response_code)
|
66
|
+
else
|
67
|
+
extract_noticed_error(current_transaction, ctxt, response_code) unless current_transaction.exceptions.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -26,9 +26,11 @@ module NewRelic::Security
|
|
26
26
|
@buildNumber = nil
|
27
27
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
28
28
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
29
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
30
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
29
31
|
@httpRequest = Hash.new
|
30
32
|
@httpResponse = Hash.new
|
31
|
-
@metaData = { :reflectedMetaData => { :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s } }
|
33
|
+
@metaData = { :reflectedMetaData => { :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s }, :appServerInfo => { :applicationDirectory => NewRelic::Security::Agent.config[:app_root], :serverBaseDirectory => NewRelic::Security::Agent.config[:app_root] } }
|
32
34
|
@linkingMetadata = add_linking_metadata
|
33
35
|
@pid = pid
|
34
36
|
@parameters = args
|
@@ -113,7 +115,7 @@ module NewRelic::Security
|
|
113
115
|
http_request[:contentType] = "TODO: "
|
114
116
|
http_request[:isGrpc] = ctxt.is_grpc
|
115
117
|
@httpRequest = http_request
|
116
|
-
@metaData
|
118
|
+
@metaData.merge!(ctxt.metadata)
|
117
119
|
end
|
118
120
|
|
119
121
|
private
|
@@ -128,13 +130,20 @@ module NewRelic::Security
|
|
128
130
|
end
|
129
131
|
|
130
132
|
def event_id
|
131
|
-
"#{Process.pid}:#{
|
133
|
+
"#{Process.pid}:#{current_transaction&.guid}:#{thread_monotonic_ctr}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def current_transaction
|
137
|
+
::NewRelic::Agent::Tracer.current_transaction if defined?(::NewRelic::Agent::Tracer)
|
132
138
|
end
|
133
139
|
|
134
140
|
def thread_monotonic_ctr
|
135
141
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context if NewRelic::Security::Agent::Control::HTTPContext.get_context
|
136
142
|
ctxt = NewRelic::Security::Agent::Control::GRPCContext.get_context if NewRelic::Security::Agent::Control::GRPCContext.get_context
|
137
|
-
|
143
|
+
return unless ctxt
|
144
|
+
ctxt.mutex.synchronize do
|
145
|
+
ctxt.event_counter = ctxt.event_counter + 1
|
146
|
+
end
|
138
147
|
end
|
139
148
|
|
140
149
|
def add_linking_metadata
|
@@ -5,6 +5,7 @@ module NewRelic::Security
|
|
5
5
|
module Control
|
6
6
|
EVENT_QUEUE_SIZE = 10_000
|
7
7
|
HEALTH_INTERVAL = 300
|
8
|
+
ERROR_REPORTING_INTERVAL = 30
|
8
9
|
|
9
10
|
class EventProcessor
|
10
11
|
|
@@ -15,6 +16,7 @@ module NewRelic::Security
|
|
15
16
|
@eventQ = ::SizedQueue.new(EVENT_QUEUE_SIZE)
|
16
17
|
create_dequeue_threads
|
17
18
|
create_keep_alive_thread
|
19
|
+
create_error_reporting_thread
|
18
20
|
NewRelic::Security::Agent.init_logger.info "[STEP-5] => Security agent components started"
|
19
21
|
end
|
20
22
|
|
@@ -128,6 +130,25 @@ module NewRelic::Security
|
|
128
130
|
NewRelic::Security::Agent.logger.error "Exception in health check thread, #{exception.inspect}"
|
129
131
|
end
|
130
132
|
|
133
|
+
def create_error_reporting_thread
|
134
|
+
@error_reporting_thread = Thread.new {
|
135
|
+
Thread.current.name = "newrelic_security_error_reporting_thread"
|
136
|
+
while true do
|
137
|
+
sleep ERROR_REPORTING_INTERVAL
|
138
|
+
send_error_reporting_event if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
|
139
|
+
end
|
140
|
+
}
|
141
|
+
rescue Exception => exception
|
142
|
+
NewRelic::Security::Agent.logger.error "Exception in error_reporting thread, #{exception.inspect}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def send_error_reporting_event
|
146
|
+
NewRelic::Security::Agent.agent.error_reporting&.exceptions_map&.each_value do |exception|
|
147
|
+
NewRelic::Security::Agent::Control::WebsocketClient.instance.send(exception)
|
148
|
+
end
|
149
|
+
NewRelic::Security::Agent.agent.error_reporting&.exceptions_map&.clear
|
150
|
+
end
|
151
|
+
|
131
152
|
end
|
132
153
|
end
|
133
154
|
end
|
@@ -14,6 +14,8 @@ module NewRelic::Security
|
|
14
14
|
@collectorVersion = NewRelic::Security::VERSION
|
15
15
|
@buildNumber = nil
|
16
16
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
17
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
18
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
17
19
|
@groupName = NewRelic::Security::Agent.config[:mode]
|
18
20
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
19
21
|
@policyVersion = nil
|
@@ -8,7 +8,7 @@ module NewRelic::Security
|
|
8
8
|
|
9
9
|
class GRPCContext
|
10
10
|
|
11
|
-
attr_accessor :time_stamp, :method, :headers, :body, :route, :cache, :fuzz_files, :url, :server_name, :server_port, :client_ip, :client_port, :is_grpc, :metadata, :event_counter
|
11
|
+
attr_accessor :time_stamp, :method, :headers, :body, :route, :cache, :fuzz_files, :url, :server_name, :server_port, :client_ip, :client_port, :is_grpc, :metadata, :event_counter, :mutex
|
12
12
|
|
13
13
|
def initialize(grpc_request)
|
14
14
|
@time_stamp = current_time_millis
|
@@ -32,6 +32,7 @@ module NewRelic::Security
|
|
32
32
|
@cache = {}
|
33
33
|
@fuzz_files = ::Set.new
|
34
34
|
@event_counter = 0
|
35
|
+
@mutex = Mutex.new
|
35
36
|
NewRelic::Security::Agent.agent.http_request_count.increment
|
36
37
|
end
|
37
38
|
|
@@ -18,6 +18,8 @@ module NewRelic::Security
|
|
18
18
|
@framework = NewRelic::Security::Agent.config[:framework]
|
19
19
|
@protectedServer = nil
|
20
20
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
21
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
22
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
21
23
|
@collectorVersion = NewRelic::Security::VERSION
|
22
24
|
@buildNumber = nil
|
23
25
|
@jsonVersion = NewRelic::Security::Agent.config[:json_version]
|
@@ -10,13 +10,14 @@ module NewRelic::Security
|
|
10
10
|
UNDERSCORE = '_'
|
11
11
|
HYPHEN = '-'
|
12
12
|
REQUEST_METHOD = 'REQUEST_METHOD'
|
13
|
+
HTTP_HOST = 'HTTP_HOST'
|
13
14
|
PATH_INFO = 'PATH_INFO'
|
14
15
|
RACK_INPUT = 'rack.input'
|
15
|
-
CGI_VARIABLES = ::Set.new(%W[ AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE HTTPS PATH_INFO PATH_TRANSLATED REQUEST_URI QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE rack.url_scheme ])
|
16
|
+
CGI_VARIABLES = ::Set.new(%W[ AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE HTTPS HTTP_HOST PATH_INFO PATH_TRANSLATED REQUEST_URI QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE rack.url_scheme ])
|
16
17
|
|
17
18
|
class HTTPContext
|
18
19
|
|
19
|
-
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter
|
20
|
+
attr_accessor :time_stamp, :req, :method, :headers, :params, :body, :data_truncated, :route, :cache, :fuzz_files, :event_counter, :mutex
|
20
21
|
|
21
22
|
def initialize(env)
|
22
23
|
@time_stamp = current_time_millis
|
@@ -47,6 +48,7 @@ module NewRelic::Security
|
|
47
48
|
@cache = Hash.new
|
48
49
|
@fuzz_files = ::Set.new
|
49
50
|
@event_counter = 0
|
51
|
+
@mutex = Mutex.new
|
50
52
|
NewRelic::Security::Agent.agent.http_request_count.increment
|
51
53
|
NewRelic::Security::Agent.agent.iast_client.completed_requests[@headers[NR_CSEC_PARENT_ID]] = [] if @headers.key?(NR_CSEC_PARENT_ID)
|
52
54
|
end
|
@@ -66,6 +68,10 @@ module NewRelic::Security
|
|
66
68
|
def self.reset_context
|
67
69
|
::NewRelic::Agent::Tracer.current_transaction.remove_instance_variable(:@security_context_data) if ::NewRelic::Agent::Tracer.current_transaction.instance_variable_defined?(:@security_context_data)
|
68
70
|
end
|
71
|
+
|
72
|
+
def self.get_current_transaction
|
73
|
+
::NewRelic::Agent::Tracer.current_transaction
|
74
|
+
end
|
69
75
|
end
|
70
76
|
|
71
77
|
end
|
@@ -11,6 +11,8 @@ module NewRelic::Security
|
|
11
11
|
def initialize
|
12
12
|
@jsonName = :'iast-data-request'
|
13
13
|
@applicationUUID = NewRelic::Security::Agent.config[:uuid]
|
14
|
+
@appAccountId = NewRelic::Security::Agent.config[:account_id]
|
15
|
+
@appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
|
14
16
|
@batchSize = 10
|
15
17
|
@pendingRequestIds = []
|
16
18
|
@completedRequests = Hash.new
|
@@ -76,21 +76,26 @@ module NewRelic::Security
|
|
76
76
|
|
77
77
|
connection.on :error do |e|
|
78
78
|
NewRelic::Security::Agent.logger.error "Error in websocket connection : #{e.inspect} #{e.backtrace}"
|
79
|
+
::NewRelic::Agent.notice_error(e)
|
79
80
|
Thread.new { NewRelic::Security::Agent::Control::WebsocketClient.instance.close(true) }
|
80
81
|
end
|
81
82
|
rescue Errno::EPIPE => exception
|
82
83
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
84
|
+
::NewRelic::Agent.notice_error(exception)
|
83
85
|
NewRelic::Security::Agent.config.disable_security
|
84
86
|
rescue Errno::ECONNRESET => exception
|
85
87
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
88
|
+
::NewRelic::Agent.notice_error(exception)
|
86
89
|
NewRelic::Security::Agent.config.disable_security
|
87
90
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
88
91
|
rescue Errno::ECONNREFUSED => exception
|
89
92
|
NewRelic::Security::Agent.logger.error "Unable to connect to validator_service: #{exception.inspect}"
|
93
|
+
::NewRelic::Agent.notice_error(exception)
|
90
94
|
NewRelic::Security::Agent.config.disable_security
|
91
95
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
92
96
|
rescue => exception
|
93
97
|
NewRelic::Security::Agent.logger.error "Exception in websocket init: #{exception.inspect} #{exception.backtrace}"
|
98
|
+
::NewRelic::Agent.notice_error(exception)
|
94
99
|
NewRelic::Security::Agent.config.disable_security
|
95
100
|
Thread.new { NewRelic::Security::Agent.agent.reconnect(15) }
|
96
101
|
end
|
@@ -36,10 +36,11 @@ module NewRelic::Security
|
|
36
36
|
decrypted_data.split(COMMA).each do |filename|
|
37
37
|
begin
|
38
38
|
filename.gsub!(NR_CSEC_VALIDATOR_HOME_TMP, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
39
|
+
filename.gsub!(NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED, NewRelic::Security::Agent.config[:fuzz_dir_path])
|
39
40
|
filename.gsub!(NR_CSEC_VALIDATOR_FILE_SEPARATOR, ::File::SEPARATOR)
|
40
41
|
dirname = ::File.dirname(filename)
|
41
42
|
::FileUtils.mkdir_p(dirname, :mode => 0770) unless ::File.directory?(dirname)
|
42
|
-
ctxt&.fuzz_files
|
43
|
+
ctxt&.fuzz_files&.<< filename
|
43
44
|
::File.open(filename, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |fd|
|
44
45
|
# puts "Ownership acquired by : #{Process.pid}"
|
45
46
|
end unless ::File.exist?(filename)
|
@@ -115,7 +116,7 @@ module NewRelic::Security
|
|
115
116
|
elsif framework == :grape
|
116
117
|
ObjectSpace.each_object(::Grape::Endpoint) { |z|
|
117
118
|
z.instance_variable_get(:@routes)&.each { |route|
|
118
|
-
http_method = route.instance_variable_get(:@request_method)
|
119
|
+
http_method = route.instance_variable_get(:@request_method) || route.instance_variable_get(:@options)[:method]
|
119
120
|
NewRelic::Security::Agent.agent.route_map << "#{http_method}@#{route.pattern.origin}"
|
120
121
|
}
|
121
122
|
}
|
@@ -7,6 +7,7 @@ module NewRelic::Security
|
|
7
7
|
LANGUAGE_COLLECTOR = 'LANGUAGE_COLLECTOR'
|
8
8
|
STANDARD_OUT = 'STDOUT'
|
9
9
|
NR_CSEC_VALIDATOR_HOME_TMP = '{{NR_CSEC_VALIDATOR_HOME_TMP}}'
|
10
|
+
NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED = '%7B%7BNR_CSEC_VALIDATOR_HOME_TMP%7D%7D'
|
10
11
|
NR_CSEC_VALIDATOR_FILE_SEPARATOR = '{{NR_CSEC_VALIDATOR_FILE_SEPARATOR}}'
|
11
12
|
SEC_HOME_PATH = 'nr-security-home'
|
12
13
|
LOGS_DIR = 'logs'
|
@@ -12,10 +12,15 @@ module NewRelic::Security
|
|
12
12
|
|
13
13
|
def call(env)
|
14
14
|
retval = nil
|
15
|
-
event = call_on_enter(env)
|
15
|
+
event = call_on_enter(env) do
|
16
|
+
begin
|
17
|
+
retval = call_without_security(env)
|
18
|
+
ensure
|
19
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
20
|
+
end
|
21
|
+
end
|
16
22
|
call_on_exit(event, retval) { return retval }
|
17
23
|
end
|
18
|
-
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -40,7 +41,7 @@ module NewRelic::Security
|
|
40
41
|
def prepare_env_from_route_on_enter(route)
|
41
42
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
42
43
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
43
|
-
http_method = route.instance_variable_get(:@request_method)
|
44
|
+
http_method = route.instance_variable_get(:@request_method) || route.instance_variable_get(:@options)[:method]
|
44
45
|
ctxt.route = "#{http_method}@#{route.options[:namespace]}" unless ctxt.nil?
|
45
46
|
rescue => exception
|
46
47
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -8,7 +8,13 @@ module NewRelic::Security
|
|
8
8
|
|
9
9
|
def call(env)
|
10
10
|
retval = nil
|
11
|
-
event = call_on_enter(env)
|
11
|
+
event = call_on_enter(env) do
|
12
|
+
begin
|
13
|
+
retval = super(env)
|
14
|
+
ensure
|
15
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
16
|
+
end
|
17
|
+
end
|
12
18
|
call_on_exit(event, retval) { return retval }
|
13
19
|
end
|
14
20
|
|
@@ -21,6 +21,23 @@ module NewRelic::Security
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
module Router
|
26
|
+
module Chain
|
27
|
+
def self.instrument!
|
28
|
+
::Padrino::Router.class_eval do
|
29
|
+
include NewRelic::Security::Instrumentation::Padrino::Router
|
30
|
+
|
31
|
+
alias_method :call_without_security, :call
|
32
|
+
|
33
|
+
def call(env, &block)
|
34
|
+
retval = call_without_security(env, &block)
|
35
|
+
call_on_exit(retval) { return retval }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
24
41
|
end
|
25
42
|
end
|
26
43
|
end
|
@@ -13,7 +13,7 @@ module NewRelic::Security
|
|
13
13
|
extracted_env = env.instance_variable_get(:@env)
|
14
14
|
NewRelic::Security::Agent::Control::HTTPContext.set_context(extracted_env)
|
15
15
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
16
|
-
ctxt.route = "#{extracted_env[REQUEST_METHOD]
|
16
|
+
ctxt.route = "#{extracted_env[REQUEST_METHOD]}@#{extracted_env[PATH_INFO]}" if ctxt
|
17
17
|
NewRelic::Security::Agent::Utils.parse_fuzz_header(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
18
18
|
rescue => exception
|
19
19
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -27,6 +27,7 @@ module NewRelic::Security
|
|
27
27
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
28
28
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
29
29
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
30
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
30
31
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
31
32
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
32
33
|
rescue => exception
|
@@ -34,9 +35,20 @@ module NewRelic::Security
|
|
34
35
|
ensure
|
35
36
|
yield
|
36
37
|
end
|
37
|
-
|
38
|
+
end
|
39
|
+
|
40
|
+
module Padrino::Router
|
41
|
+
def call_on_exit(retval)
|
42
|
+
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
43
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
44
|
+
rescue => exception
|
45
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
46
|
+
ensure
|
47
|
+
yield
|
48
|
+
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
|
42
53
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:padrino, ::Padrino::PathRouter::Router, ::NewRelic::Security::Instrumentation::Padrino::PathRouter::Router)
|
54
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:padrino, ::Padrino::Router, ::NewRelic::Security::Instrumentation::Padrino::Router)
|
@@ -15,6 +15,18 @@ module NewRelic::Security
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
module Router
|
20
|
+
module Prepend
|
21
|
+
include NewRelic::Security::Instrumentation::Padrino::Router
|
22
|
+
|
23
|
+
def call(env, &block)
|
24
|
+
retval = super
|
25
|
+
call_on_exit(retval) { return retval }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
18
30
|
end
|
19
31
|
end
|
20
32
|
end
|
@@ -29,12 +29,36 @@ module NewRelic::Security
|
|
29
29
|
include NewRelic::Security::Instrumentation::ActionDispatch::Journey::Router
|
30
30
|
|
31
31
|
alias_method :find_routes_without_security, :find_routes
|
32
|
-
|
32
|
+
|
33
33
|
def find_routes(req)
|
34
34
|
retval = nil
|
35
35
|
event = find_routes_on_enter(req) { retval = find_routes_without_security(req) }
|
36
36
|
find_routes_on_exit(event, retval) { return retval }
|
37
|
-
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ActionDispatch
|
46
|
+
module Routing
|
47
|
+
module RouteSet
|
48
|
+
module Dispatcher
|
49
|
+
module Chain
|
50
|
+
def self.instrument!
|
51
|
+
::ActionDispatch::Routing::RouteSet::Dispatcher.class_eval do
|
52
|
+
include NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher
|
53
|
+
|
54
|
+
alias_method :serve_without_security, :serve
|
55
|
+
|
56
|
+
def serve(req)
|
57
|
+
retval = nil
|
58
|
+
event = serve_on_enter(req) { retval = serve_without_security(req) }
|
59
|
+
serve_on_exit(event, retval) { return retval }
|
60
|
+
end
|
61
|
+
end
|
38
62
|
end
|
39
63
|
end
|
40
64
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -31,11 +32,11 @@ module NewRelic::Security
|
|
31
32
|
ensure
|
32
33
|
yield
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
end
|
36
37
|
|
37
38
|
module ActionDispatch::Journey::Router
|
38
|
-
|
39
|
+
|
39
40
|
def find_routes_on_enter(req)
|
40
41
|
event = nil
|
41
42
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
@@ -45,7 +46,7 @@ module NewRelic::Security
|
|
45
46
|
yield
|
46
47
|
return event
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
def find_routes_on_exit(event, retval)
|
50
51
|
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
51
52
|
|
@@ -60,8 +61,32 @@ module NewRelic::Security
|
|
60
61
|
yield
|
61
62
|
end
|
62
63
|
end
|
64
|
+
|
65
|
+
module ActionDispatch::Routing::RouteSet::Dispatcher
|
66
|
+
|
67
|
+
def serve_on_enter(req)
|
68
|
+
event = nil
|
69
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
70
|
+
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
71
|
+
ctxt.route = "#{ctxt.method}@#{req.route_uri_pattern.to_s.gsub(/\(\.:format\)/, EMPTY_STRING)}" if ctxt && req.respond_to?(:route_uri_pattern)
|
72
|
+
rescue => exception
|
73
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
74
|
+
ensure
|
75
|
+
yield
|
76
|
+
return event
|
77
|
+
end
|
78
|
+
|
79
|
+
def serve_on_exit(event, retval)
|
80
|
+
NewRelic::Security::Agent.logger.debug "OnExit : #{self.class}.#{__method__}"
|
81
|
+
rescue => exception
|
82
|
+
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
83
|
+
ensure
|
84
|
+
yield
|
85
|
+
end
|
86
|
+
end
|
63
87
|
end
|
64
88
|
end
|
65
89
|
|
66
90
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::Rails::Engine, ::NewRelic::Security::Instrumentation::Rails::Engine)
|
67
91
|
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::ActionDispatch::Journey::Router, ::NewRelic::Security::Instrumentation::ActionDispatch::Journey::Router)
|
92
|
+
NewRelic::Security::Instrumentation::InstrumentationLoader.install_instrumentation(:rails, ::ActionDispatch::Routing::RouteSet::Dispatcher, ::NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher)
|
@@ -29,5 +29,23 @@ module NewRelic::Security
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
module ActionDispatch
|
34
|
+
module Routing
|
35
|
+
module RouteSet
|
36
|
+
module Dispatcher
|
37
|
+
module Prepend
|
38
|
+
include NewRelic::Security::Instrumentation::ActionDispatch::Routing::RouteSet::Dispatcher
|
39
|
+
|
40
|
+
def serve(req)
|
41
|
+
retval = nil
|
42
|
+
event = serve_on_enter(req) { retval = super }
|
43
|
+
serve_on_exit(event, retval) { return retval }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
32
50
|
end
|
33
51
|
end
|
@@ -10,10 +10,15 @@ module NewRelic::Security
|
|
10
10
|
|
11
11
|
def _roda_handle_main_route(*args)
|
12
12
|
retval = nil
|
13
|
-
event = _roda_handle_main_route_on_enter(
|
13
|
+
event = _roda_handle_main_route_on_enter(env) do
|
14
|
+
begin
|
15
|
+
retval = _roda_handle_main_route_without_security(*args)
|
16
|
+
ensure
|
17
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
18
|
+
end
|
19
|
+
end
|
14
20
|
_roda_handle_main_route_on_exit(event, retval) { return retval }
|
15
21
|
end
|
16
|
-
|
17
22
|
end
|
18
23
|
end
|
19
24
|
end
|
@@ -12,7 +12,7 @@ module NewRelic::Security
|
|
12
12
|
NewRelic::Security::Agent::Utils.get_app_routes(:roda) if NewRelic::Security::Agent.agent.route_map.empty?
|
13
13
|
NewRelic::Security::Agent::Control::HTTPContext.set_context(env)
|
14
14
|
ctxt = NewRelic::Security::Agent::Control::HTTPContext.get_context
|
15
|
-
ctxt.route = "#{env[REQUEST_METHOD]
|
15
|
+
ctxt.route = "#{env[REQUEST_METHOD]}@#{env[PATH_INFO]}" if ctxt
|
16
16
|
NewRelic::Security::Agent::Utils.parse_fuzz_header(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
17
17
|
rescue => exception
|
18
18
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
@@ -26,6 +26,7 @@ module NewRelic::Security
|
|
26
26
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
27
27
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
28
28
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
29
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
29
30
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
30
31
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
31
32
|
rescue => exception
|
@@ -6,7 +6,13 @@ module NewRelic::Security
|
|
6
6
|
|
7
7
|
def _roda_handle_main_route(*args)
|
8
8
|
retval = nil
|
9
|
-
event = _roda_handle_main_route_on_enter(
|
9
|
+
event = _roda_handle_main_route_on_enter(env) do
|
10
|
+
begin
|
11
|
+
retval = super
|
12
|
+
ensure
|
13
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, nil)
|
14
|
+
end
|
15
|
+
end
|
10
16
|
_roda_handle_main_route_on_exit(event, retval) { return retval }
|
11
17
|
end
|
12
18
|
|
@@ -20,6 +20,12 @@ module NewRelic::Security
|
|
20
20
|
def route_eval(&block)
|
21
21
|
route_eval_on_enter { route_eval_without_security(&block) }
|
22
22
|
end
|
23
|
+
|
24
|
+
alias_method :dispatch_without_security, :dispatch!
|
25
|
+
|
26
|
+
def dispatch!
|
27
|
+
dispatch_on_enter { dispatch_without_security }
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
@@ -24,6 +24,7 @@ module NewRelic::Security
|
|
24
24
|
# NewRelic::Security::Agent.logger.debug "\n\nHTTP Context : #{::NewRelic::Agent::Tracer.current_transaction.instance_variable_get(:@security_context_data).inspect}\n\n"
|
25
25
|
NewRelic::Security::Agent::Control::ReflectedXSS.check_xss(NewRelic::Security::Agent::Control::HTTPContext.get_context, retval) if NewRelic::Security::Agent.config[:'security.detection.rxss.enabled']
|
26
26
|
NewRelic::Security::Agent::Utils.delete_created_files(NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
27
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context, retval[0])
|
27
28
|
NewRelic::Security::Agent::Control::HTTPContext.reset_context
|
28
29
|
NewRelic::Security::Agent.logger.debug "Exit event : #{event}"
|
29
30
|
rescue => exception
|
@@ -41,6 +42,13 @@ module NewRelic::Security
|
|
41
42
|
ensure
|
42
43
|
yield
|
43
44
|
end
|
45
|
+
|
46
|
+
def dispatch_on_enter
|
47
|
+
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
48
|
+
yield
|
49
|
+
ensure
|
50
|
+
NewRelic::Security::Agent.agent.error_reporting&.report_unhandled_or_5xx_exceptions(NewRelic::Security::Agent::Control::HTTPContext.get_current_transaction, NewRelic::Security::Agent::Control::HTTPContext.get_context)
|
51
|
+
end
|
44
52
|
|
45
53
|
end
|
46
54
|
end
|
@@ -10,7 +10,7 @@ module NewRelic::Security
|
|
10
10
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
11
11
|
hash = {}
|
12
12
|
hash[:sql] = sql
|
13
|
-
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.
|
13
|
+
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.flatten
|
14
14
|
hash[:parameters] = hash[:parameters] + args.map(&:to_s) unless args.empty?
|
15
15
|
event = NewRelic::Security::Agent::Control::Collector.collect(SQL_DB_COMMAND, [hash], SQLITE) unless NewRelic::Security::Instrumentation::InstrumentationUtils.sql_filter_events?(hash[:sql])
|
16
16
|
rescue => exception
|
@@ -34,7 +34,7 @@ module NewRelic::Security
|
|
34
34
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
35
35
|
hash = {}
|
36
36
|
hash[:sql] = sql
|
37
|
-
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.
|
37
|
+
hash[:parameters] = bind_vars.is_a?(String) ? [bind_vars] : bind_vars.flatten
|
38
38
|
hash[:parameters] = hash[:parameters] + args unless args.empty?
|
39
39
|
event = NewRelic::Security::Agent::Control::Collector.collect(SQL_DB_COMMAND, [hash], SQLITE) unless NewRelic::Security::Instrumentation::InstrumentationUtils.sql_filter_events?(hash[:sql])
|
40
40
|
rescue => exception
|
@@ -102,7 +102,7 @@ module NewRelic::Security
|
|
102
102
|
def bind_params_on_enter(*bind_vars)
|
103
103
|
event = nil
|
104
104
|
NewRelic::Security::Agent.logger.debug "OnEnter : #{self.class}.#{__method__}"
|
105
|
-
NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[self.object_id][:parameters] = bind_vars.
|
105
|
+
NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[self.object_id][:parameters] = bind_vars.flatten if NewRelic::Security::Agent::Control::HTTPContext.get_context && NewRelic::Security::Agent::Control::HTTPContext.get_context.cache.key?(self.object_id)
|
106
106
|
rescue => exception
|
107
107
|
NewRelic::Security::Agent.logger.error "Exception in hook in #{self.class}.#{__method__}, #{exception.inspect}, #{exception.backtrace}"
|
108
108
|
ensure
|
@@ -129,7 +129,7 @@ module NewRelic::Security
|
|
129
129
|
else
|
130
130
|
hash = {}
|
131
131
|
hash[:sql] = NewRelic::Security::Agent::Control::HTTPContext.get_context.cache[key][:sql]
|
132
|
-
hash[:parameters] = bind_vars.
|
132
|
+
hash[:parameters] = bind_vars.flatten
|
133
133
|
ic_args.push(hash)
|
134
134
|
end
|
135
135
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: newrelic_security
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Prateek Sen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: newrelic_rpm
|
@@ -151,10 +151,12 @@ files:
|
|
151
151
|
- lib/newrelic_security/agent/configuration/server_source.rb
|
152
152
|
- lib/newrelic_security/agent/configuration/yaml_source.rb
|
153
153
|
- lib/newrelic_security/agent/control/app_info.rb
|
154
|
+
- lib/newrelic_security/agent/control/application_runtime_error.rb
|
154
155
|
- lib/newrelic_security/agent/control/application_url_mappings.rb
|
155
156
|
- lib/newrelic_security/agent/control/collector.rb
|
156
157
|
- lib/newrelic_security/agent/control/control_command.rb
|
157
158
|
- lib/newrelic_security/agent/control/critical_message.rb
|
159
|
+
- lib/newrelic_security/agent/control/error_reporting.rb
|
158
160
|
- lib/newrelic_security/agent/control/event.rb
|
159
161
|
- lib/newrelic_security/agent/control/event_counter.rb
|
160
162
|
- lib/newrelic_security/agent/control/event_processor.rb
|