sqreen 1.3.21489051313 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1832eec9e4dccfbd713daa35419d991e16ea73c9
4
- data.tar.gz: fab4c69e57b92617ffafb1f96237ecf4863d407e
3
+ metadata.gz: 3b13dece251d9c305d0f1f79ea551b1b51a35c20
4
+ data.tar.gz: 00acadad553d40803de98b2bb70c81c274e6aa83
5
5
  SHA512:
6
- metadata.gz: a760f2d396252dd000c83d55842f0ec14db02632539d9ec465ff85fa708c76aa768665844267e7c1af111844eb0d02121e8a70c7deac6d38d8d706405c3f1fad
7
- data.tar.gz: 8189c5212276db0bae7161d62568271f19123095ea3f66d20da6671e8da6980c34df5b6d2598afa16481f08be3e15b940c7aa2629b1402df153216f0ff055fdd
6
+ metadata.gz: e61529e9cb80bf86226272f0843f9a8e9fc1d8fafaa71f29ec36ff0e7254f16d039ea5b083a6475c58b3ea2c49f4e9b112b616a3236c1e4a271c034a09a28b15
7
+ data.tar.gz: 6dd83b5fd2809940dcd9875c7321ef4efcad0b711fdeadb652197607e80003afe0d018c3bdc86643a1bc47839487a83cc6aeb5ca6f0812b4808e3033b6baa59c
data/Rakefile CHANGED
@@ -18,23 +18,3 @@ end
18
18
 
19
19
  desc 'Run tests'
20
20
  task :default => :test
21
-
22
- task :pre_build do
23
- version = Bundler::GemHelper.new.send(:version)
24
- template = <<-EOF.gsub(/^\s{0,3}/m, '')
25
- # Copyright (c) 2015 Sqreen. All Rights Reserved.
26
- # Please refer to our terms for more information: https://www.sqreen.io/terms.html
27
- # Warning This file is auto generated! DO NOT edit.
28
-
29
- module Sqreen
30
- VERSION = #{version.to_s.inspect}.freeze
31
- end
32
- EOF
33
- fname = File.join(File.dirname(__FILE__), 'lib', 'sqreen', 'version.rb')
34
- File.open(fname, 'w') do |f|
35
- f.puts template
36
- end
37
- end
38
-
39
- default_build = Rake::Task['build']
40
- default_build.prerequisites.push(Rake::Task['pre_build'])
@@ -45,6 +45,7 @@ module Sqreen
45
45
  res = {}
46
46
  rule_p = payload['rule']
47
47
  request_p = payload['request']
48
+ whitelisted = request_p.delete('whitelisted') if request_p
48
49
  res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
49
50
  res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
50
51
  res[:test] = rule_p['test'] if rule_p && rule_p['test']
@@ -55,6 +56,7 @@ module Sqreen
55
56
  res[:params] = payload['params'] if payload['params']
56
57
  res[:context] = payload['context'] if payload['context']
57
58
  res[:headers] = payload['headers'] if payload['headers']
59
+ res[:whitelist_match] = whitelisted if whitelisted
58
60
  res
59
61
  end
60
62
  end
@@ -371,6 +371,13 @@ module Sqreen
371
371
  klass.singleton_methods.include? method
372
372
  end
373
373
 
374
+ # Does this object or an instance of it respond_to method?
375
+ def valid_method?(obj, method)
376
+ return true if is_class_method?(obj, method)
377
+ return false unless obj.respond_to?(:instance_methods)
378
+ is_instance_method?(obj, method)
379
+ end
380
+
374
381
  def add_callback(cb)
375
382
  @@override_semaphore.synchronize do
376
383
  klass = cb.klass
@@ -487,7 +494,7 @@ module Sqreen
487
494
  end
488
495
  remove_all_callbacks # Force cb tree to be empty before instrumenting
489
496
  rules.each do |rule|
490
- rcb = Sqreen::Rules.cb_from_rule(rule, metrics_engine, verifier)
497
+ rcb = Sqreen::Rules.cb_from_rule(rule, self, metrics_engine, verifier)
491
498
  next unless rcb
492
499
  rcb.framework = framework
493
500
  add_callback(rcb)
@@ -12,6 +12,7 @@ module Sqreen
12
12
  :features_get => :features,
13
13
  :features_change => :change_features,
14
14
  :force_logout => :shutdown,
15
+ :paths_whitelist => :change_whitelisted_paths,
15
16
  }.freeze
