newrelic_security 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/pr_ci.yml +4 -4
  3. data/.github/workflows/release.yml +1 -1
  4. data/.github/workflows/rubocop.yml +1 -1
  5. data/CHANGELOG.md +90 -1
  6. data/Gemfile_test +3 -0
  7. data/README.md +1 -0
  8. data/THIRD_PARTY_NOTICES.md +8 -0
  9. data/lib/newrelic_security/agent/agent.rb +22 -4
  10. data/lib/newrelic_security/agent/configuration/manager.rb +65 -7
  11. data/lib/newrelic_security/agent/control/application_runtime_error.rb +1 -1
  12. data/lib/newrelic_security/agent/control/collector.rb +41 -4
  13. data/lib/newrelic_security/agent/control/control_command.rb +2 -3
  14. data/lib/newrelic_security/agent/control/error_reporting.rb +8 -6
  15. data/lib/newrelic_security/agent/control/event.rb +15 -1
  16. data/lib/newrelic_security/agent/control/event_processor.rb +25 -14
  17. data/lib/newrelic_security/agent/control/event_subscriber.rb +6 -8
  18. data/lib/newrelic_security/agent/control/health_check.rb +4 -0
  19. data/lib/newrelic_security/agent/control/http_context.rb +10 -6
  20. data/lib/newrelic_security/agent/control/iast_client.rb +24 -11
  21. data/lib/newrelic_security/agent/control/reflected_xss.rb +3 -4
  22. data/lib/newrelic_security/agent/control/scan_scheduler.rb +77 -0
  23. data/lib/newrelic_security/agent/control/websocket_client.rb +71 -16
  24. data/lib/newrelic_security/agent/utils/agent_utils.rb +25 -17
  25. data/lib/newrelic_security/constants.rb +1 -2
  26. data/lib/newrelic_security/instrumentation-security/async-http/instrumentation.rb +2 -13
  27. data/lib/newrelic_security/instrumentation-security/curb/instrumentation.rb +1 -14
  28. data/lib/newrelic_security/instrumentation-security/ethon/chain.rb +0 -6
  29. data/lib/newrelic_security/instrumentation-security/ethon/instrumentation.rb +7 -42
  30. data/lib/newrelic_security/instrumentation-security/ethon/prepend.rb +0 -4
  31. data/lib/newrelic_security/instrumentation-security/excon/instrumentation.rb +3 -13
  32. data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +1 -0
  33. data/lib/newrelic_security/instrumentation-security/graphql/chain.rb +26 -0
  34. data/lib/newrelic_security/instrumentation-security/graphql/instrumentation.rb +28 -0
  35. data/lib/newrelic_security/instrumentation-security/graphql/prepend.rb +18 -0
  36. data/lib/newrelic_security/instrumentation-security/grpc/server/instrumentation.rb +3 -2
  37. data/lib/newrelic_security/instrumentation-security/httpclient/instrumentation.rb +4 -28
  38. data/lib/newrelic_security/instrumentation-security/httprb/instrumentation.rb +1 -12
  39. data/lib/newrelic_security/instrumentation-security/httpx/instrumentation.rb +1 -15
  40. data/lib/newrelic_security/instrumentation-security/instrumentation_utils.rb +0 -17
  41. data/lib/newrelic_security/instrumentation-security/io/chain.rb +2 -2
  42. data/lib/newrelic_security/instrumentation-security/io/prepend.rb +1 -1
  43. data/lib/newrelic_security/instrumentation-security/net_http/instrumentation.rb +6 -23
  44. data/lib/newrelic_security/instrumentation-security/net_ldap/instrumentation.rb +1 -1
  45. data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +1 -0
  46. data/lib/newrelic_security/instrumentation-security/patron/instrumentation.rb +2 -15
  47. data/lib/newrelic_security/instrumentation-security/rack/chain.rb +24 -0
  48. data/lib/newrelic_security/instrumentation-security/rack/instrumentation.rb +44 -0
  49. data/lib/newrelic_security/instrumentation-security/rack/prepend.rb +18 -0
  50. data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +1 -0
  51. data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +1 -0
  52. data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +1 -0
  53. data/lib/newrelic_security/newrelic-security-api/api.rb +1 -1
  54. data/lib/newrelic_security/parse-cron/cron_parser.rb +294 -0
  55. data/lib/newrelic_security/version.rb +1 -1
  56. data/lib/newrelic_security/websocket-client-simple/client.rb +5 -1
  57. data/newrelic_security.gemspec +1 -1
  58. metadata +15 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b1160c4b821177b6ce0400848a72bf09899780ff83abadb60b3e60f35b8338b
