sqreen 1.11.0-java → 1.11.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sqreen.rb +0 -1
  3. data/lib/sqreen/callback_tree.rb +38 -20
  4. data/lib/sqreen/callbacks.rb +6 -4
  5. data/lib/sqreen/conditionable.rb +3 -3
  6. data/lib/sqreen/frameworks/generic.rb +38 -18
  7. data/lib/sqreen/frameworks/request_recorder.rb +0 -2
  8. data/lib/sqreen/instrumentation.rb +217 -160
  9. data/lib/sqreen/performance_notifications.rb +10 -36
  10. data/lib/sqreen/performance_notifications/log.rb +1 -2
  11. data/lib/sqreen/performance_notifications/log_performance.rb +1 -2
  12. data/lib/sqreen/performance_notifications/metrics.rb +1 -2
  13. data/lib/sqreen/performance_notifications/newrelic.rb +1 -2
  14. data/lib/sqreen/remote_command.rb +7 -7
  15. data/lib/sqreen/rule_callback.rb +4 -0
  16. data/lib/sqreen/rules.rb +2 -2
  17. data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +7 -1
  18. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +3 -3
  19. data/lib/sqreen/rules_callbacks/blacklist_ips.rb +1 -1
  20. data/lib/sqreen/rules_callbacks/count_http_codes.rb +7 -6
  21. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +1 -1
  22. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +1 -1
  23. data/lib/sqreen/rules_callbacks/custom_error.rb +2 -5
  24. data/lib/sqreen/rules_callbacks/execjs.rb +76 -37
  25. data/lib/sqreen/rules_callbacks/headers_insert.rb +1 -1
  26. data/lib/sqreen/rules_callbacks/inspect_rule.rb +3 -3
  27. data/lib/sqreen/rules_callbacks/matcher_rule.rb +18 -17
  28. data/lib/sqreen/rules_callbacks/rails_parameters.rb +1 -1
  29. data/lib/sqreen/rules_callbacks/record_request_context.rb +9 -8
  30. data/lib/sqreen/rules_callbacks/reflected_xss.rb +40 -34
  31. data/lib/sqreen/rules_callbacks/regexp_rule.rb +13 -4
  32. data/lib/sqreen/rules_callbacks/shell_env.rb +2 -2
  33. data/lib/sqreen/rules_callbacks/url_matches.rb +1 -1
  34. data/lib/sqreen/rules_callbacks/user_agent_matches.rb +1 -1
  35. data/lib/sqreen/session.rb +1 -1
  36. data/lib/sqreen/shared_storage.rb +16 -12
  37. data/lib/sqreen/{stats.rb → shared_storage23.rb} +3 -11
  38. data/lib/sqreen/version.rb +1 -1
  39. metadata +4 -4
@@ -7,7 +7,7 @@ module Sqreen
7
7
  module Rules
8
8
  # Display sqreen presence
9
9
  class HeadersInsertCB < RuleCB
10
- def post(rv, _inst, *_args, &_block)
10
+ def post(rv, _inst, _args, _budget = nil, &_block)
11
11
  return unless rv && rv.respond_to?(:[]) && rv[1].is_a?(Hash)
12
12
  return nil unless @data
13
13
  headers = @data['values'] || []
@@ -6,17 +6,17 @@ require 'sqreen/rule_callback'
6
6
  module Sqreen
7
7
  module Rules
8
8
  class InspectRuleCB < RuleCB
9
- def pre(_inst, *args, &_block)
9
+ def pre(_inst, args, _budget = nil, &_block)
10
10
  Sqreen.log.debug { "<< #{@klass} #{@method} #{Thread.current}" }
11
11
  Sqreen.log.debug { args.map(&:inspect).join(' ') }
12
12
  end
13
13
 
14
- def post(rv, _inst, *_args, &_block)
14
+ def post(rv, _inst, _args, _budget = nil, &_block)
15
15
  Sqreen.log.debug { ">> #{rv.inspect} #{@klass} #{@method} #{Thread.current}" }
16
16
  byebug if defined? byebug && @data.is_a?(Hash) && @data[:break] == 1
17
17
  end
18
18
 
