Class: Datadog::AppSec::SecurityEngine::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/datadog/appsec/security_engine/engine.rb

Overview

SecurityEngine::Engine creates WAF builder and manages its configuration. It also rebuilds WAF handle from the WAF builder when configuration changes.

Constant Summary collapse

DEFAULT_RULES_CONFIG_PATH =
'ASM_DD/default'
TELEMETRY_ACTIONS =
%w[init update].freeze
DIAGNOSTICS_CONFIG_KEYS =
%w[
  rules
  custom_rules
  exclusions
  actions
  processors
  scanners
  rules_override
  rules_data
  exclusion_data
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(appsec_settings:, telemetry:) ⇒ Engine

Returns a new instance of Engine.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/datadog/appsec/security_engine/engine.rb', line 23

def initialize(appsec_settings:, telemetry:)
  @default_ruleset = appsec_settings.ruleset

  # NOTE: replace appsec_settings argument with default_ruleset when removing these deprecated settings
  @default_ip_denylist = appsec_settings.ip_denylist
  @default_user_id_denylist = appsec_settings.user_id_denylist
  @default_ip_passlist = appsec_settings.ip_passlist

  @waf_builder = WAF::HandleBuilder.new(
    obfuscator: {
      key_regex: appsec_settings.obfuscator_key_regex,
      value_regex: appsec_settings.obfuscator_value_regex
    }
  )

  diagnostics = load_default_config(telemetry: telemetry)
  report_configuration_diagnostics(diagnostics, action: 'init', telemetry: telemetry)
  @ruleset_version = diagnostics['ruleset_version']

  @handle_ref = ThreadSafeRef.new(@waf_builder.build_handle)
rescue WAF::Error => e
  error_message = "AppSec security engine failed to initialize"

  Datadog.logger.error("#{error_message}, error #{e.inspect}")
  telemetry.report(e, description: error_message)

  raise e
end

Instance Method Details

#add_or_update_config(config, path:) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/datadog/appsec/security_engine/engine.rb', line 56

def add_or_update_config(config, path:)
  @is_ruleset_update = path.include?('ASM_DD')

  # default config has to be removed when adding an ASM_DD config
  remove_config_at_path(DEFAULT_RULES_CONFIG_PATH) if @is_ruleset_update

  diagnostics = @waf_builder.add_or_update_config(config, path: path)
  @reconfigured_ruleset_version = diagnostics['ruleset_version'] if diagnostics.key?('ruleset_version')
  report_configuration_diagnostics(diagnostics, action: 'update', telemetry: AppSec.telemetry)

  # we need to load default config if diagnostics contains top-level error for rules or processors
  if @is_ruleset_update &&
      (diagnostics.key?('error') ||
      diagnostics.dig('rules', 'error') ||
      diagnostics.dig('processors', 'errors'))
    diagnostics = load_default_config(telemetry: AppSec.telemetry)
    @reconfigured_ruleset_version = diagnostics['ruleset_version']
    report_configuration_diagnostics(diagnostics, action: 'update', telemetry: AppSec.telemetry)
  end

  diagnostics
rescue WAF::Error => e
  error_message = "libddwaf builder failed to add or update config at path: #{path}"

  Datadog.logger.debug("#{error_message}, error: #{e.inspect}")
  AppSec.telemetry.report(e, description: error_message)
end

#new_runnerObject



52
53
54
# File 'lib/datadog/appsec/security_engine/engine.rb', line 52

def new_runner
  SecurityEngine::Runner.new(@handle_ref, ruleset_version: @ruleset_version)
end

#reconfigure!Object



101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/datadog/appsec/security_engine/engine.rb', line 101

def reconfigure!
  new_waf_handle = @waf_builder.build_handle
  @ruleset_version = @reconfigured_ruleset_version

  @handle_ref.current = new_waf_handle
rescue WAF::Error => e
  # WAF::Error can only be raised during new WAF handle creation or when reading known addresses.
  # This means that the current WAF handle was not yet substituted.
  error_message = "AppSec security engine failed to reconfigure, reverting to the previous configuration"

  Datadog.logger.error("#{error_message}, error #{e.inspect}")
  AppSec.telemetry.report(e, description: error_message)
end

#remove_config_at_path(path) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/datadog/appsec/security_engine/engine.rb', line 84

def remove_config_at_path(path)
  result = @waf_builder.remove_config_at_path(path)

  if result && path != DEFAULT_RULES_CONFIG_PATH && path.include?('ASM_DD')
    diagnostics = load_default_config(telemetry: AppSec.telemetry)
    @reconfigured_ruleset_version = diagnostics['ruleset_version']
    report_configuration_diagnostics(diagnostics, action: 'update', telemetry: AppSec.telemetry)
  end

  result
rescue WAF::Error => e
  error_message = "libddwaf handle builder failed to remove config at path: #{path}"

  Datadog.logger.error("#{error_message}, error: #{e.inspect}")
  AppSec.telemetry.report(e, description: error_message)
end