Class: Rack::LibInjection::Config

Inherits:
Data
  • Object
show all
Defined in:
lib/rack/libinjection.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#detect_maskObject (readonly)

Returns the value of attribute detect_mask

Returns:

  • (Object)

    the current value of detect_mask



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def detect_mask
  @detect_mask
end

#ignore_headersObject (readonly)

Returns the value of attribute ignore_headers

Returns:

  • (Object)

    the current value of ignore_headers



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def ignore_headers
  @ignore_headers
end

#ignore_headers_lookupObject (readonly)

Returns the value of attribute ignore_headers_lookup

Returns:

  • (Object)

    the current value of ignore_headers_lookup



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def ignore_headers_lookup
  @ignore_headers_lookup
end

#ignore_paramsObject (readonly)

Returns the value of attribute ignore_params

Returns:

  • (Object)

    the current value of ignore_params



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def ignore_params
  @ignore_params
end

#ignore_params_lookupObject (readonly)

Returns the value of attribute ignore_params_lookup

Returns:

  • (Object)

    the current value of ignore_params_lookup



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def ignore_params_lookup
  @ignore_params_lookup
end

#max_depthObject (readonly)

Returns the value of attribute max_depth

Returns:

  • (Object)

    the current value of max_depth



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def max_depth
  @max_depth
end

#max_value_bytesObject (readonly)

Returns the value of attribute max_value_bytes

Returns:

  • (Object)

    the current value of max_value_bytes



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def max_value_bytes
  @max_value_bytes
end

#modeObject (readonly)

Returns the value of attribute mode

Returns:

  • (Object)

    the current value of mode



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def mode
  @mode
end

#notifierObject (readonly)

Returns the value of attribute notifier

Returns:

  • (Object)

    the current value of notifier



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def notifier
  @notifier
end

#notifier_errorsObject (readonly)

Returns the value of attribute notifier_errors

Returns:

  • (Object)

    the current value of notifier_errors



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def notifier_errors
  @notifier_errors
end

#notify_skippedObject (readonly)

Returns the value of attribute notify_skipped

Returns:

  • (Object)

    the current value of notify_skipped



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def notify_skipped
  @notify_skipped
end

#parser_errorsObject (readonly)

Returns the value of attribute parser_errors

Returns:

  • (Object)

    the current value of parser_errors



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def parser_errors
  @parser_errors
end

#path_decode_depthObject (readonly)

Returns the value of attribute path_decode_depth

Returns:

  • (Object)

    the current value of path_decode_depth



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def path_decode_depth
  @path_decode_depth
end

#scanObject (readonly)

Returns the value of attribute scan

Returns:

  • (Object)

    the current value of scan



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def scan
  @scan
end

Returns the value of attribute scan_cookie_names

Returns:

  • (Object)

    the current value of scan_cookie_names



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def scan_cookie_names
  @scan_cookie_names
end

#scan_sqliObject (readonly)

Returns the value of attribute scan_sqli

Returns:

  • (Object)

    the current value of scan_sqli



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def scan_sqli
  @scan_sqli
end

#scan_xssObject (readonly)

Returns the value of attribute scan_xss

Returns:

  • (Object)

    the current value of scan_xss



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def scan_xss
  @scan_xss
end

#skipped_inputsObject (readonly)

Returns the value of attribute skipped_inputs

Returns:

  • (Object)

    the current value of skipped_inputs



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def skipped_inputs
  @skipped_inputs
end

#threatsObject (readonly)

Returns the value of attribute threats

Returns:

  • (Object)

    the current value of threats



68
69
70
# File 'lib/rack/libinjection.rb', line 68

def threats
  @threats
end

Class Method Details

.bounded_non_negative_integer!(value, name, max) ⇒ Object

Raises:

  • (ArgumentError)


188
189
190
191
192
193
# File 'lib/rack/libinjection.rb', line 188

def self.bounded_non_negative_integer!(value, name, max)
  integer = non_negative_integer!(value, name)
  return integer if integer <= max

  raise ArgumentError, "#{name} must be <= #{max}"
end

.build(mode: :report, scan: DEFAULT_SCAN, threats: DEFAULT_THREATS, ignore_params: DEFAULT_IGNORE_PARAMS, ignore_headers: DEFAULT_IGNORE_HEADERS, scan_cookie_names: false, max_value_bytes: DEFAULT_MAX_VALUE_BYTES, max_depth: DEFAULT_MAX_DEPTH, path_decode_depth: DEFAULT_PATH_DECODE_DEPTH, parser_errors: :auto, notifier: nil, logger: nil, notifier_errors: :ignore, notify_skipped: true, skipped_inputs: :auto) ⇒ Object