19
- def failing(rv, _inst, *_args, &_block)
19
+ def failing(rv, _inst, _args, _budget = nil, &_block)
20
20
  Sqreen.log.debug { "># #{rv.inspect} #{@klass} #{@method} #{Thread.current}" }
21
21
  byebug if defined? byebug && @data.is_a?(Hash) && @data[:break] == 1
22
22
  end
@@ -13,11 +13,12 @@ module Sqreen
13
13
  res |= Regexp::MULTILINE if options.include?('multiline')
14
14
  res |= Regexp::IGNORECASE unless case_sensitive
15
15
  r = Regexp.compile(value, res)
16
- r.match("")
16
+ r.match('')
17
17
  r
18
18
  end
19
19
 
20
20
  ANYWHERE_OPT = 'anywhere'.freeze
21
+ MATCH_PREDICATE = Regexp.new('').respond_to?(:match?)
21
22
  def prepare(patterns)
22
23
  @string = {}
23
24
  @regexp_patterns = []
@@ -52,12 +53,13 @@ module Sqreen
52
53
  val.downcase!
53
54
  end
54
55
 
55
- unless @funs.keys.include?(opt)
56
+ way = @funs[opt]
57
+ unless way
56
58
  Sqreen.log.debug { "Error: unknown string option '#{opt}' " }
57
59
  next
58
60
  end
59
- @string[opt] = { :ci => [], :cs => [] } unless @string.key?(opt)
60
- @string[opt][case_type] << val
61
+ @string[way] = { :ci => [], :cs => [] } unless @string.key?(way)
62
+ @string[way][case_type] << val
61
63
  sizes << entry.fetch('min_length') { val.size }
62
64
  when 'regexp'
63
65
  pattern = Matcher.prepare_re_pattern(val, opt, case_sensitive)
@@ -81,17 +83,9 @@ module Sqreen
81
83
  str = enforce_encoding(str) unless str.ascii_only?
82
84
  istr = str.downcase unless @string.empty?
83
85
 
84
- @string.each do |type, cases|
85
- fun = @funs[type]
86
- if fun.nil?
87
- Sqreen.log.debug { "no matching function found for type #{type}" }
88
- end
86
+ @string.each do |fun, cases|
89
87
  cases.each do |case_type, patterns|
90
- input_str = if case_type == :ci
91
- istr
92
- else
93
- str
94
- end
88
+ input_str = case_type == :ci ? istr : str
95
89
  patterns.each do |pat|
96
90
  return pat if fun.call(pat, input_str)
97
91
  end
@@ -99,9 +93,16 @@ module Sqreen
99
93
  end
100
94
 
101
95
  if defined?(Encoding)
102
- @regexp_patterns.each do |p|
103
- next unless Encoding.compatible?(p, str)
104
- return p if p.match(str)
96
+ if MATCH_PREDICATE
97
+ @regexp_patterns.each do |p|
98
+ next unless Encoding.compatible?(p, str)
99
+ return p if p.match?(str)
100
+ end
101
+ else
102
+ @regexp_patterns.each do |p|
103
+ next unless Encoding.compatible?(p, str)
104
+ return p if p.match(str)
105
+ end
105
106
  end
106
107
  else
107
108
  @regexp_patterns.each do |p|
@@ -6,7 +6,7 @@ require 'sqreen/rule_callback'
6
6
  module Sqreen
7
7
  module Rules
8
8
  class RailsParametersCB < RuleCB
9
- def pre(_inst, *_args, &_block)
9
+ def pre(_inst, _args, _budget = nil, &_block)
10
10
  advise_action(nil)
11
11
  end
12
12
  end
@@ -2,39 +2,40 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  require 'sqreen/rule_callback'
5
- require 'sqreen/instrumentation'
6
5
 
7
6
  module Sqreen
8
7
  module Rules
9
8
  # Save request context for handling further down
10
9
  class RecordRequestContext < RuleCB
11
- def whitelisted?
12
- false
10
+ WHITELISTED_METRIC = 'whitelisted'.freeze
11
+ def initialize(*args)
12
+ super(*args)
13
+ @overtimeable = false
13
14
  end
