ddtrace 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -1
- data/LICENSE-3rdparty.csv +1 -0
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +1169 -275
- data/lib/datadog/appsec/assets/waf_rules/risky.json +78 -78
- data/lib/datadog/appsec/assets/waf_rules/strict.json +278 -88
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +25 -18
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/request.rb +3 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +42 -19
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -6
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +11 -11
- data/lib/datadog/appsec/contrib/rails/request.rb +3 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +14 -12
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +11 -11
- data/lib/datadog/appsec/event.rb +2 -12
- data/lib/datadog/appsec/instrumentation/gateway.rb +16 -2
- data/lib/datadog/appsec/processor.rb +18 -2
- data/lib/datadog/core/configuration/settings.rb +19 -5
- data/lib/datadog/tracing/client_ip.rb +11 -0
- data/lib/datadog/tracing/configuration/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +14 -6
- data/lib/ddtrace/transport/traces.rb +2 -0
- data/lib/ddtrace/version.rb +1 -1
- metadata +3 -3
@@ -16,8 +16,10 @@ module Datadog
|
|
16
16
|
module Watcher
|
17
17
|
# rubocop:disable Metrics/AbcSize
|
18
18
|
# rubocop:disable Metrics/MethodLength
|
19
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
20
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
19
21
|
def self.watch
|
20
|
-
Instrumentation.gateway.watch('rack.request') do |stack, request|
|
22
|
+
Instrumentation.gateway.watch('rack.request', :appsec) do |stack, request|
|
21
23
|
block = false
|
22
24
|
event = nil
|
23
25
|
waf_context = request.env['datadog.waf.context']
|
@@ -26,23 +28,24 @@ module Datadog
|
|
26
28
|
trace = active_trace
|
27
29
|
span = active_span
|
28
30
|
|
29
|
-
Rack::Reactive::Request.subscribe(op, waf_context) do |
|
30
|
-
|
31
|
-
if record
|
31
|
+
Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
|
32
|
+
if result.status == :match
|
32
33
|
# TODO: should this hash be an Event instance instead?
|
33
34
|
event = {
|
34
35
|
waf_result: result,
|
35
36
|
trace: trace,
|
36
37
|
span: span,
|
37
38
|
request: request,
|
38
|
-
|
39
|
+
actions: result.actions
|
39
40
|
}
|
40
41
|
|
42
|
+
span.set_tag('appsec.event', 'true') if span
|
43
|
+
|
41
44
|
waf_context.events << event
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
|
-
|
48
|
+
_result, block = Rack::Reactive::Request.publish(op, request)
|
46
49
|
end
|
47
50
|
|
48
51
|
next [nil, [[:block, event]]] if block
|
@@ -57,7 +60,7 @@ module Datadog
|
|
57
60
|
[ret, res]
|
58
61
|
end
|
59
62
|
|
60
|
-
Instrumentation.gateway.watch('rack.response') do |stack, response|
|
63
|
+
Instrumentation.gateway.watch('rack.response', :appsec) do |stack, response|
|
61
64
|
block = false
|
62
65
|
event = nil
|
63
66
|
waf_context = response.instance_eval { @waf_context }
|
@@ -66,23 +69,24 @@ module Datadog
|
|
66
69
|
trace = active_trace
|
67
70
|
span = active_span
|
68
71
|
|
69
|
-
Rack::Reactive::Response.subscribe(op, waf_context) do |
|
70
|
-
|
71
|
-
if record
|
72
|
+
Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
|
73
|
+
if result.status == :match
|
72
74
|
# TODO: should this hash be an Event instance instead?
|
73
75
|
event = {
|
74
76
|
waf_result: result,
|
75
77
|
trace: trace,
|
76
78
|
span: span,
|
77
79
|
response: response,
|
78
|
-
|
80
|
+
actions: result.actions
|
79
81
|
}
|
80
82
|
|
83
|
+
span.set_tag('appsec.event', 'true') if span
|
84
|
+
|
81
85
|
waf_context.events << event
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
85
|
-
|
89
|
+
_result, block = Rack::Reactive::Response.publish(op, response)
|
86
90
|
end
|
87
91
|
|
88
92
|
next [nil, [[:block, event]]] if block
|
@@ -97,7 +101,7 @@ module Datadog
|
|
97
101
|
[ret, res]
|
98
102
|
end
|
99
103
|
|
100
|
-
Instrumentation.gateway.watch('rack.request.body') do |stack, request|
|
104
|
+
Instrumentation.gateway.watch('rack.request.body', :appsec) do |stack, request|
|
101
105
|
block = false
|
102
106
|
event = nil
|
103
107
|
waf_context = request.env['datadog.waf.context']
|
@@ -106,23 +110,24 @@ module Datadog
|
|
106
110
|
trace = active_trace
|
107
111
|
span = active_span
|
108
112
|
|
109
|
-
Rack::Reactive::RequestBody.subscribe(op, waf_context) do |
|
110
|
-
|
111
|
-
if record
|
113
|
+
Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
|
114
|
+
if result.status == :match
|
112
115
|
# TODO: should this hash be an Event instance instead?
|
113
116
|
event = {
|
114
117
|
waf_result: result,
|
115
118
|
trace: trace,
|
116
119
|
span: span,
|
117
120
|
request: request,
|
118
|
-
|
121
|
+
actions: result.actions
|
119
122
|
}
|
120
123
|
|
124
|
+
span.set_tag('appsec.event', 'true') if span
|
125
|
+
|
121
126
|
waf_context.events << event
|
122
127
|
end
|
123
128
|
end
|
124
129
|
|
125
|
-
|
130
|
+
_result, block = Rack::Reactive::RequestBody.publish(op, request)
|
126
131
|
end
|
127
132
|
|
128
133
|
next [nil, [[:block, event]]] if block
|
@@ -137,6 +142,8 @@ module Datadog
|
|
137
142
|
[ret, res]
|
138
143
|
end
|
139
144
|
end
|
145
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
146
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
140
147
|
# rubocop:enable Metrics/MethodLength
|
141
148
|
# rubocop:enable Metrics/AbcSize
|
142
149
|
|
@@ -54,27 +54,27 @@ module Datadog
|
|
54
54
|
}
|
55
55
|
|
56
56
|
waf_timeout = Datadog::AppSec.settings.waf_timeout
|
57
|
-
|
57
|
+
result = waf_context.run(waf_args, waf_timeout)
|
58
58
|
|
59
59
|
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
when :monitor
|
61
|
+
case result.status
|
62
|
+
when :match
|
64
63
|
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
yield [
|
69
|
-
|
70
|
-
|
64
|
+
|
65
|
+
block = result.actions.include?('block')
|
66
|
+
|
67
|
+
yield [result, block]
|
68
|
+
|
69
|
+
throw(:block, [result, true]) if block
|
70
|
+
when :ok
|
71
71
|
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
72
72
|
when :invalid_call
|
73
73
|
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
74
74
|
when :invalid_rule, :invalid_flow, :no_rule
|
75
75
|
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
76
76
|
else
|
77
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{
|
77
|
+
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -32,27 +32,27 @@ module Datadog
|
|
32
32
|
}
|
33
33
|
|
34
34
|
waf_timeout = Datadog::AppSec.settings.waf_timeout
|
35
|
-
|
35
|
+
result = waf_context.run(waf_args, waf_timeout)
|
36
36
|
|
37
37
|
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
when :monitor
|
39
|
+
case result.status
|
40
|
+
when :match
|
42
41
|
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
yield [
|
47
|
-
|
48
|
-
|
42
|
+
|
43
|
+
block = result.actions.include?('block')
|
44
|
+
|
45
|
+
yield [result, block]
|
46
|
+
|
47
|
+
throw(:block, [result, true]) if block
|
48
|
+
when :ok
|
49
49
|
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
50
50
|
when :invalid_call
|
51
51
|
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
52
52
|
when :invalid_rule, :invalid_flow, :no_rule
|
53
53
|
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
54
54
|
else
|
55
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{
|
55
|
+
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -32,27 +32,27 @@ module Datadog
|
|
32
32
|
}
|
33
33
|
|
34
34
|
waf_timeout = Datadog::AppSec.settings.waf_timeout
|
35
|
-
|
35
|
+
result = waf_context.run(waf_args, waf_timeout)
|
36
36
|
|
37
37
|
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
when :monitor
|
39
|
+
case result.status
|
40
|
+
when :match
|
42
41
|
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
yield [
|
47
|
-
|
48
|
-
|
42
|
+
|
43
|
+
block = result.actions.include?('block')
|
44
|
+
|
45
|
+
yield [result, block]
|
46
|
+
|
47
|
+
throw(:block, [result, true]) if block
|
48
|
+
when :ok
|
49
49
|
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
50
50
|
when :invalid_call
|
51
51
|
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
52
52
|
when :invalid_rule, :invalid_flow, :no_rule
|
53
53
|
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
54
54
|
else
|
55
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{
|
55
|
+
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -47,6 +47,9 @@ module Datadog
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def self.form_hash(request)
|
50
|
+
# force form data processing
|
51
|
+
request.POST if request.form_data?
|
52
|
+
|
50
53
|
# usually Hash<String,String> but can be a more complex
|
51
54
|
# Hash<String,String||Array||Hash> when e.g coming from JSON
|
52
55
|
request.env['rack.request.form_hash']
|
@@ -6,6 +6,9 @@ require_relative '../../instrumentation/gateway'
|
|
6
6
|
require_relative '../../processor'
|
7
7
|
require_relative '../../assets'
|
8
8
|
|
9
|
+
require_relative '../../../tracing/client_ip'
|
10
|
+
require_relative '../../../tracing/contrib/rack/header_collection'
|
11
|
+
|
9
12
|
module Datadog
|
10
13
|
module AppSec
|
11
14
|
module Contrib
|
@@ -21,7 +24,7 @@ module Datadog
|
|
21
24
|
end
|
22
25
|
|
23
26
|
def call(env)
|
24
|
-
return @app.call(env) unless @processor.ready?
|
27
|
+
return @app.call(env) unless Datadog.configuration.appsec.enabled && @processor.ready?
|
25
28
|
|
26
29
|
# TODO: handle exceptions, except for @app.call
|
27
30
|
|
@@ -30,7 +33,7 @@ module Datadog
|
|
30
33
|
env['datadog.waf.context'] = context
|
31
34
|
request = ::Rack::Request.new(env)
|
32
35
|
|
33
|
-
add_appsec_tags
|
36
|
+
add_appsec_tags(active_trace, active_span, env)
|
34
37
|
|
35
38
|
request_return, request_response = Instrumentation.gateway.push('rack.request', request) do
|
36
39
|
@app.call(env)
|
@@ -56,7 +59,8 @@ module Datadog
|
|
56
59
|
|
57
60
|
request_return
|
58
61
|
ensure
|
59
|
-
add_waf_runtime_tags(context) if context
|
62
|
+
add_waf_runtime_tags(active_trace, context) if context
|
63
|
+
context.finalize if context
|
60
64
|
end
|
61
65
|
|
62
66
|
private
|
@@ -69,41 +73,60 @@ module Datadog
|
|
69
73
|
Datadog::Tracing.active_trace
|
70
74
|
end
|
71
75
|
|
72
|
-
def
|
73
|
-
|
76
|
+
def active_span
|
77
|
+
# TODO: factor out tracing availability detection
|
78
|
+
|
79
|
+
return unless defined?(Datadog::Tracing)
|
80
|
+
|
81
|
+
Datadog::Tracing.active_span
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_appsec_tags(trace, span, env)
|
85
|
+
return unless trace
|
86
|
+
|
87
|
+
trace.set_tag('_dd.appsec.enabled', 1)
|
88
|
+
trace.set_tag('_dd.runtime_family', 'ruby')
|
89
|
+
trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
|
74
90
|
|
75
|
-
|
76
|
-
|
77
|
-
|
91
|
+
if span && span.get_tag(Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).nil?
|
92
|
+
request_header_collection = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env)
|
93
|
+
|
94
|
+
# always collect client ip, as this is part of AppSec provided functionality
|
95
|
+
Datadog::Tracing::ClientIp.set_client_ip_tag!(
|
96
|
+
span,
|
97
|
+
headers: request_header_collection,
|
98
|
+
remote_ip: env['REMOTE_ADDR']
|
99
|
+
)
|
100
|
+
end
|
78
101
|
|
79
102
|
if @processor.ruleset_info
|
80
|
-
|
103
|
+
trace.set_tag('_dd.appsec.event_rules.version', @processor.ruleset_info[:version])
|
81
104
|
|
82
105
|
unless @oneshot_tags_sent
|
83
106
|
# Small race condition, but it's inoccuous: worst case the tags
|
84
107
|
# are sent a couple of times more than expected
|
85
108
|
@oneshot_tags_sent = true
|
86
109
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
110
|
+
trace.set_tag('_dd.appsec.event_rules.loaded', @processor.ruleset_info[:loaded].to_f)
|
111
|
+
trace.set_tag('_dd.appsec.event_rules.error_count', @processor.ruleset_info[:failed].to_f)
|
112
|
+
trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(@processor.ruleset_info[:errors]))
|
113
|
+
trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(@processor.addresses))
|
91
114
|
|
92
115
|
# Ensure these tags reach the backend
|
93
|
-
|
116
|
+
trace.keep!
|
94
117
|
end
|
95
118
|
end
|
96
119
|
end
|
97
120
|
|
98
|
-
def add_waf_runtime_tags(context)
|
99
|
-
return unless
|
121
|
+
def add_waf_runtime_tags(trace, context)
|
122
|
+
return unless trace
|
100
123
|
return unless context
|
101
124
|
|
102
|
-
|
125
|
+
trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
|
103
126
|
|
104
127
|
# these tags expect time in us
|
105
|
-
|
106
|
-
|
128
|
+
trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
|
129
|
+
trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
|
107
130
|
end
|
108
131
|
end
|
109
132
|
end
|
@@ -13,7 +13,7 @@ module Datadog
|
|
13
13
|
# Watcher for Rails gateway events
|
14
14
|
module Watcher
|
15
15
|
def self.watch
|
16
|
-
Instrumentation.gateway.watch('rails.request.action') do |stack, request|
|
16
|
+
Instrumentation.gateway.watch('rails.request.action', :appsec) do |stack, request|
|
17
17
|
block = false
|
18
18
|
event = nil
|
19
19
|
waf_context = request.env['datadog.waf.context']
|
@@ -22,23 +22,24 @@ module Datadog
|
|
22
22
|
trace = active_trace
|
23
23
|
span = active_span
|
24
24
|
|
25
|
-
Rails::Reactive::Action.subscribe(op, waf_context) do |
|
26
|
-
|
27
|
-
if record
|
25
|
+
Rails::Reactive::Action.subscribe(op, waf_context) do |result, _block|
|
26
|
+
if result.status == :match
|
28
27
|
# TODO: should this hash be an Event instance instead?
|
29
28
|
event = {
|
30
29
|
waf_result: result,
|
31
30
|
trace: trace,
|
32
31
|
span: span,
|
33
32
|
request: request,
|
34
|
-
|
33
|
+
actions: result.actions
|
35
34
|
}
|
36
35
|
|
36
|
+
span.set_tag('appsec.event', 'true') if span
|
37
|
+
|
37
38
|
waf_context.events << event
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
|
42
|
+
_result, block = Rails::Reactive::Action.publish(op, request)
|
42
43
|
end
|
43
44
|
|
44
45
|
next [nil, [[:block, event]]] if block
|
@@ -36,27 +36,27 @@ module Datadog
|
|
36
36
|
}
|
37
37
|
|
38
38
|
waf_timeout = Datadog::AppSec.settings.waf_timeout
|
39
|
-
|
39
|
+
result = waf_context.run(waf_args, waf_timeout)
|
40
40
|
|
41
41
|
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
when :monitor
|
43
|
+
case result.status
|
44
|
+
when :match
|
46
45
|
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
yield [
|
51
|
-
|
52
|
-
|
46
|
+
|
47
|
+
block = result.actions.include?('block')
|
48
|
+
|
49
|
+
yield [result, block]
|
50
|
+
|
51
|
+
throw(:block, [result, true]) if block
|
52
|
+
when :ok
|
53
53
|
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
54
54
|
when :invalid_call
|
55
55
|
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
56
56
|
when :invalid_rule, :invalid_flow, :no_rule
|
57
57
|
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
58
58
|
else
|
59
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{
|
59
|
+
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
@@ -7,6 +7,9 @@ module Datadog
|
|
7
7
|
# Normalized extration of data from ActionDispatch::Request
|
8
8
|
module Request
|
9
9
|
def self.parsed_body(request)
|
10
|
+
# force body parameter parsing, which is done lazily by Rails
|
11
|
+
request.parameters
|
12
|
+
|
10
13
|
# usually Hash<String,String> but can be a more complex
|
11
14
|
# Hash<String,String||Array||Hash> when e.g coming from JSON or
|
12
15
|
# with Rails advanced param square bracket parsing
|
@@ -15,7 +15,7 @@ module Datadog
|
|
15
15
|
module Watcher
|
16
16
|
# rubocop:disable Metrics/MethodLength
|
17
17
|
def self.watch
|
18
|
-
Instrumentation.gateway.watch('sinatra.request.dispatch') do |stack, request|
|
18
|
+
Instrumentation.gateway.watch('sinatra.request.dispatch', :appsec) do |stack, request|
|
19
19
|
block = false
|
20
20
|
event = nil
|
21
21
|
waf_context = request.env['datadog.waf.context']
|
@@ -24,23 +24,24 @@ module Datadog
|
|
24
24
|
trace = active_trace
|
25
25
|
span = active_span
|
26
26
|
|
27
|
-
Rack::Reactive::RequestBody.subscribe(op, waf_context) do |
|
28
|
-
|
29
|
-
if record
|
27
|
+
Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
|
28
|
+
if result.status == :match
|
30
29
|
# TODO: should this hash be an Event instance instead?
|
31
30
|
event = {
|
32
31
|
waf_result: result,
|
33
32
|
trace: trace,
|
34
33
|
span: span,
|
35
34
|
request: request,
|
36
|
-
|
35
|
+
actions: result.actions
|
37
36
|
}
|
38
37
|
|
38
|
+
span.set_tag('appsec.event', 'true') if span
|
39
|
+
|
39
40
|
waf_context.events << event
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
+
_result, block = Rack::Reactive::RequestBody.publish(op, request)
|
44
45
|
end
|
45
46
|
|
46
47
|
next [nil, [[:block, event]]] if block
|
@@ -55,7 +56,7 @@ module Datadog
|
|
55
56
|
[ret, res]
|
56
57
|
end
|
57
58
|
|
58
|
-
Instrumentation.gateway.watch('sinatra.request.routed') do |stack, (request, route_params)|
|
59
|
+
Instrumentation.gateway.watch('sinatra.request.routed', :appsec) do |stack, (request, route_params)|
|
59
60
|
block = false
|
60
61
|
event = nil
|
61
62
|
waf_context = request.env['datadog.waf.context']
|
@@ -64,23 +65,24 @@ module Datadog
|
|
64
65
|
trace = active_trace
|
65
66
|
span = active_span
|
66
67
|
|
67
|
-
Sinatra::Reactive::Routed.subscribe(op, waf_context) do |
|
68
|
-
|
69
|
-
if record
|
68
|
+
Sinatra::Reactive::Routed.subscribe(op, waf_context) do |result, _block|
|
69
|
+
if result.status == :match
|
70
70
|
# TODO: should this hash be an Event instance instead?
|
71
71
|
event = {
|
72
72
|
waf_result: result,
|
73
73
|
trace: trace,
|
74
74
|
span: span,
|
75
75
|
request: request,
|
76
|
-
|
76
|
+
actions: result.actions
|
77
77
|
}
|
78
78
|
|
79
|
+
span.set_tag('appsec.event', 'true') if span
|
80
|
+
|
79
81
|
waf_context.events << event
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
83
|
-
|
85
|
+
_result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
|
84
86
|
end
|
85
87
|
|
86
88
|
next [nil, [[:block, event]]] if block
|
@@ -31,27 +31,27 @@ module Datadog
|
|
31
31
|
}
|
32
32
|
|
33
33
|
waf_timeout = Datadog::AppSec.settings.waf_timeout
|
34
|
-
|
34
|
+
result = waf_context.run(waf_args, waf_timeout)
|
35
35
|
|
36
36
|
Datadog.logger.debug { "WAF TIMEOUT: #{result.inspect}" } if result.timeout
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
when :monitor
|
38
|
+
case result.status
|
39
|
+
when :match
|
41
40
|
Datadog.logger.debug { "WAF: #{result.inspect}" }
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
yield [
|
46
|
-
|
47
|
-
|
41
|
+
|
42
|
+
block = result.actions.include?('block')
|
43
|
+
|
44
|
+
yield [result, block]
|
45
|
+
|
46
|
+
throw(:block, [result, true]) if block
|
47
|
+
when :ok
|
48
48
|
Datadog.logger.debug { "WAF OK: #{result.inspect}" }
|
49
49
|
when :invalid_call
|
50
50
|
Datadog.logger.debug { "WAF CALL ERROR: #{result.inspect}" }
|
51
51
|
when :invalid_rule, :invalid_flow, :no_rule
|
52
52
|
Datadog.logger.debug { "WAF RULE ERROR: #{result.inspect}" }
|
53
53
|
else
|
54
|
-
Datadog.logger.debug { "WAF UNKNOWN: #{
|
54
|
+
Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
data/lib/datadog/appsec/event.rb
CHANGED
@@ -51,9 +51,7 @@ module Datadog
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
# rubocop:disable Metrics/AbcSize
|
55
|
-
# rubocop:disable Metrics/MethodLength
|
56
|
-
def self.record_via_span(*events)
|
54
|
+
def self.record_via_span(*events) # rubocop:disable Metrics/AbcSize
|
57
55
|
events.group_by { |e| e[:trace] }.each do |trace, event_group|
|
58
56
|
unless trace
|
59
57
|
Datadog.logger.debug { "{ error: 'no trace: cannot record', event_group: #{event_group.inspect}}" }
|
@@ -64,10 +62,6 @@ module Datadog
|
|
64
62
|
|
65
63
|
# prepare and gather tags to apply
|
66
64
|
trace_tags = event_group.each_with_object({}) do |event, tags|
|
67
|
-
span = event[:span]
|
68
|
-
|
69
|
-
span.set_tag('appsec.event', 'true') if span
|
70
|
-
|
71
65
|
# TODO: assume HTTP request context for now
|
72
66
|
|
73
67
|
if (request = event[:request])
|
@@ -81,9 +75,7 @@ module Datadog
|
|
81
75
|
|
82
76
|
tags['http.host'] = request.host
|
83
77
|
tags['http.useragent'] = request.user_agent
|
84
|
-
tags['network.client.ip'] = request.
|
85
|
-
|
86
|
-
# tags['actor.ip'] = request.ip # TODO: uses client IP resolution algorithm
|
78
|
+
tags['network.client.ip'] = request.env['REMOTE_ADDR'] if request.env['REMOTE_ADDR']
|
87
79
|
end
|
88
80
|
|
89
81
|
if (response = event[:response])
|
@@ -114,8 +106,6 @@ module Datadog
|
|
114
106
|
end
|
115
107
|
end
|
116
108
|
end
|
117
|
-
# rubocop:enable Metrics/MethodLength
|
118
|
-
# rubocop:enable Metrics/AbcSize
|
119
109
|
end
|
120
110
|
end
|
121
111
|
end
|
@@ -6,6 +6,20 @@ module Datadog
|
|
6
6
|
module Instrumentation
|
7
7
|
# Instrumentation gateway implementation
|
8
8
|
class Gateway
|
9
|
+
# Instrumentation gateway middleware
|
10
|
+
class Middleware
|
11
|
+
attr_reader :key, :block
|
12
|
+
|
13
|
+
def initialize(key, &block)
|
14
|
+
@key = key
|
15
|
+
@block = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(*args, **kwargs, &block)
|
19
|
+
@block.call(*args, **kwargs, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
9
23
|
def initialize
|
10
24
|
@middlewares = Hash.new { |h, k| h[k] = [] }
|
11
25
|
end
|
@@ -31,8 +45,8 @@ module Datadog
|
|
31
45
|
stack.call(env)
|
32
46
|
end
|
33
47
|
|
34
|
-
def watch(name, &block)
|
35
|
-
@middlewares[name] << block
|
48
|
+
def watch(name, key, &block)
|
49
|
+
@middlewares[name] << Middleware.new(key, &block) unless @middlewares[name].any? { |m| m.key == key }
|
36
50
|
end
|
37
51
|
end
|
38
52
|
|