16
17
 
17
18
  attr_reader :uuid
@@ -46,11 +46,39 @@ module Sqreen
46
46
  @rule[Attrs::RULESPACK_ID]
47
47
  end
48
48
 
49
+ # Recommend taking an action (optionnally adding more data/context)
50
+ #
51
+ # This will format the requested action and optionnally
52
+ # override it if it should not be taken (should not block for example)
53
+ def advise_action(action, additional_data = {})
54
+ return if action.nil? && additional_data.empty?
55
+ if %w(override raise).include?(action.to_s) && framework
56
+ prefix = find_whitelisted_path(framework.request_path.to_s)
57
+ if prefix
58
+ Sqreen.log.debug { "Trying to block on whitelisted path #{prefix}" }
59
+ action = nil
60
+ end
61
+ end
62
+ additional_data.merge(:status => action)
63
+ end
64
+
65
+ # Is this a whitelisted path?
66
+ # return the path witelisted prefix that match path
67
+ def find_whitelisted_path(rpath)
68
+ (Sqreen.whitelisted_paths || []).find do |path|
69
+ rpath.start_with?(path)
70
+ end
71
+ end
72
+
49
73
  # Record an attack event into Sqreen system
50
74
  # @param infos [Hash] Additional information about request
51
75
  def record_event(infos)
52
76
  payload = @payload_generator.payload(framework, @rule)
53
77
  payload['infos'] = infos
78
+ if framework && payload['request']
79
+ prefix = find_whitelisted_path(framework.request_path.to_s)
80
+ payload['request']['whitelisted'] = prefix
81
+ end
54
82
  Attack.record(payload)
55
83
  end
56
84
 
@@ -40,9 +40,10 @@ module Sqreen
40
40
 
41
41
  # Given a rule, will instantiate the related callback.
42
42
  # @param hash_rule [Hash] Rules metadata
43
+ # @param instrumentation_engine [Instrumentation] Instrumentation engine
43
44
  # @param metrics_store [MetricStore] Metrics storage facility
44
45
  # @param verifier [SqreenSignedVerifier] Signed verifier
45
- def self::cb_from_rule(hash_rule, metrics_store = nil, verifier = nil)
46
+ def self::cb_from_rule(hash_rule, instrumentation_engine=nil, metrics_store = nil, verifier = nil)
46
47
  # Check rules signature
47
48
  if verifier
48
49
  raise InvalidSignatureException unless verifier.verify(hash_rule)
@@ -56,12 +57,18 @@ module Sqreen
56
57
 
57
58
  if instr_class.nil?
58
59
  rule_name = hash_rule[Attrs::NAME]
59
- Sqreen.log.debug "#{klass} does not exists. Skipping #{rule_name}"
60
+ Sqreen.log.debug { "#{klass} does not exists. Skipping #{rule_name}" }
60
61
  return nil
61
62
  end
62
63
 
63
64
  instr_method = hook[Attrs::METHOD]
64
65
  instr_method = instr_method.to_sym
66
+ if instrumentation_engine &&
67
+ !instrumentation_engine.valid_method?(instr_class, instr_method)
68
+
69
+ Sqreen.log.debug { "#{instr_method} does not exist on #{klass} Skipping #{rule_name}" }
70
+ return nil
71
+ end
65
72
 
66
73
  cbname = hook[Attrs::CALLBACK_CLASS]
67
74
 
@@ -63,7 +63,7 @@ module Sqreen
63
63
  accessor.resolve(binding, framework, inst, args, @data, rv)
64
64
  end
65
65
  record_observation(category, key, value)
66
- nil
66
+ advise_action(nil)
67
67
  end
68
68
 
69
69
  def build_expressions(callbacks)
@@ -11,7 +11,7 @@ module Sqreen
11
11
  def post(rv, _inst, *_args, &_block)
12
12
  return unless rv.is_a?(Array) && !rv.empty?
13
13
  record_observation(METRIC_CATEGORY, rv[0], 1)
14
- nil
14
+ advise_action(nil)
15
15
  end
16
16
  end
17
17
  end
@@ -17,7 +17,7 @@ module Sqreen
17
17
  Sqreen.log.debug { "Found UA #{ua} - found: #{found}" }
18
18
  infos = { :found => found }
19
19
  record_event(infos)
20
- nil
20
+ advise_action(nil)
21
21
  end
22
22
  end
23
23
  end
