newrelic_security 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/enhancement.md +27 -0
- data/.github/actions/simplecov-report/LICENSE +22 -0
- data/.github/actions/simplecov-report/README.md +36 -0
- data/.github/actions/simplecov-report/__tests__/.keep +0 -0
- data/.github/actions/simplecov-report/__tests__/main.test.ts +3 -0
- data/.github/actions/simplecov-report/action.yml +25 -0
- data/.github/actions/simplecov-report/dist/index.js +10238 -0
- data/.github/actions/simplecov-report/dummy_coverage/.last_run.json +5 -0
- data/.github/actions/simplecov-report/jest.config.js +11 -0
- data/.github/actions/simplecov-report/package.json +51 -0
- data/.github/actions/simplecov-report/src/main.ts +54 -0
- data/.github/actions/simplecov-report/src/report.ts +28 -0
- data/.github/actions/simplecov-report/tsconfig.json +12 -0
- data/.github/workflows/pr_ci.yml +77 -0
- data/.github/workflows/release.yml +51 -0
- data/.github/workflows/repolinter.yml +31 -0
- data/.github/workflows/rubocop.yml +17 -0
- data/.github/workflows/scripts/rubygems-authenticate.py +13 -0
- data/.github/workflows/scripts/rubygems-publish.rb +33 -0
- data/.gitignore +72 -0
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +1414 -0
- data/.simplecov +16 -0
- data/CHANGELOG.md +69 -0
- data/CONTRIBUTING.md +22 -0
- data/Gemfile +6 -0
- data/Gemfile_test +58 -0
- data/LICENSE +43 -0
- data/README.md +133 -0
- data/README_agent.md +44 -0
- data/Rakefile +28 -0
- data/THIRD_PARTY_NOTICES.md +36 -0
- data/lib/newrelic_security/agent/agent.rb +109 -0
- data/lib/newrelic_security/agent/configuration/default_source.rb +8 -0
- data/lib/newrelic_security/agent/configuration/environment_source.rb +8 -0
- data/lib/newrelic_security/agent/configuration/manager.rb +178 -0
- data/lib/newrelic_security/agent/configuration/manual_source.rb +8 -0
- data/lib/newrelic_security/agent/configuration/server_source.rb +8 -0
- data/lib/newrelic_security/agent/configuration/yaml_source.rb +8 -0
- data/lib/newrelic_security/agent/control/app_info.rb +132 -0
- data/lib/newrelic_security/agent/control/application_url_mappings.rb +66 -0
- data/lib/newrelic_security/agent/control/collector.rb +117 -0
- data/lib/newrelic_security/agent/control/control_command.rb +117 -0
- data/lib/newrelic_security/agent/control/critical_message.rb +58 -0
- data/lib/newrelic_security/agent/control/event.rb +149 -0
- data/lib/newrelic_security/agent/control/event_counter.rb +28 -0
- data/lib/newrelic_security/agent/control/event_processor.rb +134 -0
- data/lib/newrelic_security/agent/control/event_stats.rb +26 -0
- data/lib/newrelic_security/agent/control/event_subscriber.rb +28 -0
- data/lib/newrelic_security/agent/control/exit_event.rb +38 -0
- data/lib/newrelic_security/agent/control/fuzz_request.rb +18 -0
- data/lib/newrelic_security/agent/control/grpc_context.rb +57 -0
- data/lib/newrelic_security/agent/control/health_check.rb +136 -0
- data/lib/newrelic_security/agent/control/http_context.rb +73 -0
- data/lib/newrelic_security/agent/control/iast_client.rb +151 -0
- data/lib/newrelic_security/agent/control/iast_data_transfer_request.rb +32 -0
- data/lib/newrelic_security/agent/control/reflected_xss.rb +258 -0
- data/lib/newrelic_security/agent/control/websocket_client.rb +131 -0
- data/lib/newrelic_security/agent/logging/init_logger.rb +91 -0
- data/lib/newrelic_security/agent/logging/logger.rb +92 -0
- data/lib/newrelic_security/agent/logging/null_logger.rb +21 -0
- data/lib/newrelic_security/agent/resources/cert.pem +50 -0
- data/lib/newrelic_security/agent/utils/agent_utils.rb +219 -0
- data/lib/newrelic_security/agent.rb +57 -0
- data/lib/newrelic_security/constants.rb +67 -0
- data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/chain.rb +70 -0
- data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/instrumentation.rb +187 -0
- data/lib/newrelic_security/instrumentation-security/active_record/mysql2_adapter/prepend.rb +54 -0
- data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/chain.rb +60 -0
- data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/instrumentation.rb +143 -0
- data/lib/newrelic_security/instrumentation-security/active_record/postgresql_adapter/prepend.rb +48 -0
- data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/chain.rb +72 -0
- data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/instrumentation.rb +187 -0
- data/lib/newrelic_security/instrumentation-security/active_record/sqlite3_adapter/prepend.rb +54 -0
- data/lib/newrelic_security/instrumentation-security/async-http/chain.rb +21 -0
- data/lib/newrelic_security/instrumentation-security/async-http/instrumentation.rb +46 -0
- data/lib/newrelic_security/instrumentation-security/async-http/prepend.rb +16 -0
- data/lib/newrelic_security/instrumentation-security/curb/chain.rb +26 -0
- data/lib/newrelic_security/instrumentation-security/curb/instrumentation.rb +52 -0
- data/lib/newrelic_security/instrumentation-security/curb/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/dir/chain.rb +42 -0
- data/lib/newrelic_security/instrumentation-security/dir/instrumentation.rb +102 -0
- data/lib/newrelic_security/instrumentation-security/dir/prepend.rb +28 -0
- data/lib/newrelic_security/instrumentation-security/ethon/chain.rb +53 -0
- data/lib/newrelic_security/instrumentation-security/ethon/instrumentation.rb +122 -0
- data/lib/newrelic_security/instrumentation-security/ethon/prepend.rb +39 -0
- data/lib/newrelic_security/instrumentation-security/excon/chain.rb +23 -0
- data/lib/newrelic_security/instrumentation-security/excon/instrumentation.rb +44 -0
- data/lib/newrelic_security/instrumentation-security/excon/prepend.rb +17 -0
- data/lib/newrelic_security/instrumentation-security/file/chain.rb +34 -0
- data/lib/newrelic_security/instrumentation-security/file/instrumentation.rb +62 -0
- data/lib/newrelic_security/instrumentation-security/file/prepend.rb +22 -0
- data/lib/newrelic_security/instrumentation-security/grape/chain.rb +42 -0
- data/lib/newrelic_security/instrumentation-security/grape/instrumentation.rb +56 -0
- data/lib/newrelic_security/instrumentation-security/grape/prepend.rb +30 -0
- data/lib/newrelic_security/instrumentation-security/grpc/client/chain.rb +47 -0
- data/lib/newrelic_security/instrumentation-security/grpc/client/instrumentation.rb +37 -0
- data/lib/newrelic_security/instrumentation-security/grpc/client/prepend.rb +36 -0
- data/lib/newrelic_security/instrumentation-security/grpc/server/chain.rb +62 -0
- data/lib/newrelic_security/instrumentation-security/grpc/server/instrumentation.rb +65 -0
- data/lib/newrelic_security/instrumentation-security/grpc/server/prepend.rb +46 -0
- data/lib/newrelic_security/instrumentation-security/httpclient/chain.rb +30 -0
- data/lib/newrelic_security/instrumentation-security/httpclient/instrumentation.rb +82 -0
- data/lib/newrelic_security/instrumentation-security/httpclient/prepend.rb +22 -0
- data/lib/newrelic_security/instrumentation-security/httprb/chain.rb +21 -0
- data/lib/newrelic_security/instrumentation-security/httprb/instrumentation.rb +44 -0
- data/lib/newrelic_security/instrumentation-security/httprb/prepend.rb +16 -0
- data/lib/newrelic_security/instrumentation-security/httpx/chain.rb +23 -0
- data/lib/newrelic_security/instrumentation-security/httpx/instrumentation.rb +51 -0
- data/lib/newrelic_security/instrumentation-security/httpx/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/instrumentation_loader.rb +50 -0
- data/lib/newrelic_security/instrumentation-security/instrumentation_utils.rb +165 -0
- data/lib/newrelic_security/instrumentation-security/io/chain.rb +113 -0
- data/lib/newrelic_security/instrumentation-security/io/instrumentation.rb +300 -0
- data/lib/newrelic_security/instrumentation-security/io/prepend.rb +86 -0
- data/lib/newrelic_security/instrumentation-security/kernel/chain.rb +65 -0
- data/lib/newrelic_security/instrumentation-security/kernel/instrumentation.rb +167 -0
- data/lib/newrelic_security/instrumentation-security/kernel/prepend.rb +50 -0
- data/lib/newrelic_security/instrumentation-security/mongo/chain.rb +106 -0
- data/lib/newrelic_security/instrumentation-security/mongo/instrumentation.rb +273 -0
- data/lib/newrelic_security/instrumentation-security/mongo/prepend.rb +77 -0
- data/lib/newrelic_security/instrumentation-security/mysql2/chain.rb +53 -0
- data/lib/newrelic_security/instrumentation-security/mysql2/instrumentation.rb +84 -0
- data/lib/newrelic_security/instrumentation-security/mysql2/prepend.rb +37 -0
- data/lib/newrelic_security/instrumentation-security/net_http/chain.rb +21 -0
- data/lib/newrelic_security/instrumentation-security/net_http/instrumentation.rb +60 -0
- data/lib/newrelic_security/instrumentation-security/net_http/prepend.rb +16 -0
- data/lib/newrelic_security/instrumentation-security/net_ldap/chain.rb +21 -0
- data/lib/newrelic_security/instrumentation-security/net_ldap/instrumentation.rb +42 -0
- data/lib/newrelic_security/instrumentation-security/net_ldap/prepend.rb +16 -0
- data/lib/newrelic_security/instrumentation-security/nokogiri/chain.rb +46 -0
- data/lib/newrelic_security/instrumentation-security/nokogiri/instrumentation.rb +36 -0
- data/lib/newrelic_security/instrumentation-security/nokogiri/prepend.rb +31 -0
- data/lib/newrelic_security/instrumentation-security/padrino/chain.rb +26 -0
- data/lib/newrelic_security/instrumentation-security/padrino/instrumentation.rb +42 -0
- data/lib/newrelic_security/instrumentation-security/padrino/prepend.rb +20 -0
- data/lib/newrelic_security/instrumentation-security/patron/chain.rb +23 -0
- data/lib/newrelic_security/instrumentation-security/patron/instrumentation.rb +50 -0
- data/lib/newrelic_security/instrumentation-security/patron/prepend.rb +18 -0
- data/lib/newrelic_security/instrumentation-security/pg/chain.rb +49 -0
- data/lib/newrelic_security/instrumentation-security/pg/instrumentation.rb +102 -0
- data/lib/newrelic_security/instrumentation-security/pg/prepend.rb +36 -0
- data/lib/newrelic_security/instrumentation-security/pty/chain.rb +31 -0
- data/lib/newrelic_security/instrumentation-security/pty/instrumentation.rb +52 -0
- data/lib/newrelic_security/instrumentation-security/pty/prepend.rb +22 -0
- data/lib/newrelic_security/instrumentation-security/rails/chain.rb +46 -0
- data/lib/newrelic_security/instrumentation-security/rails/instrumentation.rb +67 -0
- data/lib/newrelic_security/instrumentation-security/rails/prepend.rb +33 -0
- data/lib/newrelic_security/instrumentation-security/roda/chain.rb +22 -0
- data/lib/newrelic_security/instrumentation-security/roda/instrumentation.rb +41 -0
- data/lib/newrelic_security/instrumentation-security/roda/prepend.rb +16 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/chain.rb +29 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/instrumentation.rb +49 -0
- data/lib/newrelic_security/instrumentation-security/sinatra/prepend.rb +21 -0
- data/lib/newrelic_security/instrumentation-security/sqlite3/chain.rb +79 -0
- data/lib/newrelic_security/instrumentation-security/sqlite3/instrumentation.rb +164 -0
- data/lib/newrelic_security/instrumentation-security/sqlite3/prepend.rb +56 -0
- data/lib/newrelic_security/newrelic-security-api/api.rb +72 -0
- data/lib/newrelic_security/version.rb +5 -0
- data/lib/newrelic_security/websocket-client-simple/client.rb +128 -0
- data/lib/newrelic_security/websocket-client-simple/event_emitter.rb +72 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/error.rb +129 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/exception_handler.rb +32 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/base.rb +62 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/data.rb +49 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/base.rb +41 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler03.rb +224 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler04.rb +18 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler05.rb +15 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler07.rb +78 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler/handler75.rb +78 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/handler.rb +15 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/client.rb +17 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming/server.rb +17 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/incoming.rb +52 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/client.rb +17 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing/server.rb +17 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame/outgoing.rb +35 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/frame.rb +11 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/base.rb +142 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/client.rb +130 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/base.rb +49 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client.rb +32 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client01.rb +20 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client04.rb +63 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client11.rb +22 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client75.rb +39 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/client76.rb +105 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server.rb +10 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server04.rb +56 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server75.rb +40 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler/server76.rb +75 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler.rb +21 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/server.rb +179 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake.rb +10 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/nice_inspect.rb +12 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/version.rb +5 -0
- data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket.rb +50 -0
- data/lib/newrelic_security.rb +6 -0
- data/lib/tasks/all.rb +8 -0
- data/lib/tasks/coverage_report.rake +27 -0
- data/newrelic_security.gemspec +51 -0
- metadata +342 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module NewRelic::Security::WebSocket
|
7
|
+
module Handshake
|
8
|
+
module Handler
|
9
|
+
class Client04 < Client
|
10
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#valid?
|
11
|
+
def valid?
|
12
|
+
super && verify_accept && verify_protocol
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
18
|
+
def handshake_keys
|
19
|
+
keys = [
|
20
|
+
%w[Upgrade websocket],
|
21
|
+
%w[Connection Upgrade]
|
22
|
+
]
|
23
|
+
host = @handshake.host
|
24
|
+
host += ":#{@handshake.port}" unless @handshake.default_port?
|
25
|
+
keys << ['Host', host]
|
26
|
+
keys += super
|
27
|
+
keys << ['Sec-WebSocket-Origin', @handshake.origin] if @handshake.origin
|
28
|
+
keys << ['Sec-WebSocket-Version', @handshake.version]
|
29
|
+
keys << ['Sec-WebSocket-Key', key]
|
30
|
+
keys << ['Sec-WebSocket-Protocol', @handshake.protocols.join(', ')] if @handshake.protocols.any?
|
31
|
+
keys
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sec-WebSocket-Key value
|
35
|
+
# @return [String] key
|
36
|
+
def key
|
37
|
+
@key ||= Base64.encode64((1..16).map { rand(255).chr } * '').strip
|
38
|
+
end
|
39
|
+
|
40
|
+
# Value of Sec-WebSocket-Accept that should be delivered back by server
|
41
|
+
# @return [Sering] accept
|
42
|
+
def accept
|
43
|
+
@accept ||= Base64.encode64(Digest::SHA1.digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')).strip
|
44
|
+
end
|
45
|
+
|
46
|
+
# Verify if received header Sec-WebSocket-Accept matches generated one.
|
47
|
+
# @return [Boolean] True if accept is matching. False otherwise(appropriate error is set)
|
48
|
+
def verify_accept
|
49
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.headers['sec-websocket-accept'] == accept
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def supported_protocols
|
54
|
+
@handshake.protocols
|
55
|
+
end
|
56
|
+
|
57
|
+
def provided_protocols
|
58
|
+
@handshake.headers['sec-websocket-protocol'].to_s.split(/ *, */)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
module Handler
|
6
|
+
class Client11 < Client04
|
7
|
+
private
|
8
|
+
|
9
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
10
|
+
def handshake_keys
|
11
|
+
super.collect do |key_pair|
|
12
|
+
if key_pair[0] == 'Sec-WebSocket-Origin'
|
13
|
+
['Origin', key_pair[1]]
|
14
|
+
else
|
15
|
+
key_pair
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
module Handler
|
6
|
+
class Client75 < Client
|
7
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#valid?
|
8
|
+
def valid?
|
9
|
+
super && verify_protocol
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
15
|
+
def handshake_keys
|
16
|
+
keys = [
|
17
|
+
%w[Upgrade WebSocket],
|
18
|
+
%w[Connection Upgrade]
|
19
|
+
]
|
20
|
+
host = @handshake.host
|
21
|
+
host += ":#{@handshake.port}" unless @handshake.default_port?
|
22
|
+
keys << ['Host', host]
|
23
|
+
keys << ['Origin', @handshake.origin] if @handshake.origin
|
24
|
+
keys << ['WebSocket-Protocol', @handshake.protocols.first] if @handshake.protocols.any?
|
25
|
+
keys += super
|
26
|
+
keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def supported_protocols
|
30
|
+
Array(@handshake.protocols.first)
|
31
|
+
end
|
32
|
+
|
33
|
+
def provided_protocols
|
34
|
+
Array(@handshake.headers['websocket-protocol'].to_s.strip)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module NewRelic::Security::WebSocket
|
6
|
+
module Handshake
|
7
|
+
module Handler
|
8
|
+
class Client76 < Client75
|
9
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#valid?
|
10
|
+
def valid?
|
11
|
+
super && verify_challenge && verify_protocol
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#reserved_leftover_lines
|
17
|
+
def reserved_leftover_lines
|
18
|
+
1
|
19
|
+
end
|
20
|
+
|
21
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
22
|
+
def handshake_keys
|
23
|
+
keys = super
|
24
|
+
keys << ['Sec-WebSocket-Key1', key1]
|
25
|
+
keys << ['Sec-WebSocket-Key2', key2]
|
26
|
+
keys
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#finishing_line
|
30
|
+
def finishing_line
|
31
|
+
key3
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sec-WebSocket-Key1 value
|
35
|
+
# @return [String] key
|
36
|
+
def key1
|
37
|
+
@key1 ||= generate_key(:key1)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sec-WebSocket-Key2 value
|
41
|
+
# @return [String] key
|
42
|
+
def key2
|
43
|
+
@key2 ||= generate_key(:key2)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Value of third key, sent in body
|
47
|
+
# @return [String] key
|
48
|
+
def key3
|
49
|
+
@key3 ||= generate_key3
|
50
|
+
end
|
51
|
+
|
52
|
+
# Expected challenge that should be sent by server
|
53
|
+
# @return [String] challenge
|
54
|
+
def challenge
|
55
|
+
return @challenge if defined?(@challenge)
|
56
|
+
key1 && key2
|
57
|
+
sum = [@key1_number].pack('N*') +
|
58
|
+
[@key2_number].pack('N*') +
|
59
|
+
key3
|
60
|
+
|
61
|
+
@challenge = Digest::MD5.digest(sum).strip
|
62
|
+
end
|
63
|
+
|
64
|
+
# Verify if challenge sent by server match generated one
|
65
|
+
# @return [Boolena] True if challenge matches, false otherwise(sets appropriate error)
|
66
|
+
def verify_challenge
|
67
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication unless @handshake.leftovers == challenge
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
NOISE_CHARS = ("\x21".."\x2f").to_a + ("\x3a".."\x7e").to_a
|
72
|
+
|
73
|
+
# Generate Sec-WebSocket-Key1 and Sec-WebSocket-Key2
|
74
|
+
# @param key [String] name of key. Will be used to set number variable needed later. Valid values: key1, key2
|
75
|
+
# @return [String] generated key
|
76
|
+
def generate_key(key)
|
77
|
+
spaces = rand(1..12)
|
78
|
+
max = 0xffffffff / spaces
|
79
|
+
number = rand(max + 1)
|
80
|
+
instance_variable_set("@#{key}_number", number)
|
81
|
+
key = (number * spaces).to_s
|
82
|
+
rand(1..12).times do
|
83
|
+
char = NOISE_CHARS[rand(NOISE_CHARS.size)]
|
84
|
+
pos = rand(key.size + 1)
|
85
|
+
key[pos...pos] = char
|
86
|
+
end
|
87
|
+
spaces.times do
|
88
|
+
pos = 1 + rand(key.size - 1)
|
89
|
+
key[pos...pos] = ' '
|
90
|
+
end
|
91
|
+
key
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generate third key
|
95
|
+
def generate_key3
|
96
|
+
[rand(0x100000000)].pack('N') + [rand(0x100000000)].pack('N')
|
97
|
+
end
|
98
|
+
|
99
|
+
def provided_protocols
|
100
|
+
Array(@handshake.headers['sec-websocket-protocol'].to_s.strip)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module NewRelic::Security::WebSocket
|
7
|
+
module Handshake
|
8
|
+
module Handler
|
9
|
+
class Server04 < Server
|
10
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#valid?
|
11
|
+
def valid?
|
12
|
+
super && verify_key
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#header_line
|
18
|
+
def header_line
|
19
|
+
'HTTP/1.1 101 Switching Protocols'
|
20
|
+
end
|
21
|
+
|
22
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
23
|
+
def handshake_keys
|
24
|
+
[
|
25
|
+
%w[Upgrade websocket],
|
26
|
+
%w[Connection Upgrade],
|
27
|
+
['Sec-WebSocket-Accept', signature]
|
28
|
+
] + protocol
|
29
|
+
end
|
30
|
+
|
31
|
+
# Signature of response, created from client request Sec-WebSocket-Key
|
32
|
+
# @return [String] signature
|
33
|
+
def signature
|
34
|
+
return unless key
|
35
|
+
string_to_sign = "#{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
36
|
+
Base64.encode64(Digest::SHA1.digest(string_to_sign)).chomp
|
37
|
+
end
|
38
|
+
|
39
|
+
def verify_key
|
40
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication unless key
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def key
|
45
|
+
@handshake.headers['sec-websocket-key']
|
46
|
+
end
|
47
|
+
|
48
|
+
def protocol
|
49
|
+
return [] unless @handshake.headers.key?('sec-websocket-protocol')
|
50
|
+
protos = @handshake.headers['sec-websocket-protocol'].split(/ *, */) & @handshake.protocols
|
51
|
+
[['Sec-WebSocket-Protocol', protos.first]]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
module Handler
|
6
|
+
class Server75 < Server
|
7
|
+
private
|
8
|
+
|
9
|
+
def headers
|
10
|
+
{
|
11
|
+
origin: 'WebSocket-Origin',
|
12
|
+
location: 'WebSocket-Location',
|
13
|
+
protocol: 'WebSocket-Protocol'
|
14
|
+
}.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#header_line
|
18
|
+
def header_line
|
19
|
+
'HTTP/1.1 101 Web Socket Protocol Handshake'
|
20
|
+
end
|
21
|
+
|
22
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#handshake_keys
|
23
|
+
def handshake_keys
|
24
|
+
[
|
25
|
+
%w[Upgrade WebSocket],
|
26
|
+
%w[Connection Upgrade],
|
27
|
+
[headers[:origin], @handshake.headers['origin']],
|
28
|
+
[headers[:location], @handshake.uri]
|
29
|
+
] + protocol
|
30
|
+
end
|
31
|
+
|
32
|
+
def protocol
|
33
|
+
return [] unless @handshake.headers.key?(headers[:protocol].downcase)
|
34
|
+
proto = @handshake.headers[headers[:protocol].downcase]
|
35
|
+
[[headers[:protocol], @handshake.protocols.include?(proto) ? proto : nil]]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module NewRelic::Security::WebSocket
|
6
|
+
module Handshake
|
7
|
+
module Handler
|
8
|
+
class Server76 < Server75
|
9
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#valid?
|
10
|
+
def valid?
|
11
|
+
super && !finishing_line.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def headers
|
17
|
+
{
|
18
|
+
origin: 'Sec-WebSocket-Origin',
|
19
|
+
location: 'Sec-WebSocket-Location',
|
20
|
+
protocol: 'Sec-WebSocket-Protocol'
|
21
|
+
}.freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
# @see NewRelic::Security::WebSocket::Handshake::Base#reserved_leftover_lines
|
25
|
+
def reserved_leftover_lines
|
26
|
+
1
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#header_line
|
30
|
+
def header_line
|
31
|
+
'HTTP/1.1 101 WebSocket Protocol Handshake'
|
32
|
+
end
|
33
|
+
|
34
|
+
# @see NewRelic::Security::WebSocket::Handshake::Handler::Base#finishing_line
|
35
|
+
def finishing_line
|
36
|
+
@finishing_line ||= challenge_response
|
37
|
+
end
|
38
|
+
|
39
|
+
# Response to client challenge from request Sec-WebSocket-Key1, Sec-WebSocket-Key2 and leftovers
|
40
|
+
# @return [String] Challenge response or nil if error occured
|
41
|
+
def challenge_response
|
42
|
+
# Refer to 5.2 4-9 of the draft 76
|
43
|
+
first = numbers_over_spaces(@handshake.headers['sec-websocket-key1'].to_s)
|
44
|
+
second = numbers_over_spaces(@handshake.headers['sec-websocket-key2'].to_s)
|
45
|
+
third = @handshake.leftovers
|
46
|
+
|
47
|
+
sum = [first].pack('N*') +
|
48
|
+
[second].pack('N*') +
|
49
|
+
third
|
50
|
+
Digest::MD5.digest(sum)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Calculate numbers over spaces, according to spec 5.2
|
54
|
+
# @param [String] string Key to parse
|
55
|
+
# @return [Integer] Result of calculations or nil if error occured
|
56
|
+
def numbers_over_spaces(string)
|
57
|
+
numbers = string.scan(/[0-9]/).join.to_i
|
58
|
+
|
59
|
+
spaces = string.scan(/ /).size
|
60
|
+
# As per 5.2.5, abort the connection if spaces are zero.
|
61
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication if spaces.zero?
|
62
|
+
|
63
|
+
# As per 5.2.6, abort if numbers is not an integral multiple of spaces
|
64
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication if numbers % spaces != 0
|
65
|
+
|
66
|
+
quotient = numbers / spaces
|
67
|
+
|
68
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidAuthentication if quotient > 2**32 - 1
|
69
|
+
|
70
|
+
quotient
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/handler.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
module Handler
|
6
|
+
autoload :Base, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/base"
|
7
|
+
|
8
|
+
autoload :Client, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client"
|
9
|
+
autoload :Client01, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client01"
|
10
|
+
autoload :Client04, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client04"
|
11
|
+
autoload :Client11, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client11"
|
12
|
+
autoload :Client75, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client75"
|
13
|
+
autoload :Client76, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/client76"
|
14
|
+
|
15
|
+
autoload :Server, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/server"
|
16
|
+
autoload :Server04, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/server04"
|
17
|
+
autoload :Server75, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/server75"
|
18
|
+
autoload :Server76, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler/server76"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/handshake/server.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
# Construct or parse a server WebSocket handshake.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# handshake = NewRelic::Security::WebSocket::Handshake::Server.new
|
9
|
+
#
|
10
|
+
# # Parse client request
|
11
|
+
# @handshake << <<EOF
|
12
|
+
# GET /demo HTTP/1.1\r
|
13
|
+
# Upgrade: websocket\r
|
14
|
+
# Connection: Upgrade\r
|
15
|
+
# Host: example.com\r
|
16
|
+
# Origin: http://example.com\r
|
17
|
+
# Sec-WebSocket-Version: 13\r
|
18
|
+
# Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
19
|
+
# \r
|
20
|
+
# EOF
|
21
|
+
#
|
22
|
+
# # All data received?
|
23
|
+
# @handshake.finished?
|
24
|
+
#
|
25
|
+
# # No parsing errors?
|
26
|
+
# @handshake.valid?
|
27
|
+
#
|
28
|
+
# # Create response
|
29
|
+
# @handshake.to_s # HTTP/1.1 101 Switching Protocols
|
30
|
+
# # Upgrade: websocket
|
31
|
+
# # Connection: Upgrade
|
32
|
+
# # Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
33
|
+
#
|
34
|
+
class Server < Base
|
35
|
+
# Initialize new WebSocket Server
|
36
|
+
#
|
37
|
+
# @param [Hash] args Arguments for server
|
38
|
+
#
|
39
|
+
# @option args [Boolean] :secure If true then server will use wss:// protocol
|
40
|
+
# @option args [Array<String>] :protocols an array of supported sub-protocols
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# Websocket::Handshake::Server.new(secure: true)
|
44
|
+
def initialize(args = {})
|
45
|
+
super
|
46
|
+
@secure ||= false
|
47
|
+
end
|
48
|
+
|
49
|
+
# Add text of request from Client. This method will parse content immediately and update version, state and error(if neccessary)
|
50
|
+
#
|
51
|
+
# @param [String] data Data to add
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# @handshake << <<EOF
|
55
|
+
# GET /demo HTTP/1.1
|
56
|
+
# Upgrade: websocket
|
57
|
+
# Connection: Upgrade
|
58
|
+
# Host: example.com
|
59
|
+
# Origin: http://example.com
|
60
|
+
# Sec-WebSocket-Version: 13
|
61
|
+
# Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
62
|
+
#
|
63
|
+
# EOF
|
64
|
+
def <<(data)
|
65
|
+
super
|
66
|
+
set_version if parse_data
|
67
|
+
end
|
68
|
+
rescue_method :<<
|
69
|
+
|
70
|
+
# Parse the request from a rack environment
|
71
|
+
# @param env Rack Environment
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# @handshake.from_rack(env)
|
75
|
+
def from_rack(env)
|
76
|
+
@headers = env.select { |key, _value| key.to_s.start_with? 'HTTP_' }.each_with_object({}) do |tuple, memo|
|
77
|
+
key, value = tuple
|
78
|
+
memo[key.gsub(/\AHTTP_/, '').tr('_', '-').downcase] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
@path = env['REQUEST_PATH']
|
82
|
+
@query = env['QUERY_STRING']
|
83
|
+
|
84
|
+
set_version
|
85
|
+
|
86
|
+
# Passenger is blocking on read
|
87
|
+
# Unicorn doesn't support readpartial
|
88
|
+
# Maybe someone is providing even plain string?
|
89
|
+
# Better safe than sorry...
|
90
|
+
if @version == 76
|
91
|
+
input = env['rack.input']
|
92
|
+
@leftovers = if input.respond_to?(:readpartial)
|
93
|
+
input.readpartial
|
94
|
+
elsif input.respond_to?(:read)
|
95
|
+
input.read
|
96
|
+
else
|
97
|
+
input.to_s
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@state = :finished
|
102
|
+
end
|
103
|
+
|
104
|
+
# Parse the request from hash
|
105
|
+
# @param hash Hash to import data
|
106
|
+
# @option hash [Hash] :headers HTTP headers of request, downcased
|
107
|
+
# @option hash [String] :path Path for request(without host and query string)
|
108
|
+
# @option hash [String] :query Query string for request
|
109
|
+
# @option hash [String] :body Body of request(if exists)
|
110
|
+
#
|
111
|
+
# @example
|
112
|
+
# @handshake.from_hash(hash)
|
113
|
+
def from_hash(hash)
|
114
|
+
@headers = hash[:headers] || {}
|
115
|
+
@path = hash[:path] || '/'
|
116
|
+
@query = hash[:query] || ''
|
117
|
+
@leftovers = hash[:body]
|
118
|
+
|
119
|
+
set_version
|
120
|
+
@state = :finished
|
121
|
+
end
|
122
|
+
|
123
|
+
# Should send content to client after finished parsing?
|
124
|
+
# @return [Boolean] true
|
125
|
+
def should_respond?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
# Host of server according to client header
|
130
|
+
# @return [String] host
|
131
|
+
def host
|
132
|
+
@host || @headers['host'].to_s.split(':')[0].to_s
|
133
|
+
end
|
134
|
+
|
135
|
+
# Port of server according to client header
|
136
|
+
# @return [Integer] port
|
137
|
+
def port
|
138
|
+
(@port || @headers['host'].to_s.split(':')[1] || default_port).to_i
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Set version of protocol basing on client requets. AFter cotting method calls include_version.
|
144
|
+
def set_version
|
145
|
+
@version = @headers['sec-websocket-version'].to_i if @headers['sec-websocket-version']
|
146
|
+
@version ||= @headers['sec-websocket-draft'].to_i if @headers['sec-websocket-draft']
|
147
|
+
@version ||= 76 if @headers['sec-websocket-key1']
|
148
|
+
@version ||= 75
|
149
|
+
include_version
|
150
|
+
end
|
151
|
+
|
152
|
+
# Include set of methods for selected protocol version
|
153
|
+
# @return [Boolean] false if protocol number is unknown, otherwise true
|
154
|
+
def include_version
|
155
|
+
@handler = case @version
|
156
|
+
when 75 then Handler::Server75.new(self)
|
157
|
+
when 76, 0..3 then Handler::Server76.new(self)
|
158
|
+
when 4..17 then Handler::Server04.new(self)
|
159
|
+
else raise NewRelic::Security::WebSocket::Error::Handshake::UnknownVersion
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
PATH = %r{^(\w+) (\/[^\s]*) HTTP\/1\.1$}
|
164
|
+
|
165
|
+
# Parse first line of Client response.
|
166
|
+
# @param [String] line Line to parse
|
167
|
+
# @return [Boolean] True if parsed correctly. False otherwise
|
168
|
+
def parse_first_line(line)
|
169
|
+
line_parts = line.match(PATH)
|
170
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::InvalidHeader unless line_parts
|
171
|
+
method = line_parts[1].strip
|
172
|
+
raise NewRelic::Security::WebSocket::Error::Handshake::GetRequestRequired unless method == 'GET'
|
173
|
+
|
174
|
+
resource_name = line_parts[2].strip
|
175
|
+
@path, @query = resource_name.split('?', 2)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module Handshake
|
5
|
+
autoload :Base, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/base"
|
6
|
+
autoload :Client, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/client"
|
7
|
+
autoload :Handler, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/handler"
|
8
|
+
autoload :Server, "#{NewRelic::Security::WebSocket::ROOT}/websocket/handshake/server"
|
9
|
+
end
|
10
|
+
end
|
data/lib/newrelic_security/websocket-client-simple/websocket-ruby/lib/websocket/nice_inspect.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NewRelic::Security::WebSocket
|
4
|
+
module NiceInspect
|
5
|
+
# Recreate inspect as #to_s will be overwritten
|
6
|
+
def inspect
|
7
|
+
vars = instance_variables.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(', ')
|
8
|
+
insp = Kernel.format("#{self.class}:0x%08x", __id__)
|
9
|
+
"<#{insp} #{vars}>"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|