newrelic_security 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (205) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  3. data/.github/ISSUE_TEMPLATE/enhancement.md +27 -0
  4. data/.github/actions/simplecov-report/LICENSE +22 -0
  5. data/.github/actions/simplecov-report/README.md +36 -0
  6. data/.github/actions/simplecov-report/__tests__/.keep +0 -0
  7. data/.github/actions/simplecov-report/__tests__/main.test.ts +3 -0
  8. data/.github/actions/simplecov-report/action.yml +25 -0
  9. data/.github/actions/simplecov-report/dist/index.js +10238 -0
  10. data/.github/actions/simplecov-report/dummy_coverage/.last_run.json +5 -0
  11. data/.github/actions/simplecov-report/jest.config.js +11 -0
  12. data/.github/actions/simplecov-report/package.json +51 -0
  13. data/.github/actions/simplecov-report/src/main.ts +54 -0
  14. data/.github/actions/simplecov-report/src/report.ts +28 -0
  15. data/.github/actions/simplecov-report/tsconfig.json +12 -0
  16. data/.github/workflows/pr_ci.yml +77 -0
  17. data/.github/workflows/release.yml +51 -0
  18. data/.github/workflows/repolinter.yml +31 -0
  19. data/.github/workflows/rubocop.yml +17 -0
  20. data/.github/workflows/scripts/rubygems-authenticate.py +13 -0
  21. data/.github/workflows/scripts/rubygems-publish.rb +33 -0
  22. data/.gitignore +72 -0
  23. data/.rubocop.yml +9 -0
  24. data/.rubocop_todo.yml +1414 -0
  25. data/.simplecov +16 -0
  26. data/CHANGELOG.md +69 -0
  27. data/CONTRIBUTING.md +22 -0
  28. data/Gemfile +6 -0
  29. data/Gemfile_test +58 -0
  30. data/LICENSE +43 -0
  31. data/README.md +133 -0
  32. data/README_agent.md +44 -0
  33. data/Rakefile +28 -0
  34. data/THIRD_PARTY_NOTICES.md +36 -0
  35. data/lib/newrelic_security/agent/agent.rb +109 -0
  36. data/lib/newrelic_security/agent/configuration/default_source.rb +8 -0
  37. data/lib/newrelic_security/agent/configuration/environment_source.rb +8 -0
  38. data/lib/newrelic_security/agent/configuration/manager.rb +178 -0
  39. data/lib/newrelic_security/agent/configuration/manual_source.rb +8 -0
  40. data/lib/newrelic_security/agent/configuration/server_source.rb +8 -0
  41. data/lib/newrelic_security/agent/configuration/yaml_source.rb +8 -0
  42. data/lib/newrelic_security/agent/control/app_info.rb +132 -0
  43. data/lib/newrelic_security/agent/control/application_url_mappings.rb +66 -0
  44. data/lib/newrelic_security/agent/control/collector.rb +117 -0
  45. data/lib/newrelic_security/agent/control/control_command.rb +117 -0
  46. data/lib/newrelic_security/agent/control/critical_message.rb +58 -0
  47. data/lib/newrelic_security/agent/control/event.rb +149 -0
  48. data/lib/newrelic_security/agent/control/event_counter.rb +28 -0
  49. data/lib/newrelic_security/agent/control/event_processor.rb +134 -0
  50. data/lib/newrelic_security/agent/control/event_stats.rb +26 -0
  51. data/lib/newrelic_security/agent/control/event_subscriber.rb +28 -0
  52. data/lib/newrelic_security/agent/control/exit_event.rb +38 -0
  53. data/lib/newrelic_security/agent/control/fuzz_request.rb +18 -0
  54. data/lib/newrelic_security/agent/control/grpc_context.rb +57 -0
  55. data/lib/newrelic_security/agent/control/health_check.rb +136 -0
  56. data/lib/newrelic_security/agent/control/http_context.rb +73 -0
  57. data/lib/newrelic_security/agent/control/iast_client.rb +151 -0
  58. data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +32 -0
  59. data/lib/newrelic_security/agent/control/reflected_xss.rb +258 -0
  60. data/lib/newrelic_security/agent/control/websocket_client.rb +131 -0
  61. data/lib/newrelic_security/agent/logging/init_logger.rb +91 -0
  62. data/lib/newrelic_security/agent/logging/logger.rb +92 -0
  63. data/lib/newrelic_security/agent/logging/null_logger.rb +21 -0
  64. data/lib/newrelic_security/agent/resources/cert.pem +50 -0
  65. data/lib/newrelic_security/agent/utils/agent_utils.rb +219 -0
  66. data/lib/newrelic_security/agent.rb +57 -0
  67. data/lib/newrelic_security/constants.rb +67 -0
  68. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/chain.rb +70 -0
  69. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/instrumentation.rb +187 -0
  70. data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/prepend.rb +54 -0
  71. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/chain.rb +60 -0
  72. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/instrumentation.rb +143 -0
  73. data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/prepend.rb +48 -0
  74. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/chain.rb +72 -0
  75. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/instrumentation.rb +187 -0
  76. data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/prepend.rb +54 -0
  77. data/lib/newrelic_security/instrumentation-security/async-http/chain.rb +21 -0
  78. data/lib/newrelic_security/instrumentation-security/async-http/instrumentation.rb +46 -0
  79. data/lib/newrelic_security/instrumentation-security/async-http/prepend.rb +16 -0
  80. data/lib/newrelic_security/instrumentation-security/curb/chain.rb +26 -0
  81. data/lib/newrelic_security/instrumentation-security/curb/instrumentation.rb +52 -0
  82. data/lib/newrelic_security/instrumentation-security/curb/prepend.rb +18 -0
  83. data/lib/newrelic_security/instrumentation-security/dir/chain.rb +42 -0
  84. data/lib/newrelic_security/instrumentation-security/dir/instrumentation.rb +102 -0
  85. data/lib/newrelic_security/instrumentation-security/dir/prepend.rb +28 -0
  86. data/lib/newrelic_security/instrumentation-security/ethon/chain.rb +53 -0
  87. data/lib/newrelic_security/instrumentation-security/ethon/instrumentation.rb +122 -0
  88. data/lib/newrelic_security/instrumentation-security/ethon/prepend.rb +39 -0
  89. data/lib/newrelic_security/instrumentation-security/excon/chain.rb +23 -0
  90. data/lib/newrelic_security/instrumentation-security/excon/instrumentation.rb +44 -0
  91. data/lib/newrelic_security/instrumentation-security/excon/prepend.rb +17 -0
  92. data/lib/newrelic_security/instrumentation-security/file/chain.rb +34 -0
  93. data/lib/newrelic_security/instrumentation-security/file/instrumentation.rb +62 -0
  94. data/lib/newrelic_security/instrumentation-security/file/prepend.rb +22 -0
  95. data/lib/newrelic_security/instrumentation-security/grape/chain.rb +42 -0
  96. data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +56 -0
  97. data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +30 -0
  98. data/lib/newrelic_security/instrumentation-security/grpc/client/chain.rb +47 -0
  99. data/lib/newrelic_security/instrumentation-security/grpc/client/instrumentation.rb +37 -0
  100. data/lib/newrelic_security/instrumentation-security/grpc/client/prepend.rb +36 -0
  101. data/lib/newrelic_security/instrumentation-security/grpc/server/chain.rb +62 -0
  102. data/lib/newrelic_security/instrumentation-security/grpc/server/instrumentation.rb +65 -0
  103. data/lib/newrelic_security/instrumentation-security/grpc/server/prepend.rb +46 -0
  104. data/lib/newrelic_security/instrumentation-security/httpclient/chain.rb +30 -0
  105. data/lib/newrelic_security/instrumentation-security/httpclient/instrumentation.rb +82 -0
  106. data/lib/newrelic_security/instrumentation-security/httpclient/prepend.rb +22 -0
  107. data/lib/newrelic_security/instrumentation-security/httprb/chain.rb +21 -0
  108. data/lib/newrelic_security/instrumentation-security/httprb/instrumentation.rb +44 -0
  109. data/lib/newrelic_security/instrumentation-security/httprb/prepend.rb +16 -0
  110. data/lib/newrelic_security/instrumentation-security/httpx/chain.rb +23 -0
  111. data/lib/newrelic_security/instrumentation-security/httpx/instrumentation.rb +51 -0
  112. data/lib/newrelic_security/instrumentation-security/httpx/prepend.rb +18 -0
  113. data/lib/newrelic_security/instrumentation-security/instrumentation_loader.rb +50 -0
  114. data/lib/newrelic_security/instrumentation-security/instrumentation_utils.rb +165 -0
  115. data/lib/newrelic_security/instrumentation-security/io/chain.rb +113 -0
  116. data/lib/newrelic_security/instrumentation-security/io/instrumentation.rb +300 -0
  117. data/lib/newrelic_security/instrumentation-security/io/prepend.rb +86 -0
  118. data/lib/newrelic_security/instrumentation-security/kernel/chain.rb +65 -0
  119. data/lib/newrelic_security/instrumentation-security/kernel/instrumentation.rb +167 -0
  120. data/lib/newrelic_security/instrumentation-security/kernel/prepend.rb +50 -0
  121. data/lib/newrelic_security/instrumentation-security/mongo/chain.rb +106 -0
  122. data/lib/newrelic_security/instrumentation-security/mongo/instrumentation.rb +273 -0
  123. data/lib/newrelic_security/instrumentation-security/mongo/prepend.rb +77 -0
  124. data/lib/newrelic_security/instrumentation-security/mysql2/chain.rb +53 -0
  125. data/lib/newrelic_security/instrumentation-security/mysql2/instrumentation.rb +84 -0
  126. data/lib/newrelic_security/instrumentation-security/mysql2/prepend.rb +37 -0
  127. data/lib/newrelic_security/instrumentation-security/net_http/chain.rb +21 -0
  128. data/lib/newrelic_security/instrumentation-security/net_http/instrumentation.rb +60 -0
  129. data/lib/newrelic_security/instrumentation-security/net_http/prepend.rb +16 -0
  130. data/lib/newrelic_security/instrumentation-security/net_ldap/chain.rb +21 -0
  131. data/lib/newrelic_security/instrumentation-security/net_ldap/instrumentation.rb +42 -0
  132. data/lib/newrelic_security/instrumentation-security/net_ldap/prepend.rb +16 -0
  133. data/lib/newrelic_security/instrumentation-security/nokogiri/chain.rb +46 -0
  134. data/lib/newrelic_security/instrumentation-security/nokogiri/instrumentation.rb +36 -0
  135. data/lib/newrelic_security/instrumentation-security/nokogiri/prepend.rb +31 -0
  136. data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +26 -0
  137. data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +42 -0
  138. data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +20 -0
  139. data/lib/newrelic_security/instrumentation-security/patron/chain.rb +23 -0
  140. data/lib/newrelic_security/instrumentation-security/patron/instrumentation.rb +50 -0
  141. data/lib/newrelic_security/instrumentation-security/patron/prepend.rb +18 -0
  142. data/lib/newrelic_security/instrumentation-security/pg/chain.rb +49 -0
  143. data/lib/newrelic_security/instrumentation-security/pg/instrumentation.rb +102 -0
  144. data/lib/newrelic_security/instrumentation-security/pg/prepend.rb +36 -0
  145. data/lib/newrelic_security/instrumentation-security/pty/chain.rb +31 -0
  146. data/lib/newrelic_security/instrumentation-security/pty/instrumentation.rb +52 -0
  147. data/lib/newrelic_security/instrumentation-security/pty/prepend.rb +22 -0
  148. data/lib/newrelic_security/instrumentation-security/rails/chain.rb +46 -0
  149. data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +67 -0
  150. data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +33 -0
  151. data/lib/newrelic_security/instrumentation-security/roda/chain.rb +22 -0
  152. data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +41 -0
  153. data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +16 -0
  154. data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +29 -0
  155. data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +49 -0
  156. data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +21 -0
  157. data/lib/newrelic_security/instrumentation-security/sqlite3/chain.rb +79 -0
  158. data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +164 -0
  159. data/lib/newrelic_security/instrumentation-security/sqlite3/prepend.rb +56 -0
  160. data/lib/newrelic_security/newrelic-security-api/api.rb +72 -0
  161. data/lib/newrelic_security/version.rb +5 -0
  162. data/lib/newrelic_security/websocket-client-simple/client.rb +128 -0
  163. data/lib/newrelic_security/websocket-client-simple/event_emitter.rb +72 -0
  164. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/error.rb +129 -0
  165. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/exception_handler.rb +32 -0
  166. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/base.rb +62 -0
  167. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/data.rb +49 -0
  168. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/base.rb +41 -0
  169. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler03.rb +224 -0
  170. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler04.rb +18 -0
  171. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler05.rb +15 -0
  172. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler07.rb +78 -0
  173. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler75.rb +78 -0
  174. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler.rb +15 -0
  175. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/client.rb +17 -0
  176. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/server.rb +17 -0
  177. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming.rb +52 -0
  178. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/client.rb +17 -0
  179. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/server.rb +17 -0
  180. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing.rb +35 -0
  181. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame.rb +11 -0
  182. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/base.rb +142 -0
  183. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/client.rb +130 -0
  184. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/base.rb +49 -0
  185. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client.rb +32 -0
  186. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client01.rb +20 -0
  187. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client04.rb +63 -0
  188. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client11.rb +22 -0
  189. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client75.rb +39 -0
  190. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client76.rb +105 -0
  191. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server.rb +10 -0
  192. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server04.rb +56 -0
  193. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server75.rb +40 -0
  194. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server76.rb +75 -0
  195. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler.rb +21 -0
  196. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/server.rb +179 -0
  197. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake.rb +10 -0
  198. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/nice_inspect.rb +12 -0
  199. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/version.rb +5 -0
  200. data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket.rb +50 -0
  201. data/lib/newrelic_security.rb +6 -0
  202. data/lib/tasks/all.rb +8 -0
  203. data/lib/tasks/coverage_report.rake +27 -0
  204. data/newrelic_security.gemspec +51 -0
  205. metadata +342 -0