@@ -17,7 +17,7 @@ module Sqreen
17
17
  return unless found
18
18
  Sqreen.log.debug { "Found UA #{ua} - found: #{found}" }
19
19
  record_observation(CRAWLER_CATEGORY, ua, 1)
20
- nil
20
+ advise_action(nil)
21
21
  end
22
22
  end
23
23
  end
@@ -94,7 +94,10 @@ module Sqreen
94
94
  end
95
95
  Sqreen.log.debug { [name, arguments].inspect }
96
96
  ret = @compiled.call("callbacks.#{name}", *arguments)
97
- return ret unless record_and_continue?(ret)
97
+ unless record_and_continue?(ret)
98
+ return nil if ret.nil?
99
+ return advise_action(ret[:status], ret)
100
+ end
98
101
  name = ret[:call]
99
102
  rv = ret[:data]
100
103
  args_override = ret[:args]
@@ -15,7 +15,7 @@ module Sqreen
15
15
  headers.each do |name, value|
16
16
  rv[1][name] = value
17
17
  end
18
- nil
18
+ advise_action(nil)
19
19
  end
20
20
  end
21
21
  end
@@ -7,7 +7,7 @@ module Sqreen
7
7
  module Rules
8
8
  class RailsParametersCB < RuleCB
9
9
  def pre(_inst, *_args, &_block)
10
- nil
10
+ advise_action(nil)
11
11
  end
12
12
  end
13
13
  end
@@ -9,14 +9,17 @@ module Sqreen
9
9
  class RecordRequestContext < RuleCB
10
10
  def pre(_inst, *args, &_block)
11
11
  framework.store_request(args[0])
12
+ advise_action(nil)
12
13
  end
13
14
 
14
15
  def post(_rv, _inst, *_args, &_block)
15
16
  framework.clean_request
17
+ advise_action(nil)
16
18
  end
17
19
 
18
20
  def failing(_exception, _inst, *_args, &_block)
19
21
  framework.clean_request
22
+ advise_action(nil)
20
23
  end
21
24
  end
22
25
  end
@@ -3,9 +3,12 @@
3
3
 
4
4
  require 'cgi'
5
5
 
6
+ require 'sqreen/rule_callback'
6
7
  require 'sqreen/rules_callbacks/regexp_rule'
7
8
 
9
+ # Sqreen module
8
10
  module Sqreen
11
+ # Sqreen rules
9
12
  module Rules
10
13
  # look for reflected XSS with erb template engine
11
14
  class ReflectedXSSCB < RegexpRuleCB
@@ -25,11 +28,14 @@ module Sqreen
25
28
 
26
29
  saved_value = value.dup
27
30
  # potential XSS! let's escape
28
- args[0].replace(CGI.escape_html(value)) if block
31
+ if block &&
32
+ (!framework || !find_whitelisted_path(framework.request_path.to_s))
33
+ args[0].replace(CGI.escape_html(value))
34
+ end
29
35
 
30
36
  report_dangerous_xss(saved_value)
31
37
 
32
- nil
38
+ advise_action(nil)
33
39
  end
34
40
 
35
41
  # The remaining code is only to find out if user entry was an attack,
@@ -41,7 +47,7 @@ module Sqreen
41
47
  return unless found
42
48
  infos = {
43
49
  :found => found,
44
- :payload => value
50
+ :payload => value,
45
51
  }
46
52
  record_event(infos)
47
53
  end
@@ -52,35 +58,55 @@ module Sqreen
52
58
  # escape_html, nuke_inner_whitespace,
53
59
  # interpolated, ugly)
54
60
  class ReflectedXSSHamlCB < ReflectedXSSCB
55
- def pre(inst, *args, &_block)
56
- value = args[0]
61
+ def post(ret, _inst, *_args, &_block)
62
+ value = ret
63
+ return if value.nil?
57
64
 
58
- return unless value.is_a?(String)
59
-
60
- # if escape_html is already true, it will be escaped later
61
- return if args[4]
65
+ # Sqreen::log.debug value
62
66
 
63
67
  return unless framework.params_include?(value)
64
68
 
65
69
  Sqreen.log.debug { format('Found unescaped user param: %s', value) }
66
70
 
67
- # potential XSS ! Call the same method with the argument
68
- # escape_html true
69
- if block
70
- nargs = args.dup
71
- nargs[4] = true
72
- return_value = {
73
- :status => :skip,
74
- # 'method' is an attribut reader in class CB, it's the name of the
75
- # hooked method
76
- :new_return_value => inst.send(method, *nargs),
77
- }
78
- end
71
+ return unless value.is_a?(String)
79
72
 
