Skip to content

Commit

Permalink
Merge pull request #2617 from DataDog/appsec-pass-user-id-to-waf
Browse files Browse the repository at this point in the history
[APPSEC-8115] create AppSec::Monitor to subscribe to internal app sec events
  • Loading branch information
GustavoCaso authored Feb 23, 2023
2 parents 9d4eae6 + 4abbd89 commit 923f2a9
Show file tree
Hide file tree
Showing 19 changed files with 724 additions and 234 deletions.
21 changes: 0 additions & 21 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,6 @@ step_rubocop: &step_rubocop
# The workaround is to use `cpu.shares / 1024`:
# https://discuss.circleci.com/t/environment-variable-set-to-the-number-of-available-cpus/32670/4
command: PARALLEL_PROCESSOR_COUNT=$((`cat /sys/fs/cgroup/cpu/cpu.shares` / 1024)) bundle exec rake rubocop
step_sorbet_type_checker: &step_sorbet_type_checker
run:
name: Run sorbet type checker
command: bundle exec rake typecheck
step_appraisal_install: &step_appraisal_install
run:
name: Install Appraisal gems
Expand Down Expand Up @@ -348,16 +344,6 @@ orbs:
keys:
- bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/ddtrace/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
- *step_rubocop
sorbet_type_checker:
<<: *test_job_default
steps:
- restore_cache:
keys:
- '{{ .Environment.CIRCLE_CACHE_VERSION }}-bundled-repo-<<parameters.ruby_version>>-{{ .Environment.CIRCLE_SHA1 }}'
- restore_cache:
keys:
- bundle-{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ checksum ".circleci/images/primary/binary_version" }}-<<parameters.ruby_version>>-{{ checksum "lib/ddtrace/version.rb" }}-{{ .Branch }}-{{ checksum ".circleci/bundle_checksum" }}
- *step_sorbet_type_checker
coverage:
<<: *test_job_default
steps:
Expand Down Expand Up @@ -564,11 +550,6 @@ workflows:
name: lint
requires:
- build-2.7
- orb/sorbet_type_checker:
<<: *config-2_7-small
name: sorbet_type_checker
requires:
- build-2.7
- orb/coverage:
<<: *config-2_7-small
name: coverage
Expand Down Expand Up @@ -766,7 +747,6 @@ workflows:
<<: *filters_all_branches_and_tags
requires:
- lint
- sorbet_type_checker
- test-2.1
- test-2.2
- test-2.3
Expand All @@ -786,7 +766,6 @@ workflows:
<<: *filters_only_release_tags
requires:
- lint
- sorbet_type_checker
- test-2.1
- test-2.2
- test-2.3
Expand Down
2 changes: 2 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ target :appsec do
# check 'lib/datadog/kit'

ignore 'lib/datadog/appsec/contrib'
ignore 'lib/datadog/appsec/monitor'
ignore 'lib/datadog/appsec/component.rb'

library 'pathname', 'set'
library 'cgi'
Expand Down
218 changes: 111 additions & 107 deletions lib/datadog/appsec/contrib/rack/gateway/watcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,140 +14,144 @@ module Rack
module Gateway
# Watcher for Rack gateway events
module Watcher
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
def self.watch
Instrumentation.gateway.watch('rack.request', :appsec) do |stack, request|
block = false
event = nil
waf_context = request.env['datadog.waf.context']

AppSec::Reactive::Operation.new('rack.request') do |op|
trace = active_trace
span = active_span

Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
request: request,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
class << self
def watch
gateway = Instrumentation.gateway

watch_request(gateway)
watch_response(gateway)
watch_request_body(gateway)
end

def watch_request(gateway = Instrumentation.gateway)
gateway.watch('rack.request', :appsec) do |stack, request|
block = false
event = nil
waf_context = request.env['datadog.waf.context']

AppSec::Reactive::Operation.new('rack.request') do |op|
trace = active_trace
span = active_span

