Skip to content

Commit

Permalink
Merge pull request #4352 from DataDog/appsec-56683-update-auto-user-i…
Browse files Browse the repository at this point in the history
…nstrumentation-mode-configuration

[APPSEC-56683] Add `appsec.auto_user_instrumentation.mode` setting
  • Loading branch information
Strech authored Feb 11, 2025
2 parents 5c1cea4 + 18e933f commit 74cd014
Show file tree
Hide file tree
Showing 14 changed files with 906 additions and 467 deletions.
75 changes: 64 additions & 11 deletions lib/datadog/appsec/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,29 @@ module Settings
DEFAULT_OBFUSCATOR_KEY_REGEX = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\.net[_-]sessionid|sid|jwt'
DEFAULT_OBFUSCATOR_VALUE_REGEX = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\.net(?:[_-]|-)sessionid|sid|jwt)(?:\s*=[^;]|"\s*:\s*"[^"]+")|bearer\s+[a-z0-9\._\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\w=-]+\.ey[I-L][\w=-]+(?:\.[\w.+\/=-]+)?|[\-]{5}BEGIN[a-z\s]+PRIVATE\sKEY[\-]{5}[^\-]+[\-]{5}END[a-z\s]+PRIVATE\sKEY|ssh-rsa\s*[a-z0-9\/\.+]{100,}'
# rubocop:enable Layout/LineLength

DISABLED_AUTO_USER_INSTRUMENTATION_MODE = 'disabled'
ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE = 'anonymization'
IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE = 'identification'
AUTO_USER_INSTRUMENTATION_MODES = [
DISABLED_AUTO_USER_INSTRUMENTATION_MODE,
ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE,
IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE
].freeze
AUTO_USER_INSTRUMENTATION_MODES_ALIASES = {
'ident' => IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE,
'anon' => ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE,
}.freeze

# NOTE: These two constants are deprecated
SAFE_TRACK_USER_EVENTS_MODE = 'safe'
EXTENDED_TRACK_USER_EVENTS_MODE = 'extended'
APPSEC_VALID_TRACK_USER_EVENTS_MODE = [
'safe',
'extended'
SAFE_TRACK_USER_EVENTS_MODE, EXTENDED_TRACK_USER_EVENTS_MODE
].freeze
APPSEC_VALID_TRACK_USER_EVENTS_ENABLED_VALUES = [
'1',
'true'
].concat(APPSEC_VALID_TRACK_USER_EVENTS_MODE).freeze
APPSEC_VALID_TRACK_USER_EVENTS_ENABLED_VALUES = ['1', 'true'].concat(
APPSEC_VALID_TRACK_USER_EVENTS_MODE
).freeze

def self.extended(base)
base = base.singleton_class unless base.is_a?(Class)
Expand Down Expand Up @@ -149,6 +164,29 @@ def self.add_settings!(base)
end
end

settings :auto_user_instrumentation do
define_method(:enabled?) { get_option(:mode) != DISABLED_AUTO_USER_INSTRUMENTATION_MODE }

option :mode do |o|
o.type :string
o.env 'DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE'
o.default IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE
o.setter do |value|
mode = AUTO_USER_INSTRUMENTATION_MODES_ALIASES.fetch(value, value)
next mode if AUTO_USER_INSTRUMENTATION_MODES.include?(mode)

Datadog.logger.warn(
'The appsec.auto_user_instrumentation.mode value provided is not supported. ' \
"Supported values are: #{AUTO_USER_INSTRUMENTATION_MODES.join(' | ')}. " \
"Using default value: #{IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE}."
)

IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE
end
end
end

# DEV-3.0: Remove `track_user_events.enabled` and `track_user_events.mode` options
settings :track_user_events do
option :enabled do |o|
o.default true
Expand All @@ -161,24 +199,39 @@ def self.add_settings!(base)
APPSEC_VALID_TRACK_USER_EVENTS_ENABLED_VALUES.include?(env_value.strip.downcase)
end
end
o.after_set do
Core.log_deprecation(key: :appsec_track_user_events_enabled) do
'The appsec.track_user_events.enabled setting has been deprecated for removal. ' \
'Please remove it from your Datadog.configure block and use ' \
'appsec.auto_user_instrumentation.mode instead.'
end
end
end

option :mode do |o|
o.type :string
o.env 'DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING'
o.default 'safe'
o.default SAFE_TRACK_USER_EVENTS_MODE
o.setter do |v|
if APPSEC_VALID_TRACK_USER_EVENTS_MODE.include?(v)
v
elsif v == 'disabled'
'safe'
SAFE_TRACK_USER_EVENTS_MODE
else
Datadog.logger.warn(
'The appsec.track_user_events.mode value provided is not supported.' \
'Supported values are: safe | extended.' \
'Using default value `safe`'
"Supported values are: #{APPSEC_VALID_TRACK_USER_EVENTS_MODE.join(' | ')}." \
"Using default value: #{SAFE_TRACK_USER_EVENTS_MODE}."
)
'safe'

SAFE_TRACK_USER_EVENTS_MODE
end
end
o.after_set do
Core.log_deprecation(key: :appsec_track_user_events_mode) do
'The appsec.track_user_events.mode setting has been deprecated for removal. ' \
'Please remove it from your Datadog.configure block and use ' \
'appsec.auto_user_instrumentation.mode instead.'
end
end
end
Expand Down
33 changes: 33 additions & 0 deletions lib/datadog/appsec/contrib/devise/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module Datadog
module AppSec
module Contrib
module Devise
# A temporary configuration module to accomodate new RFC changes.
# NOTE: DEV-3 Remove module
module Configuration
module_function

# NOTE: DEV-3 Replace method use with `auto_user_instrumentation.enabled?`
def auto_user_instrumentation_enabled?
Datadog.configuration.appsec.auto_user_instrumentation.enabled? &&
Datadog.configuration.appsec.track_user_events.enabled
end

# NOTE: DEV-3 Replace method use with `auto_user_instrumentation.mode`
def auto_user_instrumentation_mode
case Datadog.configuration.appsec.track_user_events.mode
when AppSec::Configuration::Settings::SAFE_TRACK_USER_EVENTS_MODE
AppSec::Configuration::Settings::ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE
when AppSec::Configuration::Settings::EXTENDED_TRACK_USER_EVENTS_MODE
AppSec::Configuration::Settings::IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE
else
Datadog.configuration.appsec.auto_user_instrumentation.mode
end
end
end
end
end
end
end
11 changes: 4 additions & 7 deletions lib/datadog/appsec/contrib/devise/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ module Devise
class Event
UUID_REGEX = /^\h{8}-\h{4}-\h{4}-\h{4}-\h{12}$/.freeze

SAFE_MODE = 'safe'
EXTENDED_MODE = 'extended'

attr_reader :user_id

def initialize(resource, mode)
Expand Down Expand Up @@ -38,15 +35,15 @@ def extract
@user_id = @resource.id

case @mode
when EXTENDED_MODE
when AppSec::Configuration::Settings::IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE
@email = @resource.email
@username = @resource.username
when SAFE_MODE
when AppSec::Configuration::Settings::ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE
@user_id = nil unless @user_id && @user_id.to_s =~ UUID_REGEX
else
Datadog.logger.warn(
"Invalid automated user evenst mode: `#{@mode}`. "\
'Supported modes are: `safe` and `extended`.'
"Invalid auto_user_instrumentation.mode: `#{@mode}`. " \
"Supported modes are: #{AppSec::Configuration::Settings::AUTO_USER_INSTRUMENTATION_MODES.join(' | ')}."
)
end
end
Expand Down
37 changes: 16 additions & 21 deletions lib/datadog/appsec/contrib/devise/patcher/authenticatable_patch.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative '../configuration'
require_relative '../tracking'
require_relative '../resource'
require_relative '../event'
Expand All @@ -14,33 +15,27 @@ module AuthenticatablePatch
# rubocop:disable Metrics/MethodLength
def validate(resource, &block)
result = super
return result unless AppSec.enabled?
return result if @_datadog_skip_track_login_event

track_user_events_configuration = Datadog.configuration.appsec.track_user_events

return result unless track_user_events_configuration.enabled

automated_track_user_events_mode = track_user_events_configuration.mode

appsec_context = Datadog::AppSec.active_context

return result unless appsec_context
return result unless AppSec.enabled?
return result if @_datadog_appsec_skip_track_login_event
return result unless Configuration.auto_user_instrumentation_enabled?
return result unless AppSec.active_context

devise_resource = resource ? Resource.new(resource) : nil

event_information = Event.new(devise_resource, automated_track_user_events_mode)
event_information = Event.new(devise_resource, Configuration.auto_user_instrumentation_mode)

if result
if event_information.user_id
Datadog.logger.debug { 'User Login Event success' }
Datadog.logger.debug { 'AppSec: User successful login event' }
else
Datadog.logger.debug { 'User Login Event success, but can\'t extract user ID. Tracking empty event' }
Datadog.logger.debug do
"AppSec: User successful login event, but can't extract user ID. Tracking empty event"
end
end

Tracking.track_login_success(
appsec_context.trace,
appsec_context.span,
AppSec.active_context.trace,
AppSec.active_context.span,
user_id: event_information.user_id,
**event_information.to_h
)
Expand All @@ -52,15 +47,15 @@ def validate(resource, &block)

if resource
user_exists = true
Datadog.logger.debug { 'User Login Event failure users exists' }
Datadog.logger.debug { 'AppSec: User failed login event, but user exists' }
else
user_exists = false
Datadog.logger.debug { 'User Login Event failure user do not exists' }
Datadog.logger.debug { 'AppSec: User failed login event and user does not exist' }
end

Tracking.track_login_failure(
appsec_context.trace,
appsec_context.span,
AppSec.active_context.trace,
AppSec.active_context.span,
user_id: event_information.user_id,
user_exists: user_exists,
**event_information.to_h
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative '../configuration'
require_relative '../tracking'
require_relative '../resource'
require_relative '../event'
Expand All @@ -13,31 +14,23 @@ module Patcher
module RegistrationControllerPatch
def create
return super unless AppSec.enabled?

track_user_events_configuration = Datadog.configuration.appsec.track_user_events

return super unless track_user_events_configuration.enabled

automated_track_user_events_mode = track_user_events_configuration.mode

appsec_context = Datadog::AppSec.active_context
return super unless appsec_context
return super unless Configuration.auto_user_instrumentation_enabled?
return super unless AppSec.active_context

super do |resource|
if resource.persisted?
devise_resource = Resource.new(resource)

event_information = Event.new(devise_resource, automated_track_user_events_mode)
event_information = Event.new(devise_resource, Configuration.auto_user_instrumentation_mode)

if event_information.user_id
Datadog.logger.debug { 'User Signup Event' }
Datadog.logger.debug { 'AppSec: User signup event' }
else
Datadog.logger.warn { 'User Signup Event, but can\'t extract user ID. Tracking empty event' }
Datadog.logger.warn { "AppSec: User signup event, but can't extract user ID. Tracking empty event" }
end

Tracking.track_signup(
appsec_context.trace,
appsec_context.span,
AppSec.active_context.trace,
AppSec.active_context.span,
user_id: event_information.user_id,
**event_information.to_h
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Patcher
# Rememberable strategy as Login Success events.
module RememberablePatch
def validate(*args)
@_datadog_skip_track_login_event = true
@_datadog_appsec_skip_track_login_event = true

super
end
Expand Down
22 changes: 19 additions & 3 deletions sig/datadog/appsec/configuration/settings.rbs
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
module Datadog
module AppSec
module Configuration
# Settings
module Settings
extend Datadog::Core::Configuration::Base::ClassMethods
include Datadog::Core::Configuration::Base::InstanceMethods
extend Datadog::Core::Configuration::Options::ClassMethods
include Datadog::Core::Configuration::Options::InstanceMethods

DEFAULT_OBFUSCATOR_KEY_REGEX: ::String

DEFAULT_OBFUSCATOR_VALUE_REGEX: ::String
APPSEC_VALID_TRACK_USER_EVENTS_MODE: ::Array[String]
APPSEC_VALID_TRACK_USER_EVENTS_ENABLED_VALUES: ::Array[String]

DISABLED_AUTO_USER_INSTRUMENTATION_MODE: ::String

ANONYMIZATION_AUTO_USER_INSTRUMENTATION_MODE: ::String

IDENTIFICATION_AUTO_USER_INSTRUMENTATION_MODE: ::String

AUTO_USER_INSTRUMENTATION_MODES: ::Array[::String]

AUTO_USER_INSTRUMENTATION_MODES_ALIASES: ::Hash[::String, ::String]

SAFE_TRACK_USER_EVENTS_MODE: ::String

EXTENDED_TRACK_USER_EVENTS_MODE: ::String

APPSEC_VALID_TRACK_USER_EVENTS_MODE: ::Array[::String]

APPSEC_VALID_TRACK_USER_EVENTS_ENABLED_VALUES: ::Array[::String]

def self.extended: (untyped base) -> untyped

Expand Down
13 changes: 13 additions & 0 deletions sig/datadog/appsec/contrib/devise/configuration.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Datadog
module AppSec
module Contrib
module Devise
module Configuration
def self?.auto_user_instrumentation_enabled?: () -> bool

def self?.auto_user_instrumentation_mode: () -> ::String
end
end
end
end
end
Loading

0 comments on commit 74cd014

Please sign in to comment.