newrelic_security 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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