sqreen 1.6.5 → 1.7.0
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/lib/sqreen/callbacks.rb +6 -0
- data/lib/sqreen/events/attack.rb +0 -2
- data/lib/sqreen/frameworks/generic.rb +44 -0
- data/lib/sqreen/instrumentation.rb +11 -5
- data/lib/sqreen/remote_command.rb +9 -2
- data/lib/sqreen/rule_callback.rb +4 -19
- data/lib/sqreen/rules_callbacks.rb +1 -0
- data/lib/sqreen/rules_callbacks/blacklist_ips.rb +44 -0
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +2 -1
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +27 -2
- data/lib/sqreen/rules_callbacks/record_request_context.rb +13 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +2 -7
- data/lib/sqreen/rules_signature.rb +0 -1
- data/lib/sqreen/runner.rb +13 -0
- data/lib/sqreen/safe_json.rb +60 -0
- data/lib/sqreen/session.rb +2 -50
- data/lib/sqreen/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc11a3a48f65cd5aaa1b4e0444b122cf12ae2c21
|
4
|
+
data.tar.gz: 85c6e6f370a1bb862f554679063057774f96e7ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0d671840b6c605359512b50e23efef360f29d58944031ce0589683c64254a9d7205e91e7ecb6c17797a05e42c09b0d649a36c50de78468280fd4720eefe50b6
|
7
|
+
data.tar.gz: 7664d0468f7fd8e0ee71e03b325f02f5b0c76f53379046d2013232592dda05db0f3c9970db1ac046944ada579a42e87175077955485d649fdb207fdbe0e91589
|
data/lib/sqreen/callbacks.rb
CHANGED
@@ -58,6 +58,8 @@ module Sqreen
|
|
58
58
|
# - { :status => :reraise }: reraise
|
59
59
|
# - { :status => :override }: eat exception
|
60
60
|
# - { :retry => :retry }: try the block again
|
61
|
+
#
|
62
|
+
# CB can also declare that they are whitelisted and should not be run at the moment.
|
61
63
|
|
62
64
|
attr_reader :klass, :method
|
63
65
|
|
@@ -72,6 +74,10 @@ module Sqreen
|
|
72
74
|
raise(Sqreen::Exception, 'No callback provided') unless @has_pre || @has_post || @has_failing
|
73
75
|
end
|
74
76
|
|
77
|
+
def whitelisted?
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
75
81
|
def pre?
|
76
82
|
@has_pre
|
77
83
|
end
|
data/lib/sqreen/events/attack.rb
CHANGED
@@ -45,7 +45,6 @@ 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
|
49
48
|
res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
|
50
49
|
res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
|
51
50
|
res[:test] = rule_p['test'] if rule_p && rule_p['test']
|
@@ -56,7 +55,6 @@ module Sqreen
|
|
56
55
|
res[:params] = payload['params'] if payload['params']
|
57
56
|
res[:context] = payload['context'] if payload['context']
|
58
57
|
res[:headers] = payload['headers'] if payload['headers']
|
59
|
-
res[:whitelist_match] = whitelisted if whitelisted
|
60
58
|
res
|
61
59
|
end
|
62
60
|
end
|
@@ -291,8 +291,52 @@ module Sqreen
|
|
291
291
|
Dir.getwd
|
292
292
|
end
|
293
293
|
|
294
|
+
WHITELIST_KEY = 'sqreen.whitelisted_request'.freeze
|
295
|
+
|
296
|
+
# Return the current item that whitelist this request
|
297
|
+
# returns nil if request is not whitelisted
|
298
|
+
def whitelisted_match
|
299
|
+
return nil unless request
|
300
|
+
return request.env[WHITELIST_KEY] if request.env.key?(WHITELIST_KEY)
|
301
|
+
request.env[WHITELIST_KEY] = whitelisted_ip || whitelisted_path
|
302
|
+
end
|
303
|
+
|
304
|
+
# Returns the current path that whitelist the request
|
305
|
+
def whitelisted_path
|
306
|
+
path = request_path
|
307
|
+
return nil unless path
|
308
|
+
find_whitelisted_path(path)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Returns the current path that whitelist the request
|
312
|
+
def whitelisted_ip
|
313
|
+
ip = client_ip
|
314
|
+
return nil unless ip
|
315
|
+
find_whitelisted_ip(ip)
|
316
|
+
rescue
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
294
320
|
protected
|
295
321
|
|
322
|
+
# Is this a whitelisted path?
|
323
|
+
# return the path witelisted prefix that match path
|
324
|
+
def find_whitelisted_path(rpath)
|
325
|
+
(Sqreen.whitelisted_paths || []).find do |path|
|
326
|
+
rpath.start_with?(path)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Is this a whitelisted ip?
|
331
|
+
# return the ip witelisted range that match ip
|
332
|
+
def find_whitelisted_ip(rip)
|
333
|
+
ret = (Sqreen.whitelisted_ips || {}).find do |_, ip|
|
334
|
+
ip.include?(rip)
|
335
|
+
end
|
336
|
+
return nil unless ret
|
337
|
+
ret.first
|
338
|
+
end
|
339
|
+
|
296
340
|
def hook_rack_request(klass)
|
297
341
|
@calling_pid = Process.pid
|
298
342
|
klass.class_eval do
|
@@ -33,6 +33,7 @@ require 'set'
|
|
33
33
|
|
34
34
|
module Sqreen
|
35
35
|
class Instrumentation
|
36
|
+
WHITELISTED_METRIC='whitelisted'.freeze
|
36
37
|
@@override_semaphore = Mutex.new
|
37
38
|
|
38
39
|
## Overriden methods and callbacks globals
|
@@ -54,6 +55,7 @@ module Sqreen
|
|
54
55
|
def self.callback_wrapper_pre(klass, method, instance, *args, &block)
|
55
56
|
Instrumentation.guard_call(method, []) do
|
56
57
|
callbacks = @@registered_callbacks.get(klass, method, :pre)
|
58
|
+
callbacks.reject!(&:whitelisted?)
|
57
59
|
|
58
60
|
returns = []
|
59
61
|
callbacks.each do |cb|
|
@@ -90,6 +92,7 @@ module Sqreen
|
|
90
92
|
def self.callback_wrapper_post(klass, method, return_val, instance, *args, &block)
|
91
93
|
Instrumentation.guard_call(method, []) do
|
92
94
|
callbacks = @@registered_callbacks.get(klass, method, :post)
|
95
|
+
callbacks.reject!(&:whitelisted?)
|
93
96
|
|
94
97
|
returns = []
|
95
98
|
callbacks.reverse_each do |cb|
|
@@ -126,6 +129,7 @@ module Sqreen
|
|
126
129
|
def self.callback_wrapper_failing(exception, klass, method, instance, *args, &block)
|
127
130
|
Instrumentation.guard_call(method, []) do
|
128
131
|
callbacks = @@registered_callbacks.get(klass, method, :failing)
|
132
|
+
callbacks.reject!(&:whitelisted?)
|
129
133
|
|
130
134
|
returns = []
|
131
135
|
callbacks.each do |cb|
|
@@ -506,11 +510,13 @@ module Sqreen
|
|
506
510
|
|
507
511
|
def initialize(metrics_engine = nil)
|
508
512
|
self.metrics_engine = metrics_engine
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
513
|
+
return if metrics_engine.nil?
|
514
|
+
metrics_engine.create_metric('name' => CallCountable::COUNT_CALLS,
|
515
|
+
'period' => 60,
|
516
|
+
'kind' => 'Sum')
|
517
|
+
metrics_engine.create_metric('name' => WHITELISTED_METRIC,
|
518
|
+
'period' => 60,
|
519
|
+
'kind' => 'Sum')
|
514
520
|
end
|
515
521
|
end
|
516
522
|
end
|
@@ -1,6 +1,7 @@
|
|
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
|
-
require
|
3
|
+
require 'sqreen/log'
|
4
|
+
require 'sqreen/events/remote_exception'
|
4
5
|
|
5
6
|
module Sqreen
|
6
7
|
# Execute and sanitize remote commands
|
@@ -13,6 +14,7 @@ module Sqreen
|
|
13
14
|
:features_change => :change_features,
|
14
15
|
:force_logout => :shutdown,
|
15
16
|
:paths_whitelist => :change_whitelisted_paths,
|
17
|
+
:ips_whitelist => :change_whitelisted_ips,
|
16
18
|
}.freeze
|
17
19
|
|
18
20
|
attr_reader :uuid
|
@@ -27,7 +29,12 @@ module Sqreen
|
|
27
29
|
failing = validate_command(runner)
|
28
30
|
return failing if failing
|
29
31
|
Sqreen.log.debug format('processing command %s', @name)
|
30
|
-
|
32
|
+
begin
|
33
|
+
output = runner.send(KNOWN_COMMANDS[@name], *@params, context_infos)
|
34
|
+
rescue => e
|
35
|
+
Sqreen::RemoteException.record(e)
|
36
|
+
return { :status => false, :reason => "error: #{e.inspect}" }
|
37
|
+
end
|
31
38
|
format_output(output)
|
32
39
|
end
|
33
40
|
|
data/lib/sqreen/rule_callback.rb
CHANGED
@@ -46,39 +46,24 @@ module Sqreen
|
|
46
46
|
@rule[Attrs::RULESPACK_ID]
|
47
47
|
end
|
48
48
|
|
49
|
+
def whitelisted?
|
50
|
+
framework && !framework.whitelisted_match.nil?
|
51
|
+
end
|
52
|
+
|
49
53
|
# Recommend taking an action (optionnally adding more data/context)
|
50
54
|
#
|
51
55
|
# This will format the requested action and optionnally
|
52
56
|
# override it if it should not be taken (should not block for example)
|
53
57
|
def advise_action(action, additional_data = {})
|
54
58
|
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
59
|
additional_data.merge(:status => action)
|
63
60
|
end
|
64
61
|
|
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
|
-
|
73
62
|
# Record an attack event into Sqreen system
|
74
63
|
# @param infos [Hash] Additional information about request
|
75
64
|
def record_event(infos)
|
76
65
|
payload = @payload_generator.payload(framework, @rule)
|
77
66
|
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
|
82
67
|
Attack.record(payload)
|
83
68
|
end
|
84
69
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
require 'sqreen/rule_callback'
|
7
|
+
|
8
|
+
module Sqreen
|
9
|
+
module Rules
|
10
|
+
# Looks for a blacklisted ip and block
|
11
|
+
class BlacklistIPsCB < RuleCB
|
12
|
+
def initialize(klass, method, rule_hash)
|
13
|
+
super(klass, method, rule_hash)
|
14
|
+
@ips = Hash[@data['values'].map { |v| [v, IPAddr.new(v)] }]
|
15
|
+
raise ArgumentError.new("no ips given") if @ips.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def pre(_inst, *_args, &_block)
|
19
|
+
return unless framework
|
20
|
+
ip = framework.client_ip
|
21
|
+
return unless ip
|
22
|
+
found = find_blacklisted_ip(ip)
|
23
|
+
return unless found
|
24
|
+
Sqreen.log.debug { "Found blacklisted IP #{ip} - found: #{found}" }
|
25
|
+
record_observation('blacklisted', found, 1)
|
26
|
+
advise_action(:raise)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
# Is this a blacklisted ip?
|
32
|
+
# return the ip blacklisted range that match ip
|
33
|
+
def find_blacklisted_ip(rip)
|
34
|
+
ret = (@ips || {}).find do |_, ip|
|
35
|
+
ip.include?(rip)
|
36
|
+
end
|
37
|
+
return nil unless ret
|
38
|
+
ret.first
|
39
|
+
rescue
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'sqreen/rule_attributes'
|
5
5
|
require 'sqreen/rule_callback'
|
6
|
+
require 'sqreen/safe_json'
|
6
7
|
|
7
8
|
module Sqreen
|
8
9
|
module Rules
|
@@ -31,7 +32,7 @@ module Sqreen
|
|
31
32
|
key = @accessors.map do |accessor|
|
32
33
|
accessor.resolve(binding, framework, inst, args, @data, rv)
|
33
34
|
end
|
34
|
-
record_observation(@metric_category,
|
35
|
+
record_observation(@metric_category, SafeJSON.dump(key), 1)
|
35
36
|
advise_action(nil)
|
36
37
|
end
|
37
38
|
end
|
@@ -73,6 +73,7 @@ module Sqreen
|
|
73
73
|
|
74
74
|
def match(str)
|
75
75
|
return if str.nil? || str.empty?
|
76
|
+
str = enforce_encoding(str)
|
76
77
|
istr = str.downcase
|
77
78
|
|
78
79
|
@string.each do |type, cases|
|
@@ -92,11 +93,35 @@ module Sqreen
|
|
92
93
|
end
|
93
94
|
end
|
94
95
|
|
95
|
-
|
96
|
-
|
96
|
+
if defined?(Encoding)
|
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
|
+
warn(Encoding.compatible?(p, str).inspect)
|
104
|
+
return p if p.match(str)
|
105
|
+
end
|
97
106
|
end
|
98
107
|
nil
|
99
108
|
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def enforce_encoding(str)
|
113
|
+
return str unless str.is_a?(String)
|
114
|
+
return str if str.ascii_only?
|
115
|
+
encoded8bit = str.encoding.name == 'ASCII-8BIT'
|
116
|
+
return str if !encoded8bit && str.valid_encoding?
|
117
|
+
str.chars.map do |v|
|
118
|
+
if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
|
119
|
+
''
|
120
|
+
else
|
121
|
+
v
|
122
|
+
end
|
123
|
+
end.join
|
124
|
+
end
|
100
125
|
end
|
101
126
|
|
102
127
|
# A configurable matcher rule
|
@@ -2,13 +2,26 @@
|
|
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'
|
5
6
|
|
6
7
|
module Sqreen
|
7
8
|
module Rules
|
8
9
|
# Save request context for handling further down
|
9
10
|
class RecordRequestContext < RuleCB
|
11
|
+
def whitelisted?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
10
15
|
def pre(_inst, *args, &_block)
|
11
16
|
framework.store_request(args[0])
|
17
|
+
wh = framework.whitelisted_match
|
18
|
+
if wh
|
19
|
+
unless Sqreen.features.key?('whitelisted_metric') &&
|
20
|
+
!Sqreen.features['whitelisted_metric']
|
21
|
+
record_observation(Instrumentation::WHITELISTED_METRIC, wh, 1)
|
22
|
+
end
|
23
|
+
Sqreen.log.debug { "Request was whitelisted because of #{wh}" }
|
24
|
+
end
|
12
25
|
advise_action(nil)
|
13
26
|
end
|
14
27
|
|
@@ -47,8 +47,7 @@ module Sqreen
|
|
47
47
|
return unless report_dangerous_xss?(saved_value)
|
48
48
|
|
49
49
|
# potential XSS! let's escape
|
50
|
-
if block
|
51
|
-
(!framework || !find_whitelisted_path(framework.request_path.to_s))
|
50
|
+
if block
|
52
51
|
args[0].replace(CGI.escape_html(value))
|
53
52
|
end
|
54
53
|
|
@@ -150,9 +149,6 @@ module Sqreen
|
|
150
149
|
|
151
150
|
return if !found_xss || !block
|
152
151
|
# potential XSS! let's escape
|
153
|
-
if framework && find_whitelisted_path(framework.request_path.to_s)
|
154
|
-
return nil
|
155
|
-
end
|
156
152
|
args[-1] = new_attrs
|
157
153
|
r = inst.send(method, *args)
|
158
154
|
{ :status => :skip, :new_return_value => r }
|
@@ -213,8 +209,7 @@ module Sqreen
|
|
213
209
|
|
214
210
|
return unless block
|
215
211
|
# potential XSS! let's escape
|
216
|
-
if block
|
217
|
-
(!framework || !find_whitelisted_path(framework.request_path.to_s))
|
212
|
+
if block
|
218
213
|
args[0] = CGI.escape_html(value)
|
219
214
|
r = inst.send(method, *args)
|
220
215
|
return { :status => :skip, :new_return_value => r }
|
data/lib/sqreen/runner.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
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
3
|
|
4
|
+
require 'ipaddr'
|
4
5
|
require 'timeout'
|
5
6
|
require 'json'
|
6
7
|
|
@@ -53,6 +54,11 @@ module Sqreen
|
|
53
54
|
def update_whitelisted_paths(paths)
|
54
55
|
@whitelisted_paths = paths.freeze
|
55
56
|
end
|
57
|
+
|
58
|
+
attr_reader :whitelisted_ips
|
59
|
+
def update_whitelisted_ips(paths)
|
60
|
+
@whitelisted_ips = Hash[paths.map { |v| [v, IPAddr.new(v)] }].freeze
|
61
|
+
end
|
56
62
|
end
|
57
63
|
|
58
64
|
# Main running job class for the agent
|
@@ -87,6 +93,7 @@ module Sqreen
|
|
87
93
|
@token = @configuration.get(:token)
|
88
94
|
@url = @configuration.get(:url)
|
89
95
|
Sqreen.update_whitelisted_paths([])
|
96
|
+
Sqreen.update_whitelisted_ips({})
|
90
97
|
raise(Sqreen::Exception, 'no url found') unless @url
|
91
98
|
raise(Sqreen::TokenNotFoundException, 'no token found') unless @token
|
92
99
|
|
@@ -244,6 +251,12 @@ module Sqreen
|
|
244
251
|
true
|
245
252
|
end
|
246
253
|
|
254
|
+
def change_whitelisted_ips(ips, _context_infos = {})
|
255
|
+
return false unless ips.respond_to?(:each)
|
256
|
+
Sqreen.update_whitelisted_ips(ips)
|
257
|
+
true
|
258
|
+
end
|
259
|
+
|
247
260
|
def change_features(new_features, _context_infos = {})
|
248
261
|
old = features
|
249
262
|
self.features = new_features
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require 'sqreen/log'
|
7
|
+
|
8
|
+
module Sqreen
|
9
|
+
# Safely dump datastructure in json (more resilient to encoding errors)
|
10
|
+
class SafeJSON
|
11
|
+
def self.dump(data)
|
12
|
+
JSON.generate(data)
|
13
|
+
rescue JSON::GeneratorError, Encoding::UndefinedConversionError
|
14
|
+
Sqreen.log.debug('Payload could not be encoded enforcing recode')
|
15
|
+
JSON.generate(rencode_payload(data))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.rencode_payload(obj, max_depth = 20)
|
19
|
+
max_depth -= 1
|
20
|
+
return obj if max_depth < 0
|
21
|
+
return rencode_array(obj, max_depth) if obj.is_a?(Array)
|
22
|
+
return enforce_encoding(obj) unless obj.is_a?(Hash)
|
23
|
+
nobj = {}
|
24
|
+
obj.each do |k, v|
|
25
|
+
safe_k = rencode_payload(k, max_depth)
|
26
|
+
nobj[safe_k] = case v
|
27
|
+
when Array
|
28
|
+
rencode_array(v, max_depth)
|
29
|
+
when Hash
|
30
|
+
rencode_payload(v, max_depth)
|
31
|
+
when String
|
32
|
+
enforce_encoding(v)
|
33
|
+
else # for example integers
|
34
|
+
v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
nobj
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.rencode_array(array, max_depth)
|
41
|
+
array.map! { |e| rencode_payload(e, max_depth - 1) }
|
42
|
+
array
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.enforce_encoding(str)
|
46
|
+
return str unless str.is_a?(String)
|
47
|
+
return str if str.ascii_only?
|
48
|
+
encoded8bit = str.encoding.name == 'ASCII-8BIT'
|
49
|
+
return str if !encoded8bit && str.valid_encoding?
|
50
|
+
r = str.chars.map do |v|
|
51
|
+
if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
|
52
|
+
v.bytes.map { |c| "\\x#{c.to_s(16).upcase}" }.join
|
53
|
+
else
|
54
|
+
v
|
55
|
+
end
|
56
|
+
end.join
|
57
|
+
"SqBytes[#{r}]"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/sqreen/session.rb
CHANGED
@@ -6,9 +6,9 @@ require 'sqreen/serializer'
|
|
6
6
|
require 'sqreen/runtime_infos'
|
7
7
|
require 'sqreen/events/remote_exception'
|
8
8
|
require 'sqreen/exception'
|
9
|
+
require 'sqreen/safe_json'
|
9
10
|
|
10
11
|
require 'net/https'
|
11
|
-
require 'json'
|
12
12
|
require 'uri'
|
13
13
|
require 'openssl'
|
14
14
|
require 'zlib'
|
@@ -187,7 +187,7 @@ module Sqreen
|
|
187
187
|
json_data = nil
|
188
188
|
unless data.nil?
|
189
189
|
serialized = Serializer.serialize(data)
|
190
|
-
json_data = compress(
|
190
|
+
json_data = compress(SafeJSON.dump(serialized))
|
191
191
|
end
|
192
192
|
@con.post(path, json_data, headers)
|
193
193
|
else
|
@@ -209,13 +209,6 @@ module Sqreen
|
|
209
209
|
res
|
210
210
|
end
|
211
211
|
|
212
|
-
def self.encode_payload(data)
|
213
|
-
JSON.generate(data)
|
214
|
-
rescue JSON::GeneratorError, Encoding::UndefinedConversionError
|
215
|
-
Sqreen.log.debug('Payload could not be encoded enforcing recode')
|
216
|
-
JSON.generate(rencode_payload(data))
|
217
|
-
end
|
218
|
-
|
219
212
|
def compress(data)
|
220
213
|
return data unless request_compression
|
221
214
|
out = StringIO.new
|
@@ -225,47 +218,6 @@ module Sqreen
|
|
225
218
|
out.string
|
226
219
|
end
|
227
220
|
|
228
|
-
def self.rencode_payload(obj, max_depth = 20)
|
229
|
-
max_depth -= 1
|
230
|
-
return obj if max_depth < 0
|
231
|
-
return rencode_array(obj, max_depth) if obj.is_a?(Array)
|
232
|
-
return enforce_encoding(obj) unless obj.is_a?(Hash)
|
233
|
-
nobj = {}
|
234
|
-
obj.each do |k, v|
|
235
|
-
safe_k = rencode_payload(k, max_depth)
|
236
|
-
nobj[safe_k] = case v
|
237
|
-
when Array
|
238
|
-
rencode_array(v, max_depth)
|
239
|
-
when Hash
|
240
|
-
rencode_payload(v, max_depth)
|
241
|
-
when String
|
242
|
-
enforce_encoding(v)
|
243
|
-
else # for example integers
|
244
|
-
v
|
245
|
-
end
|
246
|
-
end
|
247
|
-
nobj
|
248
|
-
end
|
249
|
-
|
250
|
-
def self.rencode_array(array, max_depth)
|
251
|
-
array.map! { |e| rencode_payload(e, max_depth - 1) }
|
252
|
-
array
|
253
|
-
end
|
254
|
-
|
255
|
-
def self.enforce_encoding(str)
|
256
|
-
return str unless str.is_a?(String)
|
257
|
-
return str if str.ascii_only?
|
258
|
-
encoded8bit = str.encoding.name == 'ASCII-8BIT'
|
259
|
-
return str if !encoded8bit && str.valid_encoding?
|
260
|
-
str.chars.map do |v|
|
261
|
-
if !v.valid_encoding? || (encoded8bit && !v.ascii_only?)
|
262
|
-
v.bytes.map { |c| "\\x#{c.to_s(16).upcase}" }.join
|
263
|
-
else
|
264
|
-
v
|
265
|
-
end
|
266
|
-
end.join
|
267
|
-
end
|
268
|
-
|
269
221
|
def login(framework)
|
270
222
|
headers = { 'x-api-key' => @token }
|
271
223
|
|
data/lib/sqreen/version.rb
CHANGED
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sqreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- lib/sqreen/rules_callbacks.rb
|
94
94
|
- lib/sqreen/rules_callbacks/binding_accessor_matcher.rb
|
95
95
|
- lib/sqreen/rules_callbacks/binding_accessor_metrics.rb
|
96
|
+
- lib/sqreen/rules_callbacks/blacklist_ips.rb
|
96
97
|
- lib/sqreen/rules_callbacks/count_http_codes.rb
|
97
98
|
- lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb
|
98
99
|
- lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb
|
@@ -111,6 +112,7 @@ files:
|
|
111
112
|
- lib/sqreen/rules_signature.rb
|
112
113
|
- lib/sqreen/runner.rb
|
113
114
|
- lib/sqreen/runtime_infos.rb
|
115
|
+
- lib/sqreen/safe_json.rb
|
114
116
|
- lib/sqreen/sdk.rb
|
115
117
|
- lib/sqreen/serializer.rb
|
116
118
|
- lib/sqreen/session.rb
|
@@ -135,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
137
|
version: '0'
|
136
138
|
requirements: []
|
137
139
|
rubyforge_project:
|
138
|
-
rubygems_version: 2.6.
|
140
|
+
rubygems_version: 2.6.12
|
139
141
|
signing_key:
|
140
142
|
specification_version: 4
|
141
143
|
summary: Sqreen Ruby agent
|