Skip to content

Commit

Permalink
block request when user ID matches
Browse files Browse the repository at this point in the history
  • Loading branch information
GustavoCaso committed Feb 23, 2023
1 parent 923f2a9 commit ca6ed3b
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 3 deletions.
7 changes: 5 additions & 2 deletions lib/datadog/appsec/contrib/rack/request_middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'json'

require_relative '../../ext'
require_relative '../../instrumentation/gateway'
require_relative '../../processor'
require_relative '../../response'
Expand Down Expand Up @@ -38,8 +39,10 @@ def call(env)

add_appsec_tags(processor, active_trace, active_span, env)

request_return, request_response = Instrumentation.gateway.push('rack.request', request) do
@app.call(env)
request_return, request_response = catch(::Datadog::AppSec::Ext::INTERRUPT) do
Instrumentation.gateway.push('rack.request', request) do
@app.call(env)
end
end

if request_response && request_response.any? { |action, _event| action == :block }
Expand Down
10 changes: 10 additions & 0 deletions lib/datadog/appsec/ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# typed: false
# frozen_string_literal: true

module Datadog
module AppSec
module Ext
INTERRUPT = :datadog_appsec_interrupt
end
end
end
3 changes: 2 additions & 1 deletion lib/datadog/appsec/monitor/gateway/watcher.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# typed: false
# frozen_string_literal: true

require_relative '../../ext'
require_relative '../../instrumentation/gateway'
require_relative '../../reactive/operation'
require_relative '../reactive/set_user'
Expand Down Expand Up @@ -48,7 +49,7 @@ def watch_user_id(gateway = Instrumentation.gateway)
_result, block = Monitor::Reactive::SetUser.publish(op, user)
end

next [nil, [[:block, event]]] if block
throw(Datadog::AppSec::Ext::INTERRUPT, [nil, [:block, event]]) if block

ret, res = stack.call(user)

Expand Down
5 changes: 5 additions & 0 deletions lib/datadog/kit/identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ def self.set_user(trace, id:, email: nil, name: nil, session_id: nil, role: nil,
others.each do |k, v|
trace.set_tag("usr.#{k}", v) unless v.nil?
end

if Datadog.configuration.appsec.enabled
user = OpenStruct.new(id: id)
::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
end
end
# rubocop:enable Metrics/PerceivedComplexity
# rubocop:enable Metrics/CyclomaticComplexity
Expand Down
7 changes: 7 additions & 0 deletions sig/datadog/appsec/ext.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Datadog
module AppSec
module Ext
INTERRUPT: Symbol
end
end
end
31 changes: 31 additions & 0 deletions spec/datadog/appsec/contrib/rack/integration_test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
let(:appsec_enabled) { true }
let(:tracing_enabled) { true }
let(:appsec_ip_denylist) { nil }
let(:appsec_user_id_denylist) { nil }
let(:appsec_ruleset) { :recommended }

let(:crs_942_100) do
Expand Down Expand Up @@ -133,6 +134,7 @@
c.appsec.enabled = appsec_enabled
c.appsec.instrument :rack
c.appsec.ip_denylist = appsec_ip_denylist
c.appsec.user_id_denylist = appsec_user_id_denylist
c.appsec.ruleset = appsec_ruleset
end
end
Expand Down Expand Up @@ -293,6 +295,15 @@
end
)
end

map '/set_user' do
run(
proc do |_env|
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
[200, { 'Content-Type' => 'text/html' }, ['OK']]
end
)
end
end
end

Expand Down Expand Up @@ -382,6 +393,26 @@
it_behaves_like 'a trace with AppSec events'
end
end

context 'with user blocking ID' do
let(:url) { '/set_user' }

it { is_expected.to be_ok }

it_behaves_like 'a GET 200 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace without AppSec events'

context 'with an event-triggering user ID' do
let(:appsec_user_id_denylist) { ['blocked-user-id'] }

it { is_expected.to be_forbidden }

it_behaves_like 'a GET 403 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace with AppSec events'
end
end
end