Raises:

  • (ArgumentError)


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
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rack/libinjection.rb', line 89

def self.build(
  mode: :report,
  scan: DEFAULT_SCAN,
  threats: DEFAULT_THREATS,
  ignore_params: DEFAULT_IGNORE_PARAMS,
  ignore_headers: DEFAULT_IGNORE_HEADERS,
  scan_cookie_names: false,
  max_value_bytes: DEFAULT_MAX_VALUE_BYTES,
  max_depth: DEFAULT_MAX_DEPTH,
  path_decode_depth: DEFAULT_PATH_DECODE_DEPTH,
  parser_errors: :auto,
  notifier: nil,
  logger: nil,
  notifier_errors: :ignore,
  notify_skipped: true,
  skipped_inputs: :auto
)
  raise ArgumentError, "pass either notifier: or logger:, not both" if notifier && logger

  mode = validate_mode(mode)
  scan = validate_scan(scan)
  threats = validate_threats(threats)
  ignored = normalize_param_names(ignore_params)
  ignored_headers = normalize_header_names(ignore_headers)

  new(
    mode: mode,
    scan: scan,
    threats: threats,
    scan_sqli: threats.include?(:sqli),
    scan_xss: threats.include?(:xss),
    detect_mask: detect_mask_for(threats),
    ignore_params: ignored,
    ignore_params_lookup: lookup_for(ignored),
    ignore_headers: ignored_headers,
    ignore_headers_lookup: lookup_for(ignored_headers),
    scan_cookie_names: !!scan_cookie_names,
    max_value_bytes: positive_integer!(max_value_bytes, :max_value_bytes),
    path_decode_depth: bounded_non_negative_integer!(path_decode_depth, :path_decode_depth, MAX_PATH_DECODE_DEPTH),
    max_depth: non_negative_integer!(max_depth, :max_depth),
    parser_errors: validate_parser_errors(parser_errors),
    notifier: notifier || build_notifier(logger),
    notifier_errors: validate_notifier_errors(notifier_errors),
    notify_skipped: !!notify_skipped,
    skipped_inputs: validate_skipped_inputs(skipped_inputs)
  )
end

.build_notifier(logger) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/rack/libinjection.rb', line 244

def self.build_notifier(logger)
  if logger
    ->(event, payload) {
      logger.warn(
        "[rack-libinjection] #{event} type=#{payload[:type]} " \
        "path=#{payload[:path]} #{payload[:location]}=#{payload[:key]}"
      )
    }
  elsif defined?(::ActiveSupport::Notifications)
    ->(event, payload) { ::ActiveSupport::Notifications.instrument(event, payload) }
  else
    NOOP_NOTIFIER
  end
end

.detect_mask_for(threats) ⇒ Object



219
220
221
# File 'lib/rack/libinjection.rb', line 219

def self.detect_mask_for(threats)
  (threats.include?(:sqli) ? 1 : 0) | (threats.include?(:xss) ? 2 : 0)
end

.lookup_for(values) ⇒ Object



170
171
172
# File 'lib/rack/libinjection.rb', line 170

def self.lookup_for(values)
  values.each_with_object({}) { |key, index| index[key] = true }.freeze
end

.non_negative_integer!(value, name) ⇒ Object

Raises:

  • (ArgumentError)


181
182
183
184
185
186
# File 'lib/rack/libinjection.rb', line 181

def self.non_negative_integer!(value, name)
  integer = Integer(value)
  return integer if integer >= 0

  raise ArgumentError, "#{name} must be >= 0"
end

.normalize_header_names(values) ⇒ Object



166
167
168
# File 'lib/rack/libinjection.rb', line 166

def self.normalize_header_names(values)
  Array(values).compact.map { |value| value.to_s.downcase }.uniq.freeze
end

.normalize_param_names(values) ⇒ Object



162
163
164
# File 'lib/rack/libinjection.rb', line 162

def self.normalize_param_names(values)
  Array(values).compact.map { |value| value.to_s.downcase }.uniq.freeze
end

.positive_integer!(value, name) ⇒ Object

Raises:

  • (ArgumentError)


174
175
176
177
178
179
# File 'lib/rack/libinjection.rb', line 174

def self.positive_integer!(value, name)
  integer = Integer(value)
  return integer if integer.positive?

  raise ArgumentError, "#{name} must be positive"
end

.validate_mode(value) ⇒ Object

Raises:

  • (ArgumentError)


195
196
197
198
199
200
# File 'lib/rack/libinjection.rb', line 195

def self.validate_mode(value)
  mode = value.to_sym
  return mode if VALID_MODES.include?(mode)

  raise ArgumentError, "mode must be one of: #{VALID_MODES.join(", ")}"
end

