Class: Sqreen::Rules::WAFCB

Inherits:
RuleCB show all
Defined in:
lib/sqreen/rules/waf_cb.rb

Constant Summary collapse

MAX_FIXNUM =

2^30 -1 or 2^62 -1

1.size == 4 ? 1_073_741_823 : 4_611_686_018_427_387_903
INFINITE_BUDGET_US =

will be converted to a long, so better not to overflow

MAX_FIXNUM

Constants inherited from RuleCB

RuleCB::DEFAULT_PAYLOAD

Constants included from CallCountable

CallCountable::COUNT_CALLS, CallCountable::FAILING, CallCountable::POST, CallCountable::PRE

Constants inherited from CB

CB::DEFAULT_PRIORITY

Instance Attribute Summary collapse

Attributes inherited from RuleCB

#block, #payload_tpl, #test

Attributes included from CallCountable

#call_count_interval, #call_counts

Attributes inherited from FrameworkCB

#framework

Attributes inherited from CB

#klass, #method, #overtimeable

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from RuleCB

#advise_action, #overtime!, #priority, #record_event, #rule_name, #rulespack_id

Methods included from CallCountable

#count_callback_calls, #failing_with_count, #post_with_count, #pre_with_count

Methods included from Conditionable

#condition_callbacks, #failing_with_conditions, #post_with_conditions, #pre_with_conditions

Methods inherited from FrameworkCB

#record_observation, #whitelisted?

Methods inherited from CB

#failing?, #framework, #overtime!, #post?, #pre?, #priority, #to_s, #whitelisted?

Constructor Details

#initialize(*args) ⇒ WAFCB

Returns a new instance of WAFCB.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/sqreen/rules/waf_cb.rb', line 34

def initialize(*args)
  super(*args)
  @overtimeable = false

  unless WAFCB.libsqreen? && WAFCB.waf?
    Sqreen.log.warn('libsqreen gem with waf not found')
    return
  end

  unless @data['values']
    Sqreen.log.warn('no values in data')
    return
  end

  ::LibSqreen::WAF.logger = Sqreen.log

  name = format("%s_%s", SecureRandom.uuid, rule_name)
  unless @data['values']['waf_rules'] && (::LibSqreen::WAF[name] = @data['values']['waf_rules'])
    Sqreen.log.error("WAF rule #{name} failed to be set, from #<#{self.class.name}:0x#{object_id.to_s(16).rjust(16, '0')}>")
    return
  end
  @waf_rule_name = name
  Sqreen.log.debug("WAF rule #{name} set, from #<#{self.class.name}:0x#{object_id.to_s(16).rjust(16, '0')}>")

  @binding_accessors = @data['values'].fetch('binding_accessors', []).each_with_object({}) do |e, h|
    h[e] = BindingAccessor.new(e)
  end

  # 0 for using defaults (PW_RUN_TIMEOUT)
  @max_run_budget_us = (@data['values'].fetch('max_budget_ms', 0) * 1000).to_i
  @max_run_budget_us = INFINITE_BUDGET_US if @max_run_budget_us >= INFINITE_BUDGET_US

  Sqreen.log.debug { "Max WAF run budget for #{@waf_rule_name} set to #{@max_run_budget_us} us" }

  ObjectSpace.define_finalizer(self, WAFCB.finalizer(@waf_rule_name.dup))
end

Instance Attribute Details

#binding_accessorsObject (readonly)

Returns the value of attribute binding_accessors.



32
33
34
# File 'lib/sqreen/rules/waf_cb.rb', line 32

def binding_accessors
  @binding_accessors
end

#max_run_budget_usObject (readonly)

Returns the value of attribute max_run_budget_us.



32
33
34
# File 'lib/sqreen/rules/waf_cb.rb', line 32

def max_run_budget_us
  @max_run_budget_us
end

#waf_rule_nameObject (readonly)

Returns the value of attribute waf_rule_name.



32
33
34
# File 'lib/sqreen/rules/waf_cb.rb', line 32

def waf_rule_name
  @waf_rule_name
end

Class Method Details

.finalizer(rule_name) ⇒ Object



125
126
127
128
129
130
131
132
# File 'lib/sqreen/rules/waf_cb.rb', line 125

def self.finalizer(rule_name)
  lambda do |object_id|
    return unless WAFCB.libsqreen?

    ::LibSqreen::WAF.delete(rule_name)
    Sqreen.log.debug("WAF rule #{rule_name} deleted, from #<#{name}:0x#{object_id.to_s(16).rjust(16, '0')}>")
  end
end

.libsqreen?Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/sqreen/rules/waf_cb.rb', line 24

def self.libsqreen?
  Sqreen::Dependency::LibSqreen.required?
end

.waf?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/sqreen/rules/waf_cb.rb', line 28

def self.waf?
  Sqreen::Dependency.const_exist?('LibSqreen::WAF')
end

Instance Method Details

#pre(instance, args, budget) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/sqreen/rules/waf_cb.rb', line 71

def pre(instance, args, budget)
  return unless WAFCB.libsqreen? && WAFCB.waf?

  request = framework.request
  return if !waf_rule_name || !request

  env = [binding, framework, instance, args]

  start = Sqreen.time if budget

  capper = Sqreen::Util::Capper.new(string_size_cap: 4096, size_cap: 150, depth_cap: 10)
  waf_args = binding_accessors.each_with_object({}) do |(e, b), h|
    h[e] = capper.call(b.resolve(*env))
  end
  waf_args = Sqreen::Kit::StringSanitizer.sanitize(waf_args)

  if budget
    rem_budget_s = budget - (Sqreen.time - start)
    return advise_action(nil) if rem_budget_s <= 0.0

    waf_gen_budget_us = [(rem_budget_s * 1_000_000).to_i, MAX_FIXNUM].min
  else # no budget
    waf_gen_budget_us = INFINITE_BUDGET_US
  end

  action, data = ::LibSqreen::WAF.run(waf_rule_name, waf_args,
                                      waf_gen_budget_us, @max_run_budget_us)

  case action
  when :monitor
    record_event({ waf_data: data })
    advise_action(nil)
  when :block
    record_event({ waf_data: data })
    advise_action(:raise)
  when :good
    advise_action(nil)
  when :timeout
    Sqreen.log.debug("WAF over time budget: #{action}")
    advise_action(nil)
  when :invalid_call
    Sqreen.log.debug("Error from waf: #{action}")
    advise_action(nil)
    raise Sqreen::WAFError.new(waf_rule_name, action, data, waf_args)
  when :invalid_rule, :invalid_flow, :no_rule
    Sqreen.log.debug("error from waf: #{action}")
    advise_action(nil)
    raise Sqreen::WAFError.new(waf_rule_name, action, data)
  else
    Sqreen.log.warn("unexpected action returned from waf")
    advise_action(nil)
  end
end

#record_exception(exception, infos = {}, at = Time.now.utc) ⇒ Object



134
135
136
137
# File 'lib/sqreen/rules/waf_cb.rb', line 134

def record_exception(exception, infos = {}, at = Time.now.utc)
  infos.merge!(waf_infos(exception)) if exception.is_a?(Sqreen::WAFError)
  super(exception, infos, at)
end