4
- data.tar.gz: 98cfb5a7d513e842690dce03aa515a7e148a252a5f0666f16e40899e2ad511d0
3
+ metadata.gz: 90213be8f4f16d3ac2402f570f734466927d341581a73e3382f8d843d0a4486e
4
+ data.tar.gz: a329513f2be86620ecccd38f2811b842b897028c06027e47930b3dea4e8b7b4c
5
5
  SHA512:
6
- metadata.gz: 56dea910383ac11f0b87a29f304e0f5042336c142c1d8ca60a2cdaf15bf172102000e28e8f382fd99781afebd061a0693e3f5ff8ea9fba981cdc4de128abdc20
7
- data.tar.gz: 04bb28178707141c66ac94158fdb3cd632ccb22a1a9c4b4f5f7d9438989ee3c6fba2124abc89ffbf135d34e249b5921cd5d4b924e50768f07363b219567867bb
6
+ metadata.gz: 565fbe75ccb52096f5e1e1549f5dfdea51f96435a2ee0a8819723e1c996c731f6090a6edb3bb1673318a9273288b031a3645e7d64f81a56cd0212181d64647e4
7
+ data.tar.gz: d176f0228f7f448e2fd08abd28906f59c4e0cac88abcd41bd7a2c02b30c427a78c990cc08a41d3626d5e4ec1cb62809cc2d029bcce7b8f88aa20736c883373c6
@@ -22,7 +22,7 @@ jobs:
22
22
  run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libcurl4-nss-dev libxslt1-dev libc6-dev openjdk-11-jdk
23
23
 
24
24
  - name: Install Ruby ${{ matrix.ruby-version }}
25
- uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # tag v1.176.0
25
+ uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
26
26
  with:
27
27
  ruby-version: ${{ matrix.ruby-version }}
28
28
 
@@ -53,7 +53,7 @@ jobs:
53
53
  - name: Configure git
54
54
  run: 'git config --global init.defaultBranch main'
55
55
  - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # tag v4.1.2
56
- - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # tag v1.176.0
56
+ - uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
57
57
  with:
58
58
  ruby-version: '3.1'
59
59
  - run: bundle
@@ -72,6 +72,6 @@ jobs:
72
72
  with:
73
73
  token: ${{ secrets.GITHUB_TOKEN }}
74
74
  resultPath: lib/coverage_results/.last_run.json
75
- failedThreshold: 70
76
- failedThresholdBranch: 33
75
+ failedThreshold: 67
76
+ failedThresholdBranch: 25
77
77
 
@@ -16,7 +16,7 @@ jobs:
16
16
  with:
17
17
  fetch-depth: 0
18
18
 
19
- - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # tag v1.176.0
19
+ - uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
20
20
  with:
21
21
  ruby-version: 3.2
22
22
 
@@ -10,7 +10,7 @@ jobs:
10
10
  - name: Configure git
11
11
  run: 'git config --global init.defaultBranch main'
12
12
  - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # tag v4.1.2
13
- - uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # tag v1.176.0
13
+ - uses: ruby/setup-ruby@086ffb1a2090c870a3f881cc91ea83aa4243d408 # v1.195.0
14
14
  with:
15
15
  ruby-version: '3.3'
16
16
  - run: bundle
data/CHANGELOG.md CHANGED
@@ -1,10 +1,99 @@
1
1
  # New Relic Ruby Security Agent Release Notes
2
2
 
