Skip to content

Commit f643f15

Browse files
committed
block request when user ID matches
1 parent 923f2a9 commit f643f15

File tree

8 files changed

+115
-3
lines changed

8 files changed

+115
-3
lines changed

lib/datadog/appsec/contrib/rack/request_middleware.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require 'json'
44

5+
require_relative '../../ext'
56
require_relative '../../instrumentation/gateway'
67
require_relative '../../processor'
78
require_relative '../../response'
@@ -38,8 +39,10 @@ def call(env)
3839

3940
add_appsec_tags(processor, active_trace, active_span, env)
4041

41-
request_return, request_response = Instrumentation.gateway.push('rack.request', request) do
42-
@app.call(env)
42+
request_return, request_response = catch(::Datadog::AppSec::Ext::INTERRUPT) do
43+
Instrumentation.gateway.push('rack.request', request) do
44+
@app.call(env)
45+
end
4346
end
4447

4548
if request_response && request_response.any? { |action, _event| action == :block }

lib/datadog/appsec/ext.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# typed: false
2+
# frozen_string_literal: true
3+
4+
module Datadog
5+
module AppSec
6+
module Ext
7+
INTERRUPT = :datadog_appsec_interrupt
8+
end
9+
end
10+
end

lib/datadog/appsec/monitor/gateway/watcher.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# typed: false
22
# frozen_string_literal: true
33

4+
require_relative '../../ext'
45
require_relative '../../instrumentation/gateway'
56
require_relative '../../reactive/operation'
67
require_relative '../reactive/set_user'
@@ -48,7 +49,7 @@ def watch_user_id(gateway = Instrumentation.gateway)
4849
_result, block = Monitor::Reactive::SetUser.publish(op, user)
4950
end
5051

51-
next [nil, [[:block, event]]] if block
52+
throw(Datadog::AppSec::Ext::INTERRUPT, [nil, [:block, event]]) if block
5253

5354
ret, res = stack.call(user)
5455

lib/datadog/kit/identity.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ def self.set_user(trace, id:, email: nil, name: nil, session_id: nil, role: nil,
5656
others.each do |k, v|
5757
trace.set_tag("usr.#{k}", v) unless v.nil?
5858
end
59+
60+
if ::Datadog::AppSec.enabled?
61+
user = OpenStruct.new(id: id)
62+
::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
63+
end
5964
end
6065
# rubocop:enable Metrics/PerceivedComplexity
6166
# rubocop:enable Metrics/CyclomaticComplexity

sig/datadog/appsec/ext.rbs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Datadog
2+
module AppSec
3+
module Ext
4+
INTERRUPT: Symbol
5+
end
6+
end
7+
end

spec/datadog/appsec/contrib/rack/integration_test_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
let(:appsec_enabled) { true }
2323
let(:tracing_enabled) { true }
2424
let(:appsec_ip_denylist) { nil }
25+
let(:appsec_user_id_denylist) { nil }
2526
let(:appsec_ruleset) { :recommended }
2627

2728
let(:crs_942_100) do
@@ -133,6 +134,7 @@
133134
c.appsec.enabled = appsec_enabled
134135
c.appsec.instrument :rack
135136
c.appsec.ip_denylist = appsec_ip_denylist
137+
c.appsec.user_id_denylist = appsec_user_id_denylist
136138
c.appsec.ruleset = appsec_ruleset
137139
end
138140
end
@@ -293,6 +295,15 @@
293295
end
294296
)
295297
end
298+
299+
map '/set_user' do
300+
run(
301+
proc do |_env|
302+
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
303+
[200, { 'Content-Type' => 'text/html' }, ['OK']]
304+
end
305+
)
306+
end
296307
end
297308
end
298309

@@ -382,6 +393,26 @@
382393
it_behaves_like 'a trace with AppSec events'
383394
end
384395
end
396+
397+
context 'with user blocking ID' do
398+
let(:url) { '/set_user' }
399+
400+
it { is_expected.to be_ok }
401+
402+
it_behaves_like 'a GET 200 span'
403+
it_behaves_like 'a trace with AppSec tags'
404+
it_behaves_like 'a trace without AppSec events'
405+
406+
context 'with an event-triggering user ID' do
407+
let(:appsec_user_id_denylist) { ['blocked-user-id'] }
408+
409+
it { is_expected.to be_forbidden }
410+
411+
it_behaves_like 'a GET 403 span'
412+
it_behaves_like 'a trace with AppSec tags'
413+
it_behaves_like 'a trace with AppSec events'
414+
end
415+
end
385416
end
386417

