sqreen 1.3.21489051313 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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