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 +4 -4
- data/Rakefile +0 -20
- data/lib/sqreen/events/attack.rb +2 -0
- data/lib/sqreen/instrumentation.rb +8 -1
- data/lib/sqreen/remote_command.rb +1 -0
- data/lib/sqreen/rule_callback.rb +28 -0
- data/lib/sqreen/rules.rb +9 -2
- data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +1 -1
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +1 -1
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +1 -1
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +1 -1
- data/lib/sqreen/rules_callbacks/execjs.rb +4 -1
- data/lib/sqreen/rules_callbacks/headers_insert.rb +1 -1
- data/lib/sqreen/rules_callbacks/rails_parameters.rb +1 -1
- data/lib/sqreen/rules_callbacks/record_request_context.rb +3 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +48 -22
- data/lib/sqreen/rules_callbacks/shell_env.rb +1 -1
- data/lib/sqreen/rules_callbacks/url_matches.rb +1 -1
- data/lib/sqreen/rules_callbacks/user_agent_matches.rb +1 -1
- data/lib/sqreen/runner.rb +12 -0
- data/lib/sqreen/version.rb +1 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b13dece251d9c305d0f1f79ea551b1b51a35c20
|
4
|
+
data.tar.gz: 00acadad553d40803de98b2bb70c81c274e6aa83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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'])
|
data/lib/sqreen/events/attack.rb
CHANGED
@@ -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)
|
data/lib/sqreen/rule_callback.rb
CHANGED
@@ -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
|
|
data/lib/sqreen/rules.rb
CHANGED
@@ -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
|
|
@@ -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
|
-
|
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]
|
@@ -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
|
-
|
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
|
56
|
-
value =
|
61
|
+
def post(ret, _inst, *_args, &_block)
|
62
|
+
value = ret
|
63
|
+
return if value.nil?
|
57
64
|
|
58
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/sqreen/runner.rb
CHANGED
@@ -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
|
data/lib/sqreen/version.rb
CHANGED
@@ -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 =
|
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.
|
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-
|
11
|
+
date: 2017-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|