describe 'POST request' do
Expand Down
28 changes: 28 additions & 0 deletions spec/datadog/appsec/contrib/rails/integration_test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
let(:appsec_enabled) { true }
let(:tracing_enabled) { true }
let(:appsec_ip_denylist) { nil }
let(:appsec_user_id_denylist) { nil }
let(:appsec_ruleset) { :recommended }

let(:crs_942_100) do
Expand Down Expand Up @@ -89,6 +90,7 @@
c.appsec.enabled = appsec_enabled
c.appsec.instrument :rails
c.appsec.ip_denylist = appsec_ip_denylist
c.appsec.user_id_denylist = appsec_user_id_denylist
c.appsec.ruleset = appsec_ruleset

# TODO: test with c.appsec.instrument :rack
Expand Down Expand Up @@ -118,6 +120,11 @@
def success
head :ok
end

def set_user
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
head :ok
end
end
)
end
Expand Down Expand Up @@ -244,6 +251,7 @@ def success
{
'/success' => 'test#success',
[:post, '/success'] => 'test#success',
'/set_user' => 'test#set_user',
}
end

Expand Down Expand Up @@ -347,6 +355,26 @@ def success
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace with AppSec events'
end

context 'with user blocking ID' do
let(:url) { '/set_user' }

it { is_expected.to be_ok }

it_behaves_like 'a GET 200 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace without AppSec events'

context 'with an event-triggering user ID' do
let(:appsec_user_id_denylist) { ['blocked-user-id'] }

it { is_expected.to be_forbidden }

it_behaves_like 'a GET 403 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace with AppSec events'
end
end
end

describe 'POST request' do
Expand Down
27 changes: 27 additions & 0 deletions spec/datadog/appsec/contrib/sinatra/integration_test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
let(:appsec_enabled) { true }
let(:tracing_enabled) { true }
let(:appsec_ip_denylist) { nil }
let(:appsec_user_id_denylist) { nil }
let(:appsec_ruleset) { :recommended }

let(:crs_942_100) do
Expand Down Expand Up @@ -101,6 +102,7 @@
c.appsec.enabled = appsec_enabled
c.appsec.instrument :sinatra
c.appsec.ip_denylist = appsec_ip_denylist
c.appsec.user_id_denylist = appsec_user_id_denylist
c.appsec.ruleset = appsec_ruleset

# TODO: test with c.appsec.instrument :rack
Expand Down Expand Up @@ -254,6 +256,11 @@
post '/success' do
'ok'
end

get '/set_user' do
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
'ok'
end
end
end

Expand Down Expand Up @@ -359,6 +366,26 @@
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace with AppSec events'
end

context 'with user blocking ID' do
let(:url) { '/set_user' }

it { is_expected.to be_ok }

it_behaves_like 'a GET 200 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace without AppSec events'

context 'with an event-triggering user ID' do
let(:appsec_user_id_denylist) { ['blocked-user-id'] }

it { is_expected.to be_forbidden }

it_behaves_like 'a GET 403 span'
it_behaves_like 'a trace with AppSec tags'
it_behaves_like 'a trace with AppSec events'
end
end
end

describe 'POST request' do
Expand Down
21 changes: 21 additions & 0 deletions spec/datadog/kit/identity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,26 @@
expect { described_class.set_user(trace_op, id: 42, foo: 42) }.to raise_error(TypeError)
end
end

context 'appsec' do
after { Datadog.configuration.appsec.send(:reset!) }

context 'when is enabled' do
it 'instruments the user information to appsec' do
Datadog.configuration.appsec.enabled = true
user = OpenStruct.new(id: '42')
expect_any_instance_of(Datadog::AppSec::Instrumentation::Gateway).to receive(:push).with('identity.set_user', user)
described_class.set_user(trace_op, id: '42')
end
end

context 'when is disabled' do
it 'does not instrument the user information to appsec' do
Datadog.configuration.appsec.enabled = false
expect_any_instance_of(Datadog::AppSec::Instrumentation::Gateway).to_not receive(:push).with('identity.set_user')
described_class.set_user(trace_op, id: '42')
end
end
end
end
end

0 comments on commit ca6ed3b

Please sign in to comment.