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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/lib/newrelic_security/agent/agent.rb +6 -2
  4. data/lib/newrelic_security/agent/configuration/manager.rb +2 -3
  5. data/lib/newrelic_security/agent/control/app_info.rb +2 -0
  6. data/lib/newrelic_security/agent/control/application_runtime_error.rb +95 -0
  7. data/lib/newrelic_security/agent/control/application_url_mappings.rb +2 -0
  8. data/lib/newrelic_security/agent/control/control_command.rb +2 -1
  9. data/lib/newrelic_security/agent/control/critical_message.rb +2 -0
  10. data/lib/newrelic_security/agent/control/error_reporting.rb +74 -0
  11. data/lib/newrelic_security/agent/control/event.rb +13 -4
  12. data/lib/newrelic_security/agent/control/event_processor.rb +21 -0
  13. data/lib/newrelic_security/agent/control/exit_event.rb +2 -0
  14. data/lib/newrelic_security/agent/control/grpc_context.rb +2 -1
  15. data/lib/newrelic_security/agent/control/health_check.rb +2 -0
  16. data/lib/newrelic_security/agent/control/http_context.rb +8 -2
  17. data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +2 -0
  18. data/lib/newrelic_security/agent/control/websocket_client.rb +5 -0
  19. data/lib/newrelic_security/agent/utils/agent_utils.rb +3 -2
  20. data/lib/newrelic_security/constants.rb +1 -0
  21. data/lib/newrelic_security/instrumentation-security/grape/chain.rb +7 -2
  22. data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +2 -1
  23. data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +7 -1
  24. data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +17 -0
  25. data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +14 -2
  26. data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +12 -0
  27. data/lib/newrelic_security/instrumentation-security/rails/chain.rb +26 -2
  28. data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +28 -3
  29. data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +18 -0
  30. data/lib/newrelic_security/instrumentation-security/roda/chain.rb +7 -2
  31. data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +2 -1
  32. data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +7 -1
  33. data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +6 -0
  34. data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +8 -0
  35. data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +4 -0
  36. data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +4 -4
  37. data/lib/newrelic_security/version.rb +1 -1
  38. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c07c9ef96a8884449bdd0d5be44eccf3fee5ae6dde565863fe1eb24449a902f6
4
- data.tar.gz: 56f813a8034eed9fd717a0c549275cd1c1e1386c50acdb2142379fbcc980838d
3
+ metadata.gz: 5b1160c4b821177b6ce0400848a72bf09899780ff83abadb60b3e60f35b8338b
4
+ data.tar.gz: 98cfb5a7d513e842690dce03aa515a7e148a252a5f0666f16e40899e2ad511d0
5
5
  SHA512:
6
- metadata.gz: 0a3b7c2e86f9ea2c3a6c4c28da88676f904e78c5cce82fb152897aade9acda32ca9ea485d26dfa7ebb6ef1356c14df9d85407971d05a0a0f69e1ec43824227a1
7
- data.tar.gz: d3c6dcd24fe5ed0b54537b503e6994e6ed6701d5bab53f90220cce4b300e14a619db19cab4befec08560b72c941898a082a1b3c9738ed29e3fb8feae714edeb2
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.0'
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
- # TODO: remove this puts once agent stablizes
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 = message_object[:reflectedMetaData]
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 = ctxt.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}:#{::Thread.current.object_id}:#{thread_monotonic_ctr}"
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
- ctxt.event_counter = ctxt.event_counter + 1 if ctxt
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 << filename
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) ? route.instance_variable_get(:@request_method) : route.instance_variable_get(:@options)[: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) { retval = call_without_security(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) ? route.instance_variable_get(:@request_method) : route.instance_variable_get(:@options)[: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) { retval = super(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].to_s}@#{extracted_env[PATH_INFO].to_s}" if ctxt
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(self.env) { retval = _roda_handle_main_route_without_security(*args) }
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].to_s}@#{env[PATH_INFO].to_s}" if ctxt
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(self.env) { retval = super }
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
@@ -14,6 +14,10 @@ module NewRelic::Security
14
14
  def route_eval
15
15
  route_eval_on_enter { super }
16
16
  end
17
+
18
+ def dispatch!
19
+ dispatch_on_enter { super }
20
+ end
17
21
  end
18
22
  end
19
23
  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.map(&:to_s)
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.map(&:to_s)
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.map(&:to_s) if NewRelic::Security::Agent::Control::HTTPContext.get_context && NewRelic::Security::Agent::Control::HTTPContext.get_context.cache.key?(self.object_id)
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.map(&:to_s)
132
+ hash[:parameters] = bind_vars.flatten
133
133
  ic_args.push(hash)
134
134
  end
135
135
  end
@@ -1,5 +1,5 @@
1
1
  module NewRelic
2
2
  module Security
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  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.1.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-07-29 00:00:00.000000000 Z
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