387418
describe 'POST request' do

spec/datadog/appsec/contrib/rails/integration_test_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
let(:appsec_enabled) { true }
3333
let(:tracing_enabled) { true }
3434
let(:appsec_ip_denylist) { nil }
35+
let(:appsec_user_id_denylist) { nil }
3536
let(:appsec_ruleset) { :recommended }
3637

3738
let(:crs_942_100) do
@@ -89,6 +90,7 @@
8990
c.appsec.enabled = appsec_enabled
9091
c.appsec.instrument :rails
9192
c.appsec.ip_denylist = appsec_ip_denylist
93+
c.appsec.user_id_denylist = appsec_user_id_denylist
9294
c.appsec.ruleset = appsec_ruleset
9395

9496
# TODO: test with c.appsec.instrument :rack
@@ -118,6 +120,11 @@
118120
def success
119121
head :ok
120122
end
123+
124+
def set_user
125+
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
126+
head :ok
127+
end
121128
end
122129
)
123130
end
@@ -244,6 +251,7 @@ def success
244251
{
245252
'/success' => 'test#success',
246253
[:post, '/success'] => 'test#success',
254+
'/set_user' => 'test#set_user',
247255
}
248256
end
249257

@@ -347,6 +355,26 @@ def success
347355
it_behaves_like 'a trace with AppSec tags'
348356
it_behaves_like 'a trace with AppSec events'
349357
end
358+
359+
context 'with user blocking ID' do
360+
let(:url) { '/set_user' }
361+
362+
it { is_expected.to be_ok }
363+
364+
it_behaves_like 'a GET 200 span'
365+
it_behaves_like 'a trace with AppSec tags'
366+
it_behaves_like 'a trace without AppSec events'
367+
368+
context 'with an event-triggering user ID' do
369+
let(:appsec_user_id_denylist) { ['blocked-user-id'] }
370+
371+
it { is_expected.to be_forbidden }
372+
373+
it_behaves_like 'a GET 403 span'
374+
it_behaves_like 'a trace with AppSec tags'
375+
it_behaves_like 'a trace with AppSec events'
376+
end
377+
end
350378
end
351379

352380
describe 'POST request' do

spec/datadog/appsec/contrib/sinatra/integration_test_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
let(:appsec_enabled) { true }
4545
let(:tracing_enabled) { true }
4646
let(:appsec_ip_denylist) { nil }
47+
let(:appsec_user_id_denylist) { nil }
4748
let(:appsec_ruleset) { :recommended }
4849

4950
let(:crs_942_100) do
@@ -101,6 +102,7 @@
101102
c.appsec.enabled = appsec_enabled
102103
c.appsec.instrument :sinatra
103104
c.appsec.ip_denylist = appsec_ip_denylist
105+
c.appsec.user_id_denylist = appsec_user_id_denylist
104106
c.appsec.ruleset = appsec_ruleset
105107

106108
# TODO: test with c.appsec.instrument :rack
@@ -254,6 +256,11 @@
254256
post '/success' do
255257
'ok'
256258
end
259+
260+
get '/set_user' do
261+
Datadog::Kit::Identity.set_user(Datadog::Tracing.active_trace, id: 'blocked-user-id')
262+
'ok'
263+
end
257264
end
258265
end
259266

@@ -359,6 +366,26 @@
359366
it_behaves_like 'a trace with AppSec tags'
360367
it_behaves_like 'a trace with AppSec events'
361368
end
369+
370+
context 'with user blocking ID' do
371+
let(:url) { '/set_user' }
372+
373+
it { is_expected.to be_ok }
374+
375+
it_behaves_like 'a GET 200 span'
376+
it_behaves_like 'a trace with AppSec tags'
377+
it_behaves_like 'a trace without AppSec events'
378+
379+
context 'with an event-triggering user ID' do
380+
let(:appsec_user_id_denylist) { ['blocked-user-id'] }
381+
382+
it { is_expected.to be_forbidden }
383+
384+
it_behaves_like 'a GET 403 span'
385+
it_behaves_like 'a trace with AppSec tags'
386+
it_behaves_like 'a trace with AppSec events'
387+
end
388+
end
362389
end
363390

364391
describe 'POST request' do

0 commit comments

Comments
 (0)