14
15
 
15
- def overtime!
16
+ def whitelisted?
16
17
  false
17
18
  end
18
19
 
19
- def pre(_inst, *args, &_block)
20
+ def pre(_inst, args, _budget = nil, &_block)
20
21
  framework.store_request(args[0])
21
22
  wh = framework.whitelisted_match
22
23
  if wh
23
24
  unless Sqreen.features.key?('whitelisted_metric') &&
24
25
  !Sqreen.features['whitelisted_metric']
25
- record_observation(Instrumentation::WHITELISTED_METRIC, wh, 1)
26
+ record_observation(WHITELISTED_METRIC, wh, 1)
26
27
  end
27
28
  Sqreen.log.debug { "Request was whitelisted because of #{wh}" }
28
29
  end
29
30
  advise_action(nil)
30
31
  end
31
32
 
32
- def post(_rv, _inst, *_args, &_block)
33
+ def post(_rv, _inst, _args, _budget = nil, &_block)
33
34
  framework.clean_request
34
35
  advise_action(nil)
35
36
  end
36
37
 
37
- def failing(_exception, _inst, *_args, &_block)
38
+ def failing(_exception, _inst, _args, _budget = nil, &_block)
38
39
  framework.clean_request
39
40
  advise_action(nil)
40
41
  end
@@ -37,7 +37,7 @@ module Sqreen
37
37
  end
38
38
  end
39
39
  class ReflectedUnsafeXSSCB < XSSCB
40
- def pre(_inst, *args, &_block)
40
+ def pre(_inst, args, _budget = nil, &_block)
41
41
  value = args[0]
42
42
 
43
43
  return unless value.is_a?(String)
@@ -61,7 +61,7 @@ module Sqreen
61
61
  end
62
62
  # look for reflected XSS with erb template engine
63
63
  class ReflectedXSSCB < XSSCB
64
- def pre(_inst, *args, &_block)
64
+ def pre(_inst, args, _budget = nil, &_block)
65
65
  value = args[0]
66
66
 
67
67
  return unless value.is_a?(String)
@@ -90,7 +90,7 @@ module Sqreen
90
90
  # escape_html, nuke_inner_whitespace,
91
91
  # interpolated, ugly)
92
92
  class ReflectedXSSHamlCB < XSSCB
93
- def post(ret, _inst, *_args, &_block)
93
+ def post(ret, _inst, _args, _budget = nil, &_block)
94
94
  value = ret
95
95
  return unless value.is_a?(String)
96
96
 
@@ -109,7 +109,12 @@ module Sqreen
109
109
 
110
110
  # Hook into haml4 script parser
111
111
  class Haml4ParserScriptHookCB < RuleCB
112
- def pre(_inst, *args, &_block)
112
+ def initialize(*args)
113
+ super(*args)
114
+ @overtimeable = false
115
+ end
116
+
117
+ def pre(_inst, args, _budget = nil, &_block)
113
118
  return unless args.size > 1
114
119
  return unless Haml::VERSION < '5'
115
120
  text = args[0]
@@ -121,15 +126,16 @@ module Sqreen
121
126
  end
122
127
  nil
123
128
  end
124
-
125
- def overtime!
126
- false
127
- end
128
129
  end
129
130
 
130
131
  # Hook into haml4 tag parser
131
132
  class Haml4ParserTagHookCB < RuleCB
132
- def post(ret, _inst, *_args, &_block)
133
+ def initialize(*args)
134
+ super(*args)
135
+ @overtimeable = false
136
+ end
137
+
138
+ def post(ret, _inst, _args, _budget = nil, &_block)
133
139
  return unless Haml::VERSION < '5'
134
140
  tag = ret
135
141
  if tag.value[:escape_html] == false &&
@@ -140,14 +146,15 @@ module Sqreen
140
146
  end
141
147
  nil
142
148
  end
143
-
144
- def overtime!
145
- false
146
- end
147
149
  end
148
150
 
149
151
  class Haml4UtilInterpolationHookCB < RuleCB