@@ -0,0 +1,178 @@
1
+ require 'securerandom'
2
+ require 'socket'
3
+ require 'openssl'
4
+ require 'newrelic_security/agent/configuration/default_source'
5
+ require 'newrelic_security/agent/configuration/environment_source'
6
+ require 'newrelic_security/agent/configuration/manual_source'
7
+ require 'newrelic_security/agent/configuration/server_source'
8
+ require 'newrelic_security/agent/configuration/yaml_source'
9
+
10
+ module NewRelic::Security
11
+ module Agent
12
+ module Configuration
13
+ class Manager
14
+ def initialize
15
+ @cache = Hash.new
16
+ @cache[:agent_run_id] = ::NewRelic::Agent.agent.service.agent_id
17
+ @cache[:linking_metadata] = ::NewRelic::Agent.linking_metadata
18
+ @cache[:app_name] = ::NewRelic::Agent.config[:app_name][0]
19
+ @cache[:entity_guid] = ::NewRelic::Agent.config[:entity_guid]
20
+ @cache[:license_key] = ::NewRelic::Agent.config[:license_key]
21
+ @cache[:policy] = Hash.new
22
+ @cache[:account_id] = nil
23
+ @cache[:application_id] = nil
24
+ @cache[:primary_application_id] = nil
25
+ @cache[:log_file_path] = ::NewRelic::Agent.config[:log_file_path]
26
+ @cache[:fuzz_dir_path] = ::File.join(::File.absolute_path(::NewRelic::Agent.config[:log_file_path]), SEC_HOME_PATH, TMP_DIR)
27
+ @cache[:log_level] = ::NewRelic::Agent.config[:log_level]
28
+ @cache[:high_security] = ::NewRelic::Agent.config[:high_security]
29
+ @cache[:'agent.enabled'] = ::NewRelic::Agent.config[:'security.agent.enabled']
30
+ @cache[:enabled] = ::NewRelic::Agent.config[:'security.enabled']
31
+ @cache[:mode] = ::NewRelic::Agent.config[:'security.mode']
32
+ @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']
36
+ @cache[:framework] = detect_framework
37
+ @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
+ @cache[:listen_port] = nil
40
+ @cache[:app_root] = NewRelic::Security::Agent::Utils.app_root
41
+ @cache[:jruby_objectspace_enabled] = false
42
+ @cache[:json_version] = :'1.2.0'
43
+
44
+ @environment_source = NewRelic::Security::Agent::Configuration::EnvironmentSource.new
45
+ @server_source = NewRelic::Security::Agent::Configuration::ServerSource.new
46
+ @manual_source = NewRelic::Security::Agent::Configuration::ManualSource.new
47
+ @yaml_source = NewRelic::Security::Agent::Configuration::YamlSource.new
48
+ @default_source = NewRelic::Security::Agent::Configuration::DefaultSource.new
49
+ rescue Exception => exception
50
+ # TODO: remove this puts once agent stablizes
51
+ puts "Exception in Configuration::Manager.initialize : #{exception.inspect} #{exception.backtrace}"
52
+ end
53
+
54
+ def [](key)
55
+ @cache[key]
56
+ end
57
+
58
+ def has_key?(key)
59
+ @cache.has_key?(key)
60
+ end
61
+
62
+ def keys
63
+ @cache.keys
64
+ end
65
+
66
+ def cache
67
+ @cache
68
+ end
69
+
70
+ def refresh
71
+ NewRelic::Security::Agent.logger.debug "refreshing agent config"
72
+ NewRelic::Security::Agent.config = NewRelic::Security::Agent::Configuration::Manager.new
73
+ # TODO: add validator received config also after the new, else collector#40 throws error
74
+ end
75
+
76
+ def save_uuid
77
+ @cache[:uuid] = generate_uuid
78
+ end
79
+
80
+ def update_server_config
81
+ @cache[:agent_run_id] = ::NewRelic::Agent.agent.service.agent_id
82
+ @cache[:linking_metadata] = ::NewRelic::Agent.linking_metadata
83
+ server_source = ::NewRelic::Agent.config.instance_variable_get(:@server_source) if defined?(::NewRelic::Agent)
84
+ @cache[:account_id] = server_source[:account_id]
85
+ @cache[:application_id] = server_source[:application_id]
86
+ @cache[:entity_guid] = server_source[:entity_guid]
87
+ @cache[:primary_application_id] = server_source[:primary_application_id]
88
+ @cache[:extraction_key] = generate_key(@cache[:entity_guid])
89
+ rescue Exception => exception
90
+ NewRelic::Security::Agent.logger.error "Exception in update_server_config : #{exception.inspect} #{exception.backtrace}"
91
+ end
92
+
93
+ def update_port=(listen_port)
94
+ @cache[:listen_port] = listen_port
95
+ end
96
+
97
+ def app_server=(app_server)
98
+ @cache[:app_server] = app_server
99
+ end
100
+
101
+ def jruby_objectspace_enabled=(jruby_objectspace_enabled)
102
+ @cache[:jruby_objectspace_enabled] = jruby_objectspace_enabled
103
+ end
104
+
105
+ def disable_security
106
+ @cache[:enabled] = false
107
+ NewRelic::Security::Agent.logger.info "Security Agent is now INACTIVE for #{NewRelic::Security::Agent.config[:uuid]}\n"
108
+ NewRelic::Security::Agent.init_logger.info "Security Agent is now INACTIVE for #{NewRelic::Security::Agent.config[:uuid]}\n"
109
+ end
110
+
111
+ def enable_security
112
+ @cache[:enabled] = true
113
+ NewRelic::Security::Agent.logger.info "Security Agent is now ACTIVE for #{NewRelic::Security::Agent.config[:uuid]}\n"
114
+ NewRelic::Security::Agent.init_logger.info "Security Agent is now ACTIVE for #{NewRelic::Security::Agent.config[:uuid]}\n"
115
+ NewRelic::Security::Agent.agent.event_processor.send_critical_message("Security Agent is now ACTIVE for #{NewRelic::Security::Agent.config[:uuid]}", "INFO", caller_locations[0].to_s, Thread.current.name, nil)
116
+ end
117
+
118
+ private
119
+
120
+ def detect_framework
121
+ return :rails if defined?(::Rails)
122
+ return :padrino if defined?(::Padrino)
123
+ return :sinatra if defined?(::Sinatra)
124
+ return :roda if defined?(::Roda)
125
+ return :grape if defined?(::Grape)
126
+ end
127
+
128
+ def generate_uuid
129
+ if defined?(::Puma::Cluster)
130
+ ObjectSpace.each_object(::Puma::Cluster) { |z| return fetch_or_create_uuid if !z.preload? && z.instance_variable_get(:@options)[:workers] >= 1 }
131
+ end
132
+ if defined?(::Unicorn::HttpServer)
133
+ ObjectSpace.each_object(::Unicorn::HttpServer) { |z| return fetch_or_create_uuid if !z.preload_app && z.worker_processes >= 1 }
134
+ end
135
+ if defined?(::PhusionPassenger::App) && ::PhusionPassenger::App.options[SPAWN_METHOD].match?(/#{DIRECT}/i)
136
+ return create_uuid
137
+ end
138
+ ::SecureRandom.uuid
139
+ rescue Exception => exception
140
+ NewRelic::Security::Agent.logger.error "Exception in generate_uuid : #{exception.inspect} #{exception.backtrace}"
141
+ end
142
+
143
+ def create_uuid
144
+ hostname = ::Socket.gethostname
145
+ ip_addr = Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address.to_s
146
+ process_identity = ::Gem.win_platform? ? ::Process.pid : ::Process.getpgrp
147
+ [hostname, ip_addr, process_identity].join(HYPHEN)
148
+ end
149
+
150
+ def fetch_or_create_uuid
151
+ process_identity = ::Gem.win_platform? ? ::Process.pid : ::Process.getpgrp
152
+ tmp_dir = ::File.join(@cache[:log_file_path], SEC_HOME_PATH, TMP_DIR)
153
+ if ::File.directory?(tmp_dir)
154
+ uuid_file_name = ::File.join(@cache[:log_file_path], SEC_HOME_PATH, TMP_DIR, process_identity.to_s)
155
+ else
156
+ ::FileUtils.mkdir_p(TMP_DIR) unless ::File.directory?(TMP_DIR)
157
+ uuid_file_name = ::File.join(TMP_DIR, process_identity.to_s)
158
+ end
159
+ if ::File.exist?(uuid_file_name)
160
+ sleep 0.01
161
+ return ::File.read(uuid_file_name)
162
+ end
163
+ File.open(uuid_file_name, 'w', 0644) {|f|
164
+ ret = f.flock(File::LOCK_EX|File::LOCK_NB)
165
+ f.write(::SecureRandom.uuid) if ret == 0
166
+ }
167
+ ::File.read(uuid_file_name)
168
+ rescue Exception => exception
169
+ NewRelic::Security::Agent.logger.error "Exception in uuid file creation : #{exception.inspect} #{exception.backtrace}"
170
+ end
171
+
172
+ def generate_key(entity_guid)
173
+ ::OpenSSL::PKCS5.pbkdf2_hmac(entity_guid, entity_guid[0..15], 1024, 32, SHA1)
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,8 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Configuration
4
+ class ManualSource
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Configuration
4
+ class ServerSource
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module NewRelic::Security
2
+ module Agent
3
+ module Configuration
4
+ class YamlSource
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+ require 'digest'
3
+ require 'json'
4
+
5
+ module NewRelic::Security
6
+ module Agent
7
+ module Control
8
+
9
+ PROC_SELF_EXE = '/proc/self/exe'
10
+ PROC_SELF_CMDLINE = '/proc/self/cmdline'
11
+ STATIC = 'STATIC'
12
+ COLON = ':'
13
+ RUBYLIB = 'RUBYLIB'
14
+ BACKSLASH000 = '\000'
15
+ KIND = 'kind'
16
+
17
+ class AppInfo
18
+ attr_reader :jsonName
19
+
20
+ def initialize
21
+ @collectorType = RUBY
22
+ @language = Ruby
23
+ @jsonName = :applicationinfo
24
+ @collectorVersion = NewRelic::Security::VERSION
25
+ @buildNumber = nil
26
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
27
+ @startTime = current_time_millis
28
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
29
+ @framework = NewRelic::Security::Agent.config[:framework]
30
+ @groupName = NewRelic::Security::Agent.config[:mode]
31
+ @userProvidedApplicationInfo = Hash.new
32
+ @policyVersion = nil
33
+ @userDir = nil
34
+ @libraryPath = library_path
35
+ @bootLibraryPath = EMPTY_STRING
36
+ @binaryName = binary_name
37
+ @binaryVersion = binary_version
38
+ @pid = pid
39
+ @cpid = cpid
40
+ @binaryPath = binary_path
41
+ @agentAttachmentType = STATIC
42
+ @sha256 = sha_256
43
+ @runCommand = run_command
44
+ @cmdline = [run_command]
45
+ @procStartTime = current_time_millis
46
+ @osArch = os_arch
47
+ @osName = os_name
48
+ @osVersion = os_version
49
+ @serverInfo = Hash.new # TODO: Fill this
50
+ @identifier = Hash.new # TODO: Fill this
51
+ @linkingMetadata = add_linking_metadata
52
+ end
53
+
54
+ def as_json
55
+ instance_variables.map! do |ivar|
56
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
57
+ end.to_h
58
+ end
59
+
60
+ def to_json
61
+ as_json.to_json
62
+ end
63
+
64
+ def update_app_info
65
+ @identifier[KIND] = 'HOST' # TODO: Added other identifier details
66
+ end
67
+
68
+ private
69
+
70
+ def current_time_millis
71
+ (Time.now.to_f * 1000).to_i
72
+ end
73
+
74
+ def library_path
75
+ ENV[RUBYLIB].split(COLON)
76
+ end
77
+
78
+ def binary_name
79
+ RUBY_ENGINE
80
+ end
81
+
82
+ def binary_version
83
+ RUBY_VERSION
84
+ end
85
+
86
+ def pid
87
+ return ::Process.pid if ::Gem.win_platform?
88
+ ::Process.getpgrp
89
+ end
90
+
91
+ def cpid
92
+ Process.pid
93
+ end
94
+
95
+ def binary_path
96
+ return ::File.realpath(PROC_SELF_EXE) if ::File.exist?(PROC_SELF_EXE)
97
+ nil
98
+ end
99
+
100
+ def sha_256
101
+ return ::Digest::SHA256.file(binary_path).hexdigest if binary_path
102
+ nil
103
+ end
104
+
105
+ def run_command
106
+ return ::File.read(PROC_SELF_CMDLINE).delete(BACKSLASH000) if File.exist?(PROC_SELF_CMDLINE)
107
+ $PROGRAM_NAME
108
+ end
109
+
110
+ def os_arch
111
+ ::Gem::Platform.local.cpu
112
+ end
113
+
114
+ def os_name
115
+ ::Gem::Platform.local.os
116
+ end
117
+
118
+ def os_version
119
+ ::Gem::Platform.local.version
120
+ end
121
+
122
+ def add_linking_metadata
123
+ linking_metadata = Hash.new
124
+ linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
125
+ linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
126
+ # TODO: add other fields as well in linking metadata, for event and heathcheck as well
127
+ end
128
+
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'json'
5
+
6
+ module NewRelic::Security
7
+ module Agent
8
+ module Control
9
+
10
+
11
+ class ApplicationURLMappings
12
+ attr_reader :jsonName
13
+
14
+ def initialize
15
+ @collectorType = RUBY
16
+ @language = Ruby
17
+ @jsonName = :'sec-application-url-mapping'
18
+ @eventType = :'sec-application-url-mapping'
19
+ @collectorVersion = NewRelic::Security::VERSION
20
+ @buildNumber = nil
21
+ @jsonVersion = NewRelic::Security::Agent.config[:json_version]
22
+ @timestamp = current_time_millis
23
+ @applicationUUID = NewRelic::Security::Agent.config[:uuid]
24
+ @framework = NewRelic::Security::Agent.config[:framework]
25
+ @groupName = NewRelic::Security::Agent.config[:mode]
26
+ @policyVersion = nil
27
+ @linkingMetadata = add_linking_metadata
28
+ @mappings = []
29
+ end
30
+
31
+ def as_json
32
+ instance_variables.map! do |ivar|
33
+ [ivar[1..-1].to_sym, instance_variable_get(ivar)]
34
+ end.to_h
35
+ end
36
+
37
+ def to_json
38
+ as_json.to_json
39
+ end
40
+
41
+ def update_application_url_mappings
42
+ maps = ::Set.new
43
+ NewRelic::Security::Agent.agent.route_map.each do |mapping|
44
+ method, path = mapping.split('@')
45
+ maps << { :method => method, :path => path }
46
+ end
47
+ @mappings = maps.to_a
48
+ end
49
+
50
+ private
51
+
52
+ def current_time_millis
53
+ (Time.now.to_f * 1000).to_i
54
+ end
55
+
56
+ def add_linking_metadata
57
+ linking_metadata = {}
58
+ linking_metadata[:agentRunId] = NewRelic::Security::Agent.config[:agent_run_id]
59
+ linking_metadata.merge!(NewRelic::Security::Agent.config[:linking_metadata])
60
+ # TODO: add other fields as well in linking metadata, for event and heathcheck as well
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+ require 'digest'
3
+ require 'pathname'
4
+
5
+ module NewRelic::Security
6
+ module Agent
7
+ module Control
8
+ module Collector
9
+
10
+ COVERAGE = 101
11
+ MARSHAL_RB = 'marshal.rb'
12
+ LOAD = 'load'
13
+ PSYCH_RB = 'psych.rb'
14
+ MARSHAL_LOAD = 'marshal_load'
15
+ EVAL = 'eval'
16
+
17
+ extend self
18
+
19
+ def collect(case_type, args, event_category = nil, **keyword_args)
20
+ return unless NewRelic::Security::Agent.config[:enabled]
21
+ return if NewRelic::Security::Agent::Control::HTTPContext.get_context.nil? && NewRelic::Security::Agent::Control::GRPCContext.get_context.nil?
22
+ args.map! { |file| Pathname.new(file).relative? ? File.join(Dir.pwd, file) : file } if [FILE_OPERATION, FILE_INTEGRITY].include?(case_type)
23
+
24
+ event = NewRelic::Security::Agent::Control::Event.new(case_type, args, event_category)
25
+ stk = caller_locations[1..COVERAGE]
26
+ event.sourceMethod = stk[0].label
27
+
28
+ event.copy_http_info(NewRelic::Security::Agent::Control::HTTPContext.get_context) if NewRelic::Security::Agent::Control::HTTPContext.get_context
29
+ event.copy_grpc_info(NewRelic::Security::Agent::Control::GRPCContext.get_context) if NewRelic::Security::Agent::Control::GRPCContext.get_context
30
+ event.isIASTEnable = true if NewRelic::Security::Agent::Utils.is_IAST?
31
+ event.isIASTRequest = true if NewRelic::Security::Agent::Utils.is_IAST_request?(event.httpRequest[:headers])
32
+ event.parentId = event.httpRequest[:headers][NR_CSEC_PARENT_ID] if event.httpRequest[:headers].key?(NR_CSEC_PARENT_ID)
33
+ find_deserialisation(event, stk) if case_type != REFLECTED_XSS && NewRelic::Security::Agent.config[:'security.detection.deserialization.enabled']
34
+ find_rci(event, stk) if case_type != REFLECTED_XSS && NewRelic::Security::Agent.config[:'security.detection.rci.enabled']
35
+ route = nil
36
+ if case_type == REFLECTED_XSS
37
+ event.httpResponse[:contentType] = keyword_args[:response_header]
38
+ route = NewRelic::Security::Agent::Control::HTTPContext.get_context.route
39
+ if route && NewRelic::Security::Agent.agent.route_map.include?(route)
40
+ event.stacktrace << route
41
+ end
42
+ end
43
+ # In rails 5 method name keeps chaning for same api call (ex: _app_views_sqli_sqlinjectionattackcase_html_erb__1999281606898621405_2624809100).
44
+ # Hence, considering only frame absolute_path & lineno for apiId calculation.
45
+ user_frame_index = get_user_frame_index(stk)
46
+ 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/)}
48
+ user_frame_index = get_user_frame_index(stk)
49
+ 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
+ if user_frame_index != -1
51
+ event.userMethodName = stk[user_frame_index].label.to_s
52
+ event.userFileName = stk[user_frame_index].path
53
+ event.lineNumber = stk[user_frame_index].lineno
54
+ else
55
+ event.sourceMethod = stk[0].label.to_s
56
+ event.userMethodName = stk[0].label.to_s
57
+ event.userFileName = stk[0].path
58
+ event.lineNumber = stk[0].lineno
59
+ end
60
+ 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
+ end
65
+ event
66
+ rescue Exception => exception
67
+ NewRelic::Security::Agent.logger.error "Exception in event collector: #{exception.inspect} #{exception.backtrace}"
68
+ NewRelic::Security::Agent.agent.event_processor.send_critical_message(exception.message, "SEVERE", caller_locations[0].to_s, Thread.current.name, exception)
69
+ if NewRelic::Security::Agent::Utils.is_IAST_request?(event.httpRequest[:headers])
70
+ NewRelic::Security::Agent.agent.iast_event_stats.error_count.increment
71
+ else
72
+ NewRelic::Security::Agent.agent.rasp_event_stats.error_count.increment
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def get_user_frame_index(stk)
79
+ return -1 if NewRelic::Security::Agent.config[:app_root].nil?
80
+ stk.each_with_index do |val, index|
81
+ return index if val.path.start_with?(NewRelic::Security::Agent.config[:app_root])
82
+ end
83
+ return -1
84
+ end
85
+
86
+ def calculate_api_id(stk, method, route)
87
+ stk << route if route
88
+ ::Digest::SHA256.hexdigest("#{stk.join(PIPE)}|#{method}").to_s
89
+ rescue Exception => e
90
+ NewRelic::Security::Agent.logger.error "Exception in calculate_api_id : #{e} #{e.backtrace}"
91
+ nil
92
+ end
93
+
94
+ def find_deserialisation(event, stk)
95
+ stk.each_with_index { |val, index|
96
+ if (val.path.end_with?(MARSHAL_RB) && val.label == LOAD) || (val.path.end_with?(PSYCH_RB) && val.label == LOAD) || (val.label == MARSHAL_LOAD)
97
+ event.metaData[:triggerViaDeserialisation] = true
98
+ event.metaData[:rciMethodsCalls] = stk[0..index].collect { |frame| frame.label }
99
+ return
100
+ end
101
+ }
102
+ end
103
+
104
+ def find_rci(event, stk)
105
+ stk.each_with_index { |val, index|
106
+ if val.label == EVAL
107
+ event.metaData[:triggerViaRCI] = true
108
+ event.metaData[:rciMethodsCalls] = stk[0..index].collect { |frame| frame.label }
109
+ return
110
+ end
111
+ }
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,117 @@
1
+ require 'json'
2
+
3
+ module NewRelic::Security
4
+ module Agent
5
+ module Control
6
+ module ControlCommand
7
+
8
+ extend self
9
+
10
+ def handle_ic_command(message)
11
+ message_json = parse_message(message)
12
+ define_transform_keys unless message_json.respond_to?(:transform_keys)
13
+ message_object = message_json.transform_keys(&:to_sym)
14
+ return if message_object.nil?
15
+
16
+ if message_object.has_key?(:controlCommand)
17
+ case message_object[:controlCommand]
18
+ when 4
19
+
20
+ when 5
21
+ NewRelic::Security::Agent.logger.debug "Control command : '5', #{message_object}"
22
+ when 6
23
+
24
+ when 7
25
+ NewRelic::Security::Agent.logger.debug "Control command : '7', #{message_object}"
26
+ when 10
27
+ NewRelic::Security::Agent.logger.debug "Control command : '10', #{message_object}"
28
+ when 11
29
+ NewRelic::Security::Agent.logger.debug "Control command : '11', #{message_object.to_json}"
30
+ NewRelic::Security::Agent.config.update_port = message_object[:reflectedMetaData][LISTEN_PORT].to_i unless NewRelic::Security::Agent.config[:listen_port]
31
+ NewRelic::Security::Agent.agent.iast_client.last_fuzz_cc_timestamp = current_time_millis
32
+ fuzz_request = NewRelic::Security::Agent::Control::FuzzRequest.new(message_object[:id])
33
+ fuzz_request.request = prepare_fuzz_request(message_object)
34
+ fuzz_request.case_type = message_object[:arguments][1]
35
+ fuzz_request.reflected_metadata = message_object[:reflectedMetaData]
36
+ NewRelic::Security::Agent.agent.iast_client.pending_request_ids << message_object[:id]
37
+ NewRelic::Security::Agent.agent.iast_client.enqueue(fuzz_request)
38
+ fuzz_request = nil
39
+ when 12
40
+ NewRelic::Security::Agent.logger.info "Validator asked to reconnect(CC#12), calling reconnect_at_will"
41
+ reconnect_at_will
42
+ when 13
43
+ NewRelic::Security::Agent.logger.debug "Control command : '13', #{message_object}"
44
+ NewRelic::Security::Agent.logger.debug "Received IAST cooldown. Waiting for next : #{message_object[:data]} Seconds"
45
+ NewRelic::Security::Agent.agent.iast_client.cooldown_till_timestamp = current_time_millis + (message_object[:data] * 1000)
46
+ when 14
47
+ NewRelic::Security::Agent.logger.debug "Control command : '14', #{message_object}"
48
+ NewRelic::Security::Agent.logger.debug "Purging confirmed IAST processed records count : #{message_object[:arguments].size}"
49
+ message_object[:arguments].each { |processed_id| NewRelic::Security::Agent.agent.iast_client.completed_requests.delete(processed_id) }
50
+ when 100
51
+ 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
+ when 101
55
+
56
+ when 102
57
+ NewRelic::Security::Agent.logger.error "Update policy failed at validator with error : #{message_object}"
58
+ # TODO: Apply initial policy here
59
+ when 1006
60
+ # TODO: abnormal closure in which case LC anyway have to reconnect
61
+ when 1013
62
+ # TODO: ndicates that the service is experiencing overload. A client should only connect to a different IP (when there are multiple for the target) or reconnect to the same IP upon user action.
63
+ else
64
+ NewRelic::Security::Agent.logger.error "Unrecognized control command : #{message_object}"
65
+ end
66
+ else
67
+ NewRelic::Security::Agent.logger.error "Control command is missing in IC message : #{message_object}"
68
+ end
69
+ end
70
+
71
+ def define_transform_keys
72
+ ::Hash.class_eval do
73
+ def transform_keys
74
+ result = {}
75
+ each_key do |key|
76
+ result[yield(key)] = self[key]
77
+ end
78
+ result
79
+ end
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def parse_message(message)
86
+ JSON.parse(message)
87
+ rescue JSON::ParserError => error
88
+ NewRelic::Security::Agent.logger.error "Error in parsing IC message : #{error.inspect}"
89
+ NewRelic::Security::Agent.agent.event_processor.send_critical_message(exception.message, "SEVERE", caller_locations[0].to_s, Thread.current.name, exception)
90
+ nil
91
+ end
92
+
93
+ def reconnect_at_will
94
+ NewRelic::Security::Agent.agent.iast_client.fuzzQ.clear if NewRelic::Security::Agent.agent.iast_client
95
+ NewRelic::Security::Agent.agent.iast_client.completed_requests.clear if NewRelic::Security::Agent.agent.iast_client
96
+ NewRelic::Security::Agent.agent.iast_client.pending_request_ids.clear if NewRelic::Security::Agent.agent.iast_client
97
+ NewRelic::Security::Agent.config.disable_security
98
+ Thread.new { NewRelic::Security::Agent.agent.reconnect(0) }
99
+ end
100
+
101
+ def current_time_millis
102
+ (Time.now.to_f * 1000).to_i
103
+ end
104
+
105
+ def prepare_fuzz_request(message_object)
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_FILE_SEPARATOR, ::File::SEPARATOR)
108
+ prepared_fuzz_request = ::JSON.parse(message_object[:arguments][0])
109
+ prepared_fuzz_request[HEADERS][NR_CSEC_PARENT_ID] = message_object[:id]
110
+ prepared_fuzz_request
111
+ rescue Exception => exception # rubocop:disable Lint/RescueException
112
+ NewRelic::Security::Agent.logger.error "Exception in preparing fuzz request : #{exception.inspect} #{exception.backtrace}"
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end