Rack::Reactive::Request.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
request: request,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
end
end

_result, block = Rack::Reactive::Request.publish(op, request)
end

_result, block = Rack::Reactive::Request.publish(op, request)
end
next [nil, [[:block, event]]] if block

next [nil, [[:block, event]]] if block
ret, res = stack.call(request)

ret, res = stack.call(request)
if event
res ||= []
res << [:monitor, event]
end

if event
res ||= []
res << [:monitor, event]
[ret, res]
end

[ret, res]
end

Instrumentation.gateway.watch('rack.response', :appsec) do |stack, response|
block = false
event = nil
waf_context = response.instance_eval { @waf_context }

AppSec::Reactive::Operation.new('rack.response') do |op|
trace = active_trace
span = active_span

Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
response: response,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
def watch_response(gateway = Instrumentation.gateway)
gateway.watch('rack.response', :appsec) do |stack, response|
block = false
event = nil
waf_context = response.instance_eval { @waf_context }

AppSec::Reactive::Operation.new('rack.response') do |op|
trace = active_trace
span = active_span

Rack::Reactive::Response.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
response: response,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
end
end

_result, block = Rack::Reactive::Response.publish(op, response)
end

_result, block = Rack::Reactive::Response.publish(op, response)
end
next [nil, [[:block, event]]] if block

next [nil, [[:block, event]]] if block
ret, res = stack.call(response)

ret, res = stack.call(response)
if event
res ||= []
res << [:monitor, event]
end

if event
res ||= []
res << [:monitor, event]
[ret, res]
end

[ret, res]
end

Instrumentation.gateway.watch('rack.request.body', :appsec) do |stack, request|
block = false
event = nil
waf_context = request.env['datadog.waf.context']

AppSec::Reactive::Operation.new('rack.request.body') do |op|
trace = active_trace
span = active_span

Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
request: request,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
def watch_request_body(gateway = Instrumentation.gateway)
gateway.watch('rack.request.body', :appsec) do |stack, request|
block = false
event = nil
waf_context = request.env['datadog.waf.context']

AppSec::Reactive::Operation.new('rack.request.body') do |op|
trace = active_trace
span = active_span

Rack::Reactive::RequestBody.subscribe(op, waf_context) do |result, _block|
if result.status == :match
# TODO: should this hash be an Event instance instead?
event = {
waf_result: result,
trace: trace,
span: span,
request: request,
actions: result.actions
}

span.set_tag('appsec.event', 'true') if span

waf_context.events << event
end
end

_result, block = Rack::Reactive::RequestBody.publish(op, request)
end

_result, block = Rack::Reactive::RequestBody.publish(op, request)
end
next [nil, [[:block, event]]] if block

next [nil, [[:block, event]]] if block
ret, res = stack.call(request)

ret, res = stack.call(request)
if event
res ||= []
res << [:monitor, event]
end

if event
res ||= []
res << [:monitor, event]
[ret, res]
end

[ret, res]
end
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize

class << self
private

def active_trace
Expand Down
2 changes: 2 additions & 0 deletions lib/datadog/appsec/contrib/rack/patcher.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# typed: ignore

require_relative '../patcher'
require_relative '../../monitor'
require_relative 'gateway/watcher'

module Datadog
Expand All @@ -22,6 +23,7 @@ def target_version
end

def patch
Monitor::Gateway::Watcher.watch
Gateway::Watcher.watch
Patcher.instance_variable_set(:@patched, true)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/datadog/appsec/contrib/rack/request_middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def call(env)

# TODO: handle exceptions, except for @app.call

context = processor.new_context
context = processor.activate_context
env['datadog.waf.context'] = context

request = ::Rack::Request.new(env)
Expand Down Expand Up @@ -68,7 +68,7 @@ def call(env)
ensure
if context
add_waf_runtime_tags(active_trace, context)
context.finalize
processor.deactivate_context
end
end

Expand Down
Loading

0 comments on commit 923f2a9

Please sign in to comment.