150
- def pre(_inst, *args, &_block)
152
+ def initialize(*args)
153
+ super(*args)
154
+ @overtimeable = false
155
+ end
156
+
157
+ def pre(_inst, args, _budget = nil, &_block)
151
158
  # Also work in haml5
152
159
  str = args[0]
153
160
  escape_html = args[1]
@@ -167,15 +174,16 @@ module Sqreen
167
174
  end
168
175
  { :status => :skip, :new_return_value => res + rest }
169
176
  end
170
-
171
- def overtime!
172
- false
173
- end
174
177
  end
175
178
 
176
179
  # Hook build attributes
177
180
  class Haml4CompilerBuildAttributeCB < XSSCB
178
- def pre(inst, *args, &_block)
181
+ def initialize(*args)
182
+ super(*args)
183
+ @overtimeable = false
184
+ end
185
+
186
+ def pre(inst, args, _budget = nil, &_block)
179
187
  return unless Haml::VERSION < '5'
180
188
  attrs = args[-1]
181
189
  params = xss_params
@@ -217,38 +225,36 @@ module Sqreen
217
225
  end
218
226
  [new_h, has_xss]
219
227
  end
220
-
221
- def overtime!
222
- false
223
- end
224
228
  end
225
229
 
226
230
  class Haml5EscapableHookCB < RuleCB
227
- def pre(_inst, *args, &_block)
228
- args[0] = "Sqreen.escape_haml((#{args[0]}))"
229
- { :status => :modify_args, :args => args }
231
+ def initialize(*args)
232
+ super(*args)
233
+ @overtimeable = false
230
234
  end
231
235
 
232
- def overtime!
233
- false
236
+ def pre(_inst, args, _budget = nil, &_block)
237
+ args[0] = "Sqreen.escape_haml((#{args[0]}))"
238
+ { :status => :modify_args, :args => args }
234
239
  end
235
240
  end
236
241
 
237
242
  # Hook into temple template rendering
238
243
  class TempleEscapableHookCB < RuleCB
239
- def post(ret, _inst, *_args, &_block)
240
- ret[1] = "Sqreen.escape_temple((#{ret[1]}))"
241
- { :status => :override, :new_return_value => ret }
244
+ def initialize(*args)
245
+ super(*args)
246
+ @overtimeable = false
242
247
  end
243
248
 
244
- def overtime!
245
- false
249
+ def post(ret, _inst, _args, _budget = nil, &_block)
250
+ ret[1] = "Sqreen.escape_temple((#{ret[1]}))"
251
+ { :status => :override, :new_return_value => ret }
246
252
  end
247
253
  end
248
254
 
249
255
  # Hook into temple template rendering
250
256
  class SlimSplatBuilderCB < XSSCB
251
- def pre(inst, *args, &_block)
257
+ def pre(inst, args, _budget = nil, &_block)
252
258
  value = args[0]
253
259
  return if value.nil?
254
260
 
@@ -25,11 +25,20 @@ module Sqreen
25
25
  end
26
26
  end
27
27
 
28
- def match_regexp(str)
29
- @patterns.each do |pattern|
30
- return pattern if pattern.match(str)
28
+ if Regexp.new('').respond_to?(:match?)
29
+ def match_regexp(str)
30
+ @patterns.each do |pattern|
31
+ return pattern if pattern.match?(str)
32
+ end
33
+ nil
34
+ end
35
+ else
36
+ def match_regexp(str)
37
+ @patterns.each do |pattern|
38
+ return pattern if pattern.match(str)
39
+ end
40
+ nil
31
41
  end
32
- nil
33
42
  end
34
43
  end
35
44
  end
@@ -7,7 +7,7 @@ module Sqreen
7
7
  module Rules
8
8
  # Callback that detect nifty env in system calls
9
9
  class ShellEnvCB < RegexpRuleCB
10
- def pre(_inst, *args, &_block)
10
+ def pre(_inst, args, _budget = nil, &_block)
11
11
  return if args.size == 0
12
12
  env = args.first
13
13
  return unless env.is_a?(Hash)
@@ -23,7 +23,7 @@ module Sqreen
23
23
  :variable_value => value,
24
24
  :found => found,
25
25
  }