80
73
  report_dangerous_xss(value)
81
74
 
82
- return_value
75
+ return unless block
76
+ # potential XSS! let's escape
77
+ advise_action(:override, :new_return_value => CGI.escape_html(value))
78
+ end
79
+ end
80
+
81
+ # Hook into haml4 script parser
82
+ class Haml4ParserScriptHookCB < RuleCB
83
+ def pre(_inst, *args, &_block)
84
+ return unless args.size > 1
85
+ text = args[0]
86
+ escape_html = args[1]
87
+ if escape_html == false && !text.include?('html_escape')
88
+ args[0].replace("Sqreen.escape_haml(#{args[0]})")
89
+ end
90
+ nil
83
91
  end
84
92
  end
93
+
94
+ # Hook into haml4 tag parser
95
+ class Haml4ParserTagHookCB < RuleCB
96
+ def post(ret, _inst, *_args, &_block)
97
+ tag = ret
98
+ if tag.value[:escape_html] == false &&
99
+ !tag.value[:value].include?('html_escape')
100
+ tag.value[:value] = "Sqreen.escape_haml(#{tag.value[:value]})"
101
+ return { :status => :override, :new_return_value => tag }
102
+ end
103
+ nil
104
+ end
105
+ end
106
+ end
107
+
108
+ # Escape HAML when instrumented to do it
109
+ def self.escape_haml(x)
110
+ x
85
111
  end
86
112
  end
@@ -25,7 +25,7 @@ module Sqreen
25
25
  }
26
26
  Sqreen.log.warn "presence of a shell env tampering: #{infos.inspect}"
27
27
  record_event(infos)
28
- { :status => :raise }
28
+ advise_action(:raise)
29
29
  end
30
30
  end
31
31
  end
@@ -18,7 +18,7 @@ module Sqreen
18
18
  found = match_regexp(path)
19
19
  infos = { :path => path, :found => found }
20
20
  record_event(infos) if found
21
- nil
21
+ advise_action(nil)
22
22
  end
23
23
  end
24
24
  end
@@ -15,7 +15,7 @@ module Sqreen
15
15
  Sqreen.log.debug { "Found UA #{ua} - found: #{found}" }
16
16
  infos = { :found => found }
17
17
  record_event(infos)
18
- { :status => :raise, :data => found }
18
+ advise_action(:raise, :data => found)
19
19
  end
20
20
  end
21
21
  end
@@ -48,6 +48,11 @@ module Sqreen
48
48
 
49
49
  attr_accessor :logged_in
50
50
  alias logged_in? logged_in
51
+
52
+ attr_reader :whitelisted_paths
53
+ def update_whitelisted_paths(paths)
54
+ @whitelisted_paths = paths.freeze
55
+ end
51
56
  end
52
57
 
53
58
  # Main running job class for the agent
@@ -81,6 +86,7 @@ module Sqreen
81
86
 
82
87
  @token = @configuration.get(:token)
83
88
  @url = @configuration.get(:url)
89
+ Sqreen.update_whitelisted_paths([])
84
90
  raise(Sqreen::Exception, 'no url found') unless @url
85
91
  raise(Sqreen::TokenNotFoundException, 'no token found') unless @token
86
92
 
@@ -232,6 +238,12 @@ module Sqreen
232
238
  batch_events(features['batch_size'], features['max_staleness'])
233
239
  end
234
240
 
241
+ def change_whitelisted_paths(paths, _context_infos = {})
242
+ return false unless paths.respond_to?(:each)
243
+ Sqreen.update_whitelisted_paths(paths)
244
+ true
245
+ end
246
+
235
247
  def change_features(new_features, _context_infos = {})
236
248
  old = features
237
249
  self.features = new_features
@@ -1,6 +1,5 @@
1
1
  # Copyright (c) 2015 Sqreen. All Rights Reserved.
2
2
  # Please refer to our terms for more information: https://www.sqreen.io/terms.html
3
- # Warning This file is auto generated! DO NOT edit.
4
3
  module Sqreen
5
- VERSION = "1.3.21489051313".freeze
4
+ VERSION = '1.4.2'.freeze
6
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqreen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.21489051313
4
+ version: 1.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sqreen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-09 00:00:00.000000000 Z
11
+ date: 2017-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: execjs