Skip to content

[APPSEC-56683] Add appsec.auto_user_instrumentation.mode setting #4352

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
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
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
Loading