26
- Sqreen.log.warn "presence of a shell env tampering: #{infos.inspect}"
26
+ Sqreen.log.warn { "presence of a shell env tampering: #{infos.inspect}" }
27
27
  record_event(infos)
28
28
  advise_action(:raise)
29
29
  end
@@ -11,7 +11,7 @@ module Sqreen
11
11
  # - the path is a typical bot scanning request
12
12
  # Then we deny the ressource and record the attack.
13
13
  class URLMatchesCB < RegexpRuleCB
14
- def post(rv, _inst, *args, &_block)
14
+ def post(rv, _inst, args, _budget = nil, &_block)
15
15
  return unless rv.is_a?(Array) && rv.size > 0 && rv[0] == 404
16
16
  env = args[0]
17
17
  path = env['SCRIPT_NAME'].to_s + env['PATH_INFO'].to_s
@@ -7,7 +7,7 @@ module Sqreen
7
7
  module Rules
8
8
  # Look for badly behaved clients
9
9
  class UserAgentMatchesCB < RegexpRuleCB
10
- def pre(_inst, *_args, &_block)
10
+ def pre(_inst, _args, _budget = nil, &_block)
11
11
  ua = framework.client_user_agent
12
12
  return unless ua
13
13
  found = match_regexp(ua)
@@ -134,7 +134,7 @@ module Sqreen
134
134
  def resiliently(retry_request_seconds, max_retry, current_retry = 0)
135
135
  return yield
136
136
  rescue => e
137
- Sqreen.log.debug(e.inspect)
137
+ Sqreen.log.debug { e.inspect }
138
138
 
139
139
  current_retry += 1
140
140
 
@@ -2,30 +2,34 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  module Sqreen
5
+ # dedicated local storage
5
6
  module SharedStorage
6
-
7
- def self::get(key, default = nil)
8
- h = Thread.current["SQREEN_SHARED_STORAGE_#{self.object_id}"]
9
- return h.fetch(key, default) if h
10
- default
7
+ unless RUBY_VERSION >= '2.3.0'
8
+ def self.get(key)
9
+ h = Thread.current[:sqreen_shared_storage]
10
+ h[key] if h
11
+ end
11
12
  end
12
13
 
13
- def self::set(key, obj)
14
- Thread.current["SQREEN_SHARED_STORAGE_#{self.object_id}"] ||= {}
15
- Thread.current["SQREEN_SHARED_STORAGE_#{self.object_id}"][key] = obj
14
+ def self.set(key, obj)
15
+ Thread.current[:sqreen_shared_storage] ||= {}
16
+ Thread.current[:sqreen_shared_storage][key] = obj
16
17
  end
17
18
 
18
19
  def self.clear
19
- return unless Thread.current["SQREEN_SHARED_STORAGE_#{self.object_id}"].is_a?(Hash)
20
- Thread.current["SQREEN_SHARED_STORAGE_#{self.object_id}"].clear
20
+ return unless Thread.current[:sqreen_shared_storage].is_a?(Hash)
21
+ Thread.current[:sqreen_shared_storage].clear
21
22
  end
22
23
 
23
24
  def self.inc(value)
24
- set(value, get(value, 0) + 1)
25
+ set(value, (get(value) || 0) + 1)
25
26
  end
26
27
 
27
28
  def self.dec(value)
28
- set(value, get(value, 0) - 1)
29
+ set(value, (get(value) || 0) - 1)
29
30
  end
30
31
  end
31
32
  end
33
+
34
+ # Change SharedStorage.get if ruby is actually newer
35
+ require 'sqreen/shared_storage23' if RUBY_VERSION >= '2.3.0'
@@ -2,17 +2,9 @@
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
3
 
4
4
  module Sqreen
5
- @@stats = nil
6
-
7
- def self::stats
8
- @@stats ||= Stats.new
9
- end
10
-
11
- class Stats
12
- attr_accessor :callbacks_calls
13
-
14
- def initialize
15
- @callbacks_calls = 0
5
+ module SharedStorage
6
+ def self.get(key)
7
+ Thread.current[:sqreen_shared_storage]&.[](key)
16
8
  end
17
9
  end
18
10
  end