.validate_notifier_errors(value) ⇒ Object

Raises:

  • (ArgumentError)


230
231
232
233
234
235
# File 'lib/rack/libinjection.rb', line 230

def self.validate_notifier_errors(value)
  mode = value.to_sym
  return mode if VALID_NOTIFIER_ERRORS.include?(mode)

  raise ArgumentError, "notifier_errors must be one of: #{VALID_NOTIFIER_ERRORS.join(", ")}"
end

.validate_parser_errors(value) ⇒ Object

Raises:

  • (ArgumentError)


223
224
225
226
227
228
# File 'lib/rack/libinjection.rb', line 223

def self.validate_parser_errors(value)
  mode = value.to_sym
  return mode if VALID_PARSER_ERRORS.include?(mode)

  raise ArgumentError, "parser_errors must be one of: #{VALID_PARSER_ERRORS.join(", ")}"
end

.validate_scan(value) ⇒ Object

Raises:

  • (ArgumentError)


202
203
204
205
206
207
208
# File 'lib/rack/libinjection.rb', line 202

def self.validate_scan(value)
  scan = Array(value).map(&:to_sym).uniq.freeze
  unknown = scan - VALID_SCAN
  return scan if unknown.empty?

  raise ArgumentError, "scan contains unknown locations: #{unknown.join(", ")}"
end

.validate_skipped_inputs(value) ⇒ Object

Raises:

  • (ArgumentError)


237
238
239
240
241
242
# File 'lib/rack/libinjection.rb', line 237

def self.validate_skipped_inputs(value)
  mode = value.to_sym
  return mode if VALID_SKIPPED_INPUTS.include?(mode)

  raise ArgumentError, "skipped_inputs must be one of: #{VALID_SKIPPED_INPUTS.join(", ")}"
end

.validate_threats(value) ⇒ Object

Raises:

  • (ArgumentError)


210
211
212
213
214
215
216
217
# File 'lib/rack/libinjection.rb', line 210

def self.validate_threats(value)
  threats = Array(value).map(&:to_sym).uniq.freeze
  unknown = threats - VALID_THREATS
  raise ArgumentError, "threats must include at least one of: #{VALID_THREATS.join(", ")}" if threats.empty?
  return threats if unknown.empty?

  raise ArgumentError, "threats contains unknown types: #{unknown.join(", ")}"
end

Instance Method Details

#env_only_scan?Boolean

Returns:

  • (Boolean)


145
# File 'lib/rack/libinjection.rb', line 145

def env_only_scan? = !scan_params? && !scan_cookies?

#ignore_header?(normalized_key) ⇒ Boolean

Returns:

  • (Boolean)


160
# File 'lib/rack/libinjection.rb', line 160

def ignore_header?(normalized_key) = ignore_headers_lookup.key?(normalized_key)

#ignore_param?(key) ⇒ Boolean

Returns:

  • (Boolean)


159
# File 'lib/rack/libinjection.rb', line 159

def ignore_param?(key) = ignore_params_lookup.key?(key.to_s.downcase)

#notifier_active?Boolean

Returns:

  • (Boolean)


143
# File 'lib/rack/libinjection.rb', line 143

def notifier_active? = !notifier.equal?(NOOP_NOTIFIER)

#parser_error_policyObject



147
148
149
150
151
# File 'lib/rack/libinjection.rb', line 147

def parser_error_policy
  return mode == :block ? :block : :report if parser_errors == :auto

  parser_errors
end

#scan_both_threats?Boolean

Returns:

  • (Boolean)


142
# File 'lib/rack/libinjection.rb', line 142

def scan_both_threats? = scan_sqli && scan_xss

#scan_cookies?Boolean

Returns:

  • (Boolean)


141
# File 'lib/rack/libinjection.rb', line 141

def scan_cookies? = scan.include?(:cookies)

#scan_headers?Boolean

Returns:

  • (Boolean)


140
# File 'lib/rack/libinjection.rb', line 140

def scan_headers? = scan.include?(:headers)

#scan_params?Boolean

Returns:

  • (Boolean)


138
# File 'lib/rack/libinjection.rb', line 138

def scan_params?  = scan.include?(:params)

#scan_path?Boolean

Returns:

  • (Boolean)


139
# File 'lib/rack/libinjection.rb', line 139

def scan_path?    = scan.include?(:path)

#scan_query?Boolean

Returns:

  • (Boolean)


137
# File 'lib/rack/libinjection.rb', line 137

def scan_query?   = scan.include?(:query)

#skipped_input_policyObject



153
154
155
156
157
# File 'lib/rack/libinjection.rb', line 153

def skipped_input_policy
  return mode == :block ? :block : :report if skipped_inputs == :auto

  skipped_inputs
end