ddtrace 1.5.0 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -1
  3. data/LICENSE-3rdparty.csv +1 -0
  4. data/lib/datadog/appsec/assets/waf_rules/recommended.json +1169 -275
  5. data/lib/datadog/appsec/assets/waf_rules/risky.json +78 -78
  6. data/lib/datadog/appsec/assets/waf_rules/strict.json +278 -88
  7. data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +25 -18
  8. data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
  9. data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +11 -11
  10. data/lib/datadog/appsec/contrib/rack/reactive/response.rb +11 -11
  11. data/lib/datadog/appsec/contrib/rack/request.rb +3 -0
  12. data/lib/datadog/appsec/contrib/rack/request_middleware.rb +42 -19
  13. data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -6
  14. data/lib/datadog/appsec/contrib/rails/reactive/action.rb +11 -11
  15. data/lib/datadog/appsec/contrib/rails/request.rb +3 -0
  16. data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +14 -12
  17. data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +11 -11
  18. data/lib/datadog/appsec/event.rb +2 -12
  19. data/lib/datadog/appsec/instrumentation/gateway.rb +16 -2
  20. data/lib/datadog/appsec/processor.rb +18 -2
  21. data/lib/datadog/core/configuration/settings.rb +19 -5
  22. data/lib/datadog/tracing/client_ip.rb +11 -0
  23. data/lib/datadog/tracing/configuration/ext.rb +2 -1
  24. data/lib/datadog/tracing/contrib/rack/middlewares.rb +3 -1
  25. data/lib/datadog/tracing/contrib/utils/quantization/http.rb +14 -6
  26. data/lib/ddtrace/transport/traces.rb +2 -0
  27. data/lib/ddtrace/version.rb +1 -1
  28. 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 |action, result, _block|
30
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Rack::Reactive::Request.publish(op, request)
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 |action, result, _block|
70
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Rack::Reactive::Response.publish(op, response)
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 |action, result, _block|
110
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
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
- action, result = waf_context.run(waf_args, waf_timeout)
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
- # TODO: encapsulate return array in a type
62
- case action
63
- when :monitor
61
+ case result.status
62
+ when :match
64
63
  Datadog.logger.debug { "WAF: #{result.inspect}" }
65
- yield [action, result, false]
66
- when :block
67
- Datadog.logger.debug { "WAF: #{result.inspect}" }
68
- yield [action, result, true]
69
- throw(:block, [action, result, true])
70
- when :good
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: #{action.inspect} #{result.inspect}" }
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
- action, result = waf_context.run(waf_args, waf_timeout)
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
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
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: #{action.inspect} #{result.inspect}" }
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
- action, result = waf_context.run(waf_args, waf_timeout)
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
- # TODO: encapsulate return array in a type
40
- case action
41
- when :monitor
39
+ case result.status
40
+ when :match
42
41
  Datadog.logger.debug { "WAF: #{result.inspect}" }
43
- yield [action, result, false]
44
- when :block
45
- Datadog.logger.debug { "WAF: #{result.inspect}" }
46
- yield [action, result, true]
47
- throw(:block, [action, result, true])
48
- when :good
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: #{action.inspect} #{result.inspect}" }
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 add_appsec_tags
73
- return unless active_trace
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
- active_trace.set_tag('_dd.appsec.enabled', 1)
76
- active_trace.set_tag('_dd.runtime_family', 'ruby')
77
- active_trace.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING)
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
- active_trace.set_tag('_dd.appsec.event_rules.version', @processor.ruleset_info[:version])
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
- active_trace.set_tag('_dd.appsec.event_rules.loaded', @processor.ruleset_info[:loaded].to_f)
88
- active_trace.set_tag('_dd.appsec.event_rules.error_count', @processor.ruleset_info[:failed].to_f)
89
- active_trace.set_tag('_dd.appsec.event_rules.errors', JSON.dump(@processor.ruleset_info[:errors]))
90
- active_trace.set_tag('_dd.appsec.event_rules.addresses', JSON.dump(@processor.addresses))
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
- active_trace.keep!
116
+ trace.keep!
94
117
  end
95
118
  end
96
119
  end
97
120
 
98
- def add_waf_runtime_tags(context)
99
- return unless active_trace
121
+ def add_waf_runtime_tags(trace, context)
122
+ return unless trace
100
123
  return unless context
101
124
 
102
- active_trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
125
+ trace.set_tag('_dd.appsec.waf.timeouts', context.timeouts)
103
126
 
104
127
  # these tags expect time in us
105
- active_trace.set_tag('_dd.appsec.waf.duration', context.time_ns / 1000.0)
106
- active_trace.set_tag('_dd.appsec.waf.duration_ext', context.time_ext_ns / 1000.0)
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 |action, result, _block|
26
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Rails::Reactive::Action.publish(op, request)
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
- action, result = waf_context.run(waf_args, waf_timeout)
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
- # TODO: encapsulate return array in a type
44
- case action
45
- when :monitor
43
+ case result.status
44
+ when :match
46
45
  Datadog.logger.debug { "WAF: #{result.inspect}" }
47
- yield [action, result, false]
48
- when :block
49
- Datadog.logger.debug { "WAF: #{result.inspect}" }
50
- yield [action, result, true]
51
- throw(:block, [action, result, true])
52
- when :good
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: #{action.inspect} #{result.inspect}" }
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 |action, result, _block|
28
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Rack::Reactive::RequestBody.publish(op, request)
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 |action, result, _block|
68
- record = [:block, :monitor].include?(action)
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
- action: action
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
- _action, _result, block = Sinatra::Reactive::Routed.publish(op, [request, route_params])
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
- action, result = waf_context.run(waf_args, waf_timeout)
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
- # TODO: encapsulate return array in a type
39
- case action
40
- when :monitor
38
+ case result.status
39
+ when :match
41
40
  Datadog.logger.debug { "WAF: #{result.inspect}" }
42
- yield [action, result, false]
43
- when :block
44
- Datadog.logger.debug { "WAF: #{result.inspect}" }
45
- yield [action, result, true]
46
- throw(:block, [action, result, true])
47
- when :good
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: #{action.inspect} #{result.inspect}" }
54
+ Datadog.logger.debug { "WAF UNKNOWN: #{result.status.inspect} #{result.inspect}" }
55
55
  end
56
56
  end
57
57
  end
@@ -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.ip
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