Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/system-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ permissions: {}
env:
REGISTRY: ghcr.io
REPO: ghcr.io/datadog/dd-trace-rb
SYSTEM_TESTS_REF: main # This must always be set to `main` on dd-trace-rb's master branch
SYSTEM_TESTS_REF: appsec-57504-enable-session-tracking-and-fingerprinting # This must always be set to `main` on dd-trace-rb's master branch

jobs:
changes:
Expand Down
4 changes: 2 additions & 2 deletions lib/datadog/appsec/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ def create_processor(settings, telemetry)

# NOTE: This is a temporary solution before the RuleMerger refactoring
# with new RemoteConfig setup
processors = rules.delete('processors')
scanners = rules.delete('scanners')
processors = rules['processors']
scanners = rules['scanners']

ruleset = AppSec::Processor::RuleMerger.merge(
rules: [rules],
Expand Down
1 change: 1 addition & 0 deletions lib/datadog/appsec/contrib/devise/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module Ext
TAG_DD_LOGIN_FAILURE_MODE = '_dd.appsec.events.users.login.failure.auto.mode'

TAG_USR_ID = 'usr.id'
TAG_SESSION_ID = 'usr.session_id'
TAG_SIGNUP_TRACK = 'appsec.events.users.signup.track'
TAG_SIGNUP_USR_ID = 'appsec.events.users.signup.usr.id'
TAG_SIGNUP_USR_LOGIN = 'appsec.events.users.signup.usr.login'
Expand Down
21 changes: 17 additions & 4 deletions lib/datadog/appsec/contrib/devise/tracking_middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Devise
# A Rack middleware capable of tracking currently signed user
class TrackingMiddleware
WARDEN_KEY = 'warden'
SESSION_ID_KEY = 'session_id'

def initialize(app)
@app = app
Expand All @@ -32,16 +33,28 @@ def call(env)
return @app.call(env)
end

# NOTE: Rails session id will be set for unauthenticated users as well,
# so we need to make sure we are tracking only authenticated users.
id = transform(extract_id(env[WARDEN_KEY]))
session_id = env[WARDEN_KEY].raw_session[SESSION_ID_KEY] if id

if id
unless context.span.has_tag?(Ext::TAG_USR_ID)
context.span[Ext::TAG_USR_ID] = id
# NOTE: There is no option to set session id without setting user id via SDK.
unless context.span.has_tag?(Ext::TAG_USR_ID) && context.span.has_tag?(Ext::TAG_SESSION_ID)
user_id = context.span[Ext::TAG_USR_ID] || id
user_session_id = context.span[Ext::TAG_SESSION_ID] || session_id

# FIXME: The current implementation of event arguments is forsing us
# to bloat User class, and pass nil-value instead of skip
# passing them at first place.
# This is a temporary situation until we refactor events model.
AppSec::Instrumentation.gateway.push(
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(id, nil)
'identity.set_user', AppSec::Instrumentation::Gateway::User.new(user_id, nil, user_session_id)
)
end

context.span[Ext::TAG_DD_USR_ID] = id.to_s
context.span[Ext::TAG_USR_ID] ||= id
context.span[Ext::TAG_DD_USR_ID] = id
context.span[Ext::TAG_DD_COLLECTION_MODE] ||= Configuration.auto_user_instrumentation_mode
end

Expand Down
7 changes: 5 additions & 2 deletions lib/datadog/appsec/instrumentation/gateway/argument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ class Gateway
class Argument; end # rubocop:disable Lint/EmptyClass

# Gateway User argument
# NOTE: This class is a subject of elimination and will be removed when
# the event system is refactored.
class User < Argument
attr_reader :id, :login
attr_reader :id, :login, :session_id

def initialize(id, login)
def initialize(id, login = nil, session_id = nil)
super()

@id = id
@login = login
@session_id = session_id
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/datadog/appsec/monitor/gateway/watcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,26 @@ def watch_user_id(gateway = Instrumentation.gateway)
gateway.watch('identity.set_user', :appsec) do |stack, user|
context = AppSec.active_context

if user.id.nil? && user.login.nil?
if user.id.nil? && user.login.nil? && user.session_id.nil?
Datadog.logger.debug { 'AppSec: skipping WAF check because no user information was provided' }
next stack.call(user)
end

persistent_data = {}
persistent_data['usr.id'] = user.id if user.id
persistent_data['usr.login'] = user.login if user.login
persistent_data['usr.session_id'] = user.session_id if user.session_id

result = context.run_waf(persistent_data, {}, Datadog.configuration.appsec.waf_timeout)

if result.match?
AppSec::Event.tag_and_keep!(context, result)

if result.match? || !result.derivatives.empty?
context.events.push(
AppSec::SecurityEvent.new(result, trace: context.trace, span: context.span)
)
end

if result.match?
AppSec::Event.tag_and_keep!(context, result)
AppSec::ActionsHandler.handle(result.actions)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/datadog/kit/identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def set_user(
if Datadog::AppSec.active_context
active_span.set_tag('_dd.appsec.user.collection_mode', 'sdk')

user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id, others[:login])
user = ::Datadog::AppSec::Instrumentation::Gateway::User.new(id, others[:login], session_id)
::Datadog::AppSec::Instrumentation.gateway.push('identity.set_user', user)
end
end
Expand Down
2 changes: 2 additions & 0 deletions sig/datadog/appsec/contrib/devise/ext.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Datadog

TAG_USR_ID: ::String

TAG_SESSION_ID: ::String

TAG_LOGIN_SUCCESS_TRACK: ::String

TAG_LOGIN_SUCCESS_USR_LOGIN: ::String
Expand Down
2 changes: 2 additions & 0 deletions sig/datadog/appsec/contrib/devise/tracking_middleware.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Datadog
class TrackingMiddleware
WARDEN_KEY: ::String

SESSION_ID_KEY: ::String

@app: untyped

def initialize: (untyped app) -> void
Expand Down
6 changes: 5 additions & 1 deletion sig/datadog/appsec/instrumentation/gateway/argument.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ module Datadog

@login: String?

@session_id: String?

attr_reader id: String

attr_reader login: String?

def initialize: (String id, String? login) -> void
attr_reader session_id: String?

def initialize: (String id, String? login, String? session_id) -> void
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
require 'sqlite3'
require 'devise'

RSpec.describe 'Devise auto login and signup events tracking' do
RSpec.describe 'Devise auto authenticated multi-user tracking' do
include Rack::Test::Methods
include Warden::Test::Helpers

Expand Down
Loading
Loading