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 Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(appsec_settings:, telemetry:) ⇒ Engine

Returns a new instance of Engine.



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
51
52
# File 'lib/datadog/appsec/security_engine/engine.rb', line 25

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)

  @waf_handle = @waf_builder.build_handle
  @waf_addresses = @waf_handle.known_addresses
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 Attribute Details

#ruleset_versionObject (readonly)

Returns the value of attribute ruleset_version.



23
24
25
# File 'lib/datadog/appsec/security_engine/engine.rb', line 23

def ruleset_version
  @ruleset_version
end

#waf_addressesObject (readonly)

Returns the value of attribute waf_addresses.



23
24
25
# File 'lib/datadog/appsec/security_engine/engine.rb', line 23

def waf_addresses
  @waf_addresses
end

Instance Method Details

#add_or_update_config(config, path:) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/datadog/appsec/security_engine/engine.rb', line 66

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)
  @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)
    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

#finalize!Object



54
55
56
57
58
59
60
# File 'lib/datadog/appsec/security_engine/engine.rb', line 54

def finalize!
  @waf_handle&.finalize!
  @waf_builder&.finalize!

  @waf_addresses = []
  @ruleset_version = nil
end

#new_runnerObject



62
63
64
# File 'lib/datadog/appsec/security_engine/engine.rb', line 62

def new_runner
  SecurityEngine::Runner.new(@waf_handle.build_context)
end

#reconfigure!Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/datadog/appsec/security_engine/engine.rb', line 109

def reconfigure!
  old_waf_handle = @waf_handle

  @waf_handle = @waf_builder.build_handle
  @waf_addresses = @waf_handle.known_addresses

  old_waf_handle&.finalize!
rescue WAF::Error => e
  error_message = "AppSec security engine failed to reconfigure"

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

  if old_waf_handle
    Datadog.logger.warn("Reverting to the previous configuration")

    @waf_handle = old_waf_handle
    @waf_addresses = old_waf_handle.known_addresses
  end
end

#remove_config_at_path(path) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/datadog/appsec/security_engine/engine.rb', line 93

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)
    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