3
+ ## v0.4.0
4
+
5
+ Version 0.4.0 introuduces Rack framework support, GraphQL support & CI/CD integration as part of security. Updated json_version: **1.2.9**
6
+
7
+ - Feature: Rack framework support [PR#149](https://github.com/newrelic/csec-ruby-agent/pull/149)
8
+
9
+ - Feature: GraphQL support [PR#133](https://github.com/newrelic/csec-ruby-agent/pull/133)
10
+
11
+ - Feature: CI/CD integration support [PR#146](https://github.com/newrelic/csec-ruby-agent/pull/146)
12
+
13
+ - Bugfix: Fix for Agent is sending 404 http error in runtime error json [PR#147](https://github.com/newrelic/csec-ruby-agent/pull/147)
14
+
15
+ - Bugfix: Fix for Exception occurred in generating unhandled exception in Sinatra jruby app [PR#150](https://github.com/newrelic/csec-ruby-agent/pull/150)
16
+
17
+ - Bugfix: Fix for Do not send empty url endpoint json & Reconnect in random interval between 5-15 secs [PR#151](https://github.com/newrelic/csec-ruby-agent/pull/151)
18
+
19
+ - Bugfix: Fix for Wrong formated critical error message [PR#148](https://github.com/newrelic/csec-ruby-agent/pull/148)
20
+
21
+ - BugFix: Fix for Vulnerability is missed in text/json because of small stackTrace [PR#155](https://github.com/newrelic/csec-ruby-agent/pull/155)
22
+
23
+ - BugFix: Fix for API endpoints not detected in grape framework in JRuby environment [PR#158](https://github.com/newrelic/csec-ruby-agent/pull/158)
24
+
25
+ - BugFix: Fix for XML content type vulnerability not detected [PR#159](https://github.com/newrelic/csec-ruby-agent/pull/159)
26
+
27
+ - BugFix: Fix for Concurrency issue observed in JRuby environment, BufferOverflowException observed in writing events to websocket [PR#161](https://github.com/newrelic/csec-ruby-agent/pull/161)
28
+
29
+ - Memory usage optimisations [PR#153](https://github.com/newrelic/csec-ruby-agent/pull/153)
30
+
31
+ ## v0.3.0
32
+
33
+ Version 0.3.0 introduces more control on IAST scanning through new configs(exclude_from_iast_scan, scan_schedule & scan_controllers) and
34
+ features like API inventory for gRPC server and IAST scan start related timestamps.
35
+
36
+ Updated json_version: **1.2.8**
37
+
38
+ - Feature: IAST scan exclusion for apis, http request parameters(header, query & body) & IAST detection categories and scan scheduling through delay, duration & cron schedule. [PR#131](https://github.com/newrelic/csec-ruby-agent/pull/131)
39
+
40
+ - Feature: IAST scan request rate limit to control IAST scan request firing. [PR#132](https://github.com/newrelic/csec-ruby-agent/pull/132)
41
+
42
+ - Feature: API endpoints support for gRPC server applications. [PR#143](https://github.com/newrelic/csec-ruby-agent/pull/143)
43
+
44
+ - Feature: Reporting of IAST scanning application procStartTime, trafficStartedTime & scanStartTime. [PR#136](https://github.com/newrelic/csec-ruby-agent/pull/136)
45
+
46
+ - Misc Chore: Optimised SSRF events parameters to send only URL in parameters. [PR#129](https://github.com/newrelic/csec-ruby-agent/pull/129)
47
+
48
+ ##### New security configs
49
+
50
+ ```yaml
51
+ security:
52
+ exclude_from_iast_scan:
53
+ api: []
54
+ http_request_parameters:
55
+ header: []
56
+ query: []
57
+ body: []
58
+ iast_detection_category:
59
+ insecure_settings: false
60
+ invalid_file_access: false
61
+ sql_injection: false
62
+ nosql_injection: false
63
+ ldap_injection: false
64
+ javascript_injection: false
65
+ command_injection: false
66
+ xpath_injection: false
67
+ ssrf: false
68
+ rxss: false
69
+ scan_schedule:
70
+ delay: 0
71
+ duration: 0
72
+ schedule: ""
73
+ always_sample_traces: false
74
+ scan_controllers:
75
+ iast_scan_request_rate_limit: 3600
76
+ ```
77
+
78
+ ##### Deprecated security configs (will be removed in next major release v1.0.0)
79
+ ```yaml
80
+ security:
81
+ request:
82
+ body_limit: 300
83
+ detection:
84
+ rci:
85
+ enabled: true
86
+ rxss:
87
+ enabled: true
88
+ deserialization:
89
+ enabled: true
90
+ ```
91
+
3
92
  ## v0.2.0
4
93
 
5
94
  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
95
 
7
- - Feature: Unhandled and 5xx error reproting [PR#134](https://github.com/newrelic/csec-ruby-agent/pull/134)
96
+ - Feature: Unhandled and 5xx error reporting [PR#134](https://github.com/newrelic/csec-ruby-agent/pull/134)
8
97
 
9
98
  - Bugfix: Fix for API route not present in rails7 [PR#127](https://github.com/newrelic/csec-ruby-agent/pull/127)
10
99
 
data/Gemfile_test CHANGED
@@ -13,7 +13,9 @@ gem 'rubocop'
13
13
  gem 'rubocop-minitest'
14
14
  gem 'rubocop-rake'
15
15
  gem 'simplecov'
16
+ gem 'concurrent-ruby', '1.3.4'
16
17
  gem 'railties'
18
+ gem 'ffi', '1.15.5' if RUBY_VERSION < '3.1.0'
17
19
  if RUBY_VERSION >= '3.0.0'
18
20
  gem 'rails', '~>6.0.0'
19
21
  elsif RUBY_VERSION < '2.5.0'
@@ -23,6 +25,7 @@ else
23
25
  end
24
26
  gem 'loofah', '~> 2.19.0'
25
27
  gem 'sinatra'
28
+ gem 'tilt', '~> 2.4.0' if RUBY_VERSION < '2.5.0'
26
29
  gem 'padrino'
27
30
  gem 'grape'
28
31
  gem 'roda'
data/README.md CHANGED
@@ -46,6 +46,7 @@ The newrelic_security must be explicitly enabled in order to perform IAST analys
46
46
  - Padrino: 0.15 or higher
47
47
  - Grape: 1.2 or higher
48
48
  - Roda: 3.19 or higher
49
+ - Rack: 1.6 or higher
49
50
  - gRPC: 1 or higher
50
51
  ### Web servers
51
52
  - Puma: 3 or higher
@@ -34,3 +34,11 @@ Distributed under the following license(s):
34
34
 
35
35
  * [The MIT License](http://opensource.org/licenses/MIT)
36
36
 
37
+
38
+ ## [parse-cron](https://github.com/siebertm/parse-cron)
39
+
40
+ Copyright (C) 2013 Michael Siebert
41
+
42
+ Distributed under the following license(s):
43
+
44
+ * [The MIT License](http://opensource.org/licenses/MIT)
@@ -19,13 +19,14 @@ require 'newrelic_security/agent/control/event_stats'
19
19
  require 'newrelic_security/agent/control/exit_event'
20
20
  require 'newrelic_security/agent/control/application_runtime_error'
21
21
  require 'newrelic_security/agent/control/error_reporting'
22
+ require 'newrelic_security/agent/control/scan_scheduler'
22
23
  require 'newrelic_security/instrumentation-security/instrumentation_loader'
23
24
 
24
25
  module NewRelic::Security
25
26
  module Agent
26
27
  class Agent
27
28
 
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
29
+ 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, :scan_scheduler
29
30
 
30
31
  def initialize
31
32
  NewRelic::Security::Agent.config
@@ -44,6 +45,7 @@ module NewRelic::Security
44
45
  @rasp_event_stats = NewRelic::Security::Agent::Control::EventStats.new
45
46
  @exit_event_stats = NewRelic::Security::Agent::Control::EventStats.new
46
47
  @error_reporting = NewRelic::Security::Agent::Control::ErrorReporting.new
48
+ @scan_scheduler = NewRelic::Security::Agent::Control::ScanScheduler.new
47
49
  end
48
50
 
49
51
  def init
@@ -59,22 +61,38 @@ module NewRelic::Security
59
61
  NewRelic::Security::Agent.logger.error "Exception in security agent init: #{exception.inspect} #{exception.backtrace}\n"
60
62
  end
61
63
 
64
+ def shutdown_security_agent
65
+ NewRelic::Security::Agent.logger.info "Flushing eventQ (#{NewRelic::Security::Agent.agent.event_processor.eventQ.size} events) and closing websocket connection"
66
+ NewRelic::Security::Agent.agent.event_processor&.eventQ&.clear
67
+ @iast_client&.fuzzQ&.clear
68
+ @iast_client&.completed_requests&.clear
69
+ @iast_client&.pending_request_ids&.clear
70
+ @iast_client&.iast_data_transfer_request_processor_thread&.kill
71
+ NewRelic::Security::Agent.config.disable_security
72
+ stop_websocket_client_if_open
73
+ end
74
+
62
75
  def start_websocket_client
63
- NewRelic::Security::Agent::Control::WebsocketClient.instance.close(false) if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
76
+ stop_websocket_client_if_open
64
77
  @websocket_client = NewRelic::Security::Agent::Control::WebsocketClient.instance.connect
65
78
  end
66
79
 
80
+ def stop_websocket_client_if_open
81
+ NewRelic::Security::Agent::Control::WebsocketClient.instance.close(false) if NewRelic::Security::Agent::Control::WebsocketClient.instance.is_open?
82
+ end
83
+
67
84
  def start_event_processor
68
- @event_processor&.event_dequeue_thread&.kill
85
+ @event_processor&.event_dequeue_threads&.each { |t| t&.kill }
69
86
  @event_processor&.healthcheck_thread&.kill
70
87
  @event_processor = nil
71
88
  @event_processor = NewRelic::Security::Agent::Control::EventProcessor.new
72
89
  end
73
90
 
74
91
  def start_iast_client
75
- @iast_client&.iast_dequeue_threads&.each { |t| t.kill if t }
92
+ @iast_client&.iast_dequeue_threads&.each { |t| t&.kill }
76
93
  @iast_client&.iast_data_transfer_request_processor_thread&.kill
77
94
  @iast_client = nil
95
+ NewRelic::Security::Agent.logger.info "Starting IAST client now at current time: #{Time.now}"
78
96
  @iast_client = NewRelic::Security::Agent::Control::IASTClient.new
79
97
  end
80
98
 
@@ -27,19 +27,45 @@ module NewRelic::Security
27
27
  @cache[:log_level] = ::NewRelic::Agent.config[:log_level]
28
28
  @cache[:high_security] = ::NewRelic::Agent.config[:high_security]
29
29
  @cache[:'agent.enabled'] = ::NewRelic::Agent.config[:'security.agent.enabled']
30
- @cache[:enabled] = ::NewRelic::Agent.config[:'security.enabled']
30
+ @cache[:'security.enabled'] = ::NewRelic::Agent.config[:'security.enabled']
31
+ @cache[:enabled] = false
31
32
  @cache[:mode] = ::NewRelic::Agent.config[:'security.mode']
32
33
  @cache[:validator_service_url] = ::NewRelic::Agent.config[:'security.validator_service_url']
33
- @cache[:'security.detection.rci.enabled'] = ::NewRelic::Agent.config[:'security.detection.rci.enabled']
34
- @cache[:'security.detection.rxss.enabled'] = ::NewRelic::Agent.config[:'security.detection.rxss.enabled']
35
- @cache[:'security.detection.deserialization.enabled'] = ::NewRelic::Agent.config[:'security.detection.deserialization.enabled']
34
+ # TODO: Remove security.detection.* & security.request.body_limit in next major release
35
+ @cache[:'security.detection.rci.enabled'] = ::NewRelic::Agent.config[:'security.detection.rci.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.rci.enabled']
36
+ @cache[:'security.detection.rxss.enabled'] = ::NewRelic::Agent.config[:'security.detection.rxss.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.rxss.enabled']
37
+ @cache[:'security.detection.deserialization.enabled'] = ::NewRelic::Agent.config[:'security.detection.deserialization.enabled'].nil? ? true : ::NewRelic::Agent.config[:'security.detection.deserialization.enabled']
38
+ @cache[:'security.scan_controllers.iast_scan_request_rate_limit'] = ::NewRelic::Agent.config[:'security.scan_controllers.iast_scan_request_rate_limit'].to_i
36
39
  @cache[:framework] = detect_framework
40
+ @cache[:app_class] = detect_app_class if @cache[:framework] == :rack
37
41
  @cache[:'security.application_info.port'] = ::NewRelic::Agent.config[:'security.application_info.port'].to_i
38
- @cache[:'security.request.body_limit'] = ::NewRelic::Agent.config[:'security.request.body_limit'].to_i > 0 ? ::NewRelic::Agent.config[:'security.request.body_limit'].to_i : 300
39
42
  @cache[:listen_port] = nil
43
+ @cache[:process_start_time] = current_time_millis # TODO: Ruby doesn't provide process start time in pure ruby implementation using agent loading time for now.
44
+ @cache[:traffic_start_time] = nil
45
+ @cache[:scan_start_time] = nil
46
+ @cache[:'security.scan_controllers.scan_instance_count'] = ::NewRelic::Agent.config[:'security.scan_controllers.scan_instance_count']
47
+ @cache[:'security.iast_test_identifier'] = ::NewRelic::Agent.config[:'security.iast_test_identifier']
40
48
  @cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
41
49
  @cache[:jruby_objectspace_enabled] = false
42
- @cache[:json_version] = :'1.2.4'
50
+ @cache[:json_version] = :'1.2.9'
51
+ @cache[:'security.exclude_from_iast_scan.api'] = convert_to_regexp_list(::NewRelic::Agent.config[:'security.exclude_from_iast_scan.api'])
52
+ @cache[:'security.exclude_from_iast_scan.http_request_parameters.header'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.header']
53
+ @cache[:'security.exclude_from_iast_scan.http_request_parameters.query'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.query']
54
+ @cache[:'security.exclude_from_iast_scan.http_request_parameters.body'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.body']
55
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.insecure_settings'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.insecure_settings']
56
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access']
57
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection']
58
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection']
59
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection']
60
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.javascript_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.javascript_injection']
61
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.command_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.command_injection']
62
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection']
63
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.ssrf'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ssrf']
64
+ @cache[:'security.exclude_from_iast_scan.iast_detection_category.rxss'] = ::NewRelic::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.rxss']
65
+ @cache[:'security.scan_schedule.delay'] = ::NewRelic::Agent.config[:'security.scan_schedule.delay'].to_i
66
+ @cache[:'security.scan_schedule.duration'] = ::NewRelic::Agent.config[:'security.scan_schedule.duration'].to_i
67
+ @cache[:'security.scan_schedule.schedule'] = ::NewRelic::Agent.config[:'security.scan_schedule.schedule']
68
+ @cache[:'security.scan_schedule.always_sample_traces'] = ::NewRelic::Agent.config[:'security.scan_schedule.always_sample_traces']
43
69
 
44
70
  @environment_source = NewRelic::Security::Agent::Configuration::EnvironmentSource.new
45
71
  @server_source = NewRelic::Security::Agent::Configuration::ServerSource.new
@@ -93,6 +119,14 @@ module NewRelic::Security
93
119
  @cache[:listen_port] = listen_port
94
120
  end
95
121
 
122
+ def traffic_start_time=(traffic_start_time)
123
+ @cache[:traffic_start_time] = traffic_start_time
124
+ end
125
+
126
+ def scan_start_time=(scan_start_time)
127
+ @cache[:scan_start_time] = scan_start_time
128
+ end
129
+
96
130
  def app_server=(app_server)
97
131
  @cache[:app_server] = app_server
98
132
  end
@@ -122,6 +156,16 @@ module NewRelic::Security
122
156
  return :sinatra if defined?(::Sinatra)
123
157
  return :roda if defined?(::Roda)
124
158
  return :grape if defined?(::Grape)
159
+ return :rack if defined?(::Rack) && defined?(Rack::Builder)
160
+ end
161
+
162
+ def detect_app_class
163
+ target_class = nil
164
+ ObjectSpace.each_object(::Rack::Builder) do |z| target_class = z.instance_variable_get(:@run).target end
165
+ target_class
166
+ rescue StandardError => exception
167
+ NewRelic::Security::Agent.logger.error "Exception in detect_app_class : #{exception.inspect} #{exception.backtrace}"
168
+ nil
125
169
  end
126
170
 
127
171
  def generate_uuid
@@ -136,7 +180,8 @@ module NewRelic::Security
136
180
  end
137
181
  ::SecureRandom.uuid
138
182
  rescue Exception => exception
139
- NewRelic::Security::Agent.logger.error "Exception in generate_uuid : #{exception.inspect} #{exception.backtrace}"
183
+ NewRelic::Security::Agent.logger.warn "Error in generate_uuid, generating it through default approach : #{exception.inspect} #{exception.backtrace}"
184
+ ::SecureRandom.uuid
140
185
  end
141
186
 
142
187
  def create_uuid
@@ -171,6 +216,19 @@ module NewRelic::Security
171
216
  def generate_key(entity_guid)
172
217
  ::OpenSSL::PKCS5.pbkdf2_hmac(entity_guid, entity_guid[0..15], 1024, 32, SHA1)
173
218
  end
219
+
220
+ def current_time_millis
221
+ (Time.now.to_f * 1000).to_i
222
+ end
223
+
224
+ def convert_to_regexp_list(value_list)
225
+ value_list.map do |value|
226
+ next unless value && !value.empty?
227
+ value = "^#{value}" if value[0] != '^'
228
+ value = "#{value}$" if value[-1] != '$'
229
+ /#{value}/
230
+ end
231
+ end
174
232
  end
175
233
  end
176
234
  end
@@ -81,7 +81,7 @@ module NewRelic::Security
81
81
 
82
82
  def generate_trace_id(ctxt, category)
83
83
  @exception[:stackTrace]
84
- method, route = ctxt.route.split(AT_THE_RATE) if ctxt.route
84
+ method, route = ctxt.route.split(AT_THE_RATE) if ctxt&.route
85
85
  if @exception[:stackTrace]
86
86
  ::Digest::SHA256.hexdigest("#{@exception[:stackTrace].join(PIPE)}#{category}#{route}#{method}").to_s
87
87
  else
@@ -19,6 +19,8 @@ module NewRelic::Security
19
19
  def collect(case_type, args, event_category = nil, **keyword_args)
20
20
  return unless NewRelic::Security::Agent.config[:enabled]
21
21
  return if NewRelic::Security::Agent::Control::HTTPContext.get_context.nil? && NewRelic::Security::Agent::Control::GRPCContext.get_context.nil?
22
+ return if check_and_exclude_from_iast_scan_for_api
23
+ return if check_and_exclude_from_iast_scan_for_detection_category(case_type)
22
24
  args.map! { |file| Pathname.new(file).relative? ? File.join(Dir.pwd, file) : file } if [FILE_OPERATION, FILE_INTEGRITY].include?(case_type)
23
25
 
24
26
  event = NewRelic::Security::Agent::Control::Event.new(case_type, args, event_category)
@@ -43,8 +45,9 @@ module NewRelic::Security
43
45
  # In rails 5 method name keeps chaning for same api call (ex: _app_views_sqli_sqlinjectionattackcase_html_erb__1999281606898621405_2624809100).
44
46
  # Hence, considering only frame absolute_path & lineno for apiId calculation.
45
47
  user_frame_index = get_user_frame_index(stk)
48
+ route = route&.gsub(/\d+/, EMPTY_STRING) if NewRelic::Security::Agent.config[:framework] == :rack || NewRelic::Security::Agent.config[:framework] == :roda
46
49
  event.apiId = "#{case_type}-#{calculate_api_id(stk[0..user_frame_index].map { |frame| "#{frame.absolute_path}:#{frame.lineno}" }, event.httpRequest[:method], route)}"
47
- stk.delete_if {|frame| frame.path.match(/newrelic_security/) || frame.path.match(/new_relic/)}
50
+ stk.delete_if { |frame| frame.path.match?(/newrelic_security/) || frame.path.match?(/new_relic/) }
48
51
  user_frame_index = get_user_frame_index(stk)
49
52
  return if case_type != REFLECTED_XSS && user_frame_index == -1 # TODO: Add log message here: "Filtered because User Stk frame NOT FOUND \r\n"
50
53
  if user_frame_index != -1
@@ -58,9 +61,9 @@ module NewRelic::Security
58
61
  event.lineNumber = stk[0].lineno
59
62
  end
60
63
  event.stacktrace = stk[0..user_frame_index].map(&:to_s)
61
- NewRelic::Security::Agent.agent.event_processor.send_event(event)
62
- if event.httpRequest[:headers].key?(NR_CSEC_FUZZ_REQUEST_ID) && event.apiId == event.httpRequest[:headers][NR_CSEC_FUZZ_REQUEST_ID].split(COLON_IAST_COLON)[0]
63
- NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId] << event.id if NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId]
64
+ NewRelic::Security::Agent.agent.event_processor&.send_event(event)
65
+ if event.httpRequest[:headers].key?(NR_CSEC_FUZZ_REQUEST_ID) && event.apiId == event.httpRequest[:headers][NR_CSEC_FUZZ_REQUEST_ID].split(COLON_IAST_COLON)[0] && NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId]
66
+ NewRelic::Security::Agent.agent.iast_client.completed_requests[event.parentId] << event.id
64
67
  end
65
68
  event
66
69
  rescue Exception => exception
@@ -71,6 +74,10 @@ module NewRelic::Security
71
74
  else
72
75
  NewRelic::Security::Agent.agent.rasp_event_stats.error_count.increment
73
76
  end
77
+ ensure
78
+ event = nil
79
+ stk = nil
80
+ route = nil
74
81
  end
75
82
 
76
83
  private
@@ -78,6 +85,7 @@ module NewRelic::Security
78
85
  def get_user_frame_index(stk)
79
86
  return -1 if NewRelic::Security::Agent.config[:app_root].nil?
80
87
  stk.each_with_index do |val, index|
88
+ next if stk[index + 1] && stk[index + 1].path.start_with?(NewRelic::Security::Agent.config[:app_root])
81
89
  return index if val.path.start_with?(NewRelic::Security::Agent.config[:app_root])
82
90
  end
83
91
  return -1
@@ -111,6 +119,35 @@ module NewRelic::Security
111
119
  }
112
120
  end
113
121
 
122
+ def check_and_exclude_from_iast_scan_for_api
123
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.api'].each do |api|
124
+ return true if api&.match?(NewRelic::Security::Agent::Control::HTTPContext.get_context.url)
125
+ end
126
+ return false
127
+ end
128
+
129
+ def check_and_exclude_from_iast_scan_for_detection_category(case_type)
130
+ case case_type
131
+ when FILE_OPERATION, FILE_INTEGRITY
132
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.invalid_file_access']
133
+ when SQL_DB_COMMAND
134
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.sql_injection']
135
+ when NOSQL_DB_COMMAND
136
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.nosql_injection']
137
+ when LDAP
138
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ldap_injection']
139
+ when SYSTEM_COMMAND
140
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.command_injection']
141
+ when XPATH
142
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.xpath_injection']
143
+ when HTTP_REQUEST
144
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.ssrf']
145
+ when REFLECTED_XSS
146
+ NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.iast_detection_category.rxss']
147
+ else
148
+ false
149
+ end
150
+ end
114
151
  end
115
152
  end
116
153
  end
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'securerandom'
2
3
 
3
4
  module NewRelic::Security
4
5
  module Agent
@@ -49,8 +50,6 @@ module NewRelic::Security
49
50
  message_object[:arguments].each { |processed_id| NewRelic::Security::Agent.agent.iast_client.completed_requests.delete(processed_id) }
50
51
  when 100
51
52
  NewRelic::Security::Agent.logger.debug "Control command : '100', #{message_object.to_json}"
52
- ::NewRelic::Agent.instance.events.notify(:security_policy_received, message_object[:data])
53
- # TODO: Update policy from file here, if enabled.
54
53
  when 101
55
54
 
56
55
  when 102
@@ -95,7 +94,7 @@ module NewRelic::Security
95
94
  NewRelic::Security::Agent.agent.iast_client.completed_requests.clear if NewRelic::Security::Agent.agent.iast_client
96
95
  NewRelic::Security::Agent.agent.iast_client.pending_request_ids.clear if NewRelic::Security::Agent.agent.iast_client
97
96
  NewRelic::Security::Agent.config.disable_security
98
- Thread.new { NewRelic::Security::Agent.agent.reconnect(0) }
97
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(SecureRandom.random_number(10) + 5) }
99
98
  end
100
99
 
101
100
  def current_time_millis
@@ -40,22 +40,24 @@ module NewRelic::Security
40
40
  # TODO: when do refactoring of ctxt.route, use both route and method to generate key
41
41
  ctxt.route&.+ response_code.to_s
42
42
  else
43
- application_runtime_error.exception[:type] + application_runtime_error.exception[:stackTrace][0]
43
+ application_runtime_error.exception[:type]&.+ application_runtime_error.exception[:stackTrace]&.first
44
44
  end
45
+ return if key.nil? || key.empty?
45
46
  application_runtime_error.counter = @exceptions_map[key].counter + 1 if @exceptions_map.key?(key)
46
47
  @exceptions_map[key] = application_runtime_error
47
- rescue Exception => exception
48
+ rescue StandardError => exception
48
49
  NewRelic::Security::Agent.logger.error "Exception in generating unhandled exception: #{exception.inspect} #{exception.backtrace}\n"
49
50
  end
50
51
 
51
- def extract_noticed_error(current_transaction, ctxt, response_code)
52
+ def extract_noticed_error(current_transaction, ctxt, http_response_code)
53
+ return if http_response_code&.between?(400, 499)
52
54
  # TODO: Below operation is expensive, talk to APM to get optimized way to do this
53
55
  current_transaction.exceptions.each do |_, span|
54
56
  current_transaction.segments.each do |segment|
55
- generate_unhandled_exception(segment.noticed_error, ctxt, response_code) if span[:span_id] == segment.guid
57
+ generate_unhandled_exception(segment.noticed_error, ctxt, http_response_code) if span[:span_id] == segment.guid
56
58
  end
57
59
  end
58
- rescue Exception => exception
60
+ rescue StandardError => exception
59
61
  NewRelic::Security::Agent.logger.error "Exception in extract_noticed_error: #{exception.inspect} #{exception.backtrace}\n"
60
62
  end
61
63
 
@@ -64,7 +66,7 @@ module NewRelic::Security
64
66
  if current_transaction.exceptions.empty? && http_response_code&.between?(500, 599)
65
67
  generate_unhandled_exception(nil, ctxt, response_code)
66
68
  else
67
- extract_noticed_error(current_transaction, ctxt, response_code) unless current_transaction.exceptions.empty?
69
+ extract_noticed_error(current_transaction, ctxt, http_response_code) unless current_transaction.exceptions.empty?
68
70
  end
69
71
  end
70
72
 
@@ -30,7 +30,20 @@ module NewRelic::Security
30
30
  @appEntityGuid = NewRelic::Security::Agent.config[:entity_guid]
31
31
  @httpRequest = Hash.new
32
32
  @httpResponse = Hash.new
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] } }
33
+ @metaData = {
34
+ :reflectedMetaData => {
35
+ :listen_port => NewRelic::Security::Agent.config[:listen_port].to_s
36
+ },
37
+ :appServerInfo => {
38
+ :applicationDirectory => NewRelic::Security::Agent.config[:app_root],
39
+ :serverBaseDirectory => NewRelic::Security::Agent.config[:app_root]
40
+ },
41
+ :skipScanParameters => {
42
+ :header => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.header'],
43
+ :query => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.query'],
44
+ :body => NewRelic::Security::Agent.config[:'security.exclude_from_iast_scan.http_request_parameters.body']
45
+ }
46
+ }
34
47
  @linkingMetadata = add_linking_metadata
35
48
  @pid = pid
36
49
  @parameters = args
@@ -91,6 +104,7 @@ module NewRelic::Security
91
104
  http_request[:headers] = ctxt.headers
92
105
  http_request[:contentType] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
93
106
  http_request[:headers][CONTENT_TYPE1] = ctxt.req[CONTENT_TYPE] if ctxt.req.has_key?(CONTENT_TYPE)
107
+ http_request[:customDataType] = ctxt.custom_data_type
94
108
  http_request[:dataTruncated] = ctxt.data_truncated
95
109
  @httpRequest = http_request
96
110
  @metaData[:isClientDetectedFromXFF] = ctxt.headers.has_key?(X_FORWARDED_FOR) ? true : false