sqreen 1.10.5-java → 1.11.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sqreen/callback_tree.rb +1 -1
- data/lib/sqreen/callbacks.rb +10 -0
- data/lib/sqreen/configuration.rb +15 -3
- data/lib/sqreen/frameworks/generic.rb +43 -4
- data/lib/sqreen/frameworks/request_recorder.rb +4 -0
- data/lib/sqreen/instrumentation.rb +102 -60
- data/lib/sqreen/log.rb +3 -0
- data/lib/sqreen/performance_notifications.rb +39 -7
- data/lib/sqreen/performance_notifications/log_performance.rb +70 -0
- data/lib/sqreen/performance_notifications/metrics.rb +1 -1
- data/lib/sqreen/performance_notifications/newrelic.rb +61 -5
- data/lib/sqreen/remote_command.rb +1 -0
- data/lib/sqreen/rule_callback.rb +11 -0
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +4 -0
- data/lib/sqreen/rules_callbacks/custom_error.rb +4 -0
- data/lib/sqreen/rules_callbacks/record_request_context.rb +4 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +44 -14
- data/lib/sqreen/runner.rb +14 -0
- data/lib/sqreen/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53669623f0ae28ec6d69e5811956073c925f977a0beceda24756f4c2dc0b1bf5
|
4
|
+
data.tar.gz: dd62e44b9461fc8fac1bd11a511d1c4023fc152ced31afdaf17934ed82824e85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23dd698494d6913be0cf911d034ae7c51fa2969959f3758eed165958d390e6b80860f1c20d40298a3b1acf42456c0ca276dc65b0ab161cae917cf18ede700fb7
|
7
|
+
data.tar.gz: c9567b90cdeb4cf836cf8f20a0781e584d3cf3dddf09b83434ad70b91d3f17122660dfac4766cf818b08c77f22fcf6fbc13d84bfc2668e2257d26fcc2ad77e42
|
data/lib/sqreen/callback_tree.rb
CHANGED
data/lib/sqreen/callbacks.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'set'
|
5
5
|
require 'sqreen/shared_storage'
|
6
|
+
require 'sqreen/log'
|
6
7
|
|
7
8
|
module Sqreen
|
8
9
|
|
@@ -67,6 +68,15 @@ module Sqreen
|
|
67
68
|
def to_s
|
68
69
|
format('#<%s: %s.%s>', self.class, @klass, @method)
|
69
70
|
end
|
71
|
+
|
72
|
+
def overtime!
|
73
|
+
Sqreen.log.debug { "#{self} is overtime!" }
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def framework
|
78
|
+
nil
|
79
|
+
end
|
70
80
|
end
|
71
81
|
# target_method, position, callback, callback class
|
72
82
|
|
data/lib/sqreen/configuration.rb
CHANGED
@@ -11,8 +11,8 @@ module Sqreen
|
|
11
11
|
def self.config_init(framework = nil)
|
12
12
|
@config = Configuration.new(framework)
|
13
13
|
@config.load!
|
14
|
-
if @config && config_get(:report_perf_newrelic)
|
15
|
-
Sqreen::PerformanceNotifications::NewRelic.enable
|
14
|
+
if @config && config_get(:report_perf_newrelic) > 0
|
15
|
+
Sqreen::PerformanceNotifications::NewRelic.enable(config_get(:report_perf_newrelic))
|
16
16
|
end
|
17
17
|
@config
|
18
18
|
end
|
@@ -44,6 +44,8 @@ module Sqreen
|
|
44
44
|
{ :env => :SQREEN_BLOCK_ALL_RULES, :name => :block_all_rules,
|
45
45
|
:default => nil },
|
46
46
|
{ :env => :SQREEN_REPORT_PERF_NR, :name => :report_perf_newrelic,
|
47
|
+
:default => 0, :convert => :to_int },
|
48
|
+
{ :env => :SQREEN_REPORT_PERF, :name => :report_perf,
|
47
49
|
:default => false, :convert => :to_bool },
|
48
50
|
{ :env => :SQREEN_INITIAL_FEATURES, :name => :initial_features,
|
49
51
|
:default => nil },
|
@@ -53,7 +55,13 @@ module Sqreen
|
|
53
55
|
CONFIG_FILE_NAME = 'sqreen.yml'.freeze
|
54
56
|
|
55
57
|
def self.to_bool(value)
|
56
|
-
|
58
|
+
%w[1 true].include?(value.to_s.downcase.strip)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.to_int(value)
|
62
|
+
str = value.to_s.downcase.strip
|
63
|
+
str = 1 if str == 'true'
|
64
|
+
str.to_i
|
57
65
|
end
|
58
66
|
|
59
67
|
# Class to access configurations variables
|
@@ -164,5 +172,9 @@ module Sqreen
|
|
164
172
|
def to_bool(value)
|
165
173
|
Sqreen::to_bool(value)
|
166
174
|
end
|
175
|
+
|
176
|
+
def to_int(value)
|
177
|
+
Sqreen::to_int(value)
|
178
|
+
end
|
167
179
|
end
|
168
180
|
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
3
|
require 'ipaddr'
|
4
|
+
require 'set'
|
4
5
|
|
5
6
|
require 'sqreen/events/remote_exception'
|
6
7
|
require 'sqreen/callbacks'
|
@@ -199,8 +200,8 @@ module Sqreen
|
|
199
200
|
end
|
200
201
|
|
201
202
|
# Does the parameters value include this value
|
202
|
-
def params_include?(value)
|
203
|
-
params = request_params
|
203
|
+
def params_include?(value, params = nil)
|
204
|
+
params = request_params if params.nil?
|
204
205
|
return false if params.nil?
|
205
206
|
each_value_for_hash(params) do |param|
|
206
207
|
return true if param == value
|
@@ -209,8 +210,8 @@ module Sqreen
|
|
209
210
|
end
|
210
211
|
|
211
212
|
# Does the parameters key/value include this value
|
212
|
-
def full_params_include?(value)
|
213
|
-
params = request_params
|
213
|
+
def full_params_include?(value, params = nil)
|
214
|
+
params = request_params if params.nil?
|
214
215
|
return false if params.nil?
|
215
216
|
each_key_value_for_hash(params) do |param|
|
216
217
|
return true if param == value
|
@@ -218,12 +219,22 @@ module Sqreen
|
|
218
219
|
false
|
219
220
|
end
|
220
221
|
|
222
|
+
def mark_request_overtime!
|
223
|
+
over = SharedStorage.get(:request_overtime)
|
224
|
+
return false if over
|
225
|
+
SharedStorage.set(:request_overtime, true)
|
226
|
+
true
|
227
|
+
end
|
228
|
+
|
221
229
|
# Fetch and store the current request object
|
222
230
|
# Nota: cleanup should be performed at end of request (see clean_request)
|
223
231
|
def store_request(object)
|
224
232
|
return unless ensure_rack_loaded
|
233
|
+
self.remaining_perf_budget = Sqreen.performance_budget
|
225
234
|
SharedStorage.set(:request, Rack::Request.new(object))
|
226
235
|
SharedStorage.inc(:stored_requests)
|
236
|
+
SharedStorage.set(:xss_params, nil)
|
237
|
+
SharedStorage.set(:request_overtime, nil)
|
227
238
|
end
|
228
239
|
|
229
240
|
# Get the currently stored request
|
@@ -236,7 +247,18 @@ module Sqreen
|
|
236
247
|
return unless SharedStorage.dec(:stored_requests) <= 0
|
237
248
|
payload_creator = Sqreen::PayloadCreator.new(self)
|
238
249
|
close_request_record(Sqreen.queue, Sqreen.observations_queue, payload_creator)
|
250
|
+
self.remaining_perf_budget = nil
|
239
251
|
SharedStorage.set(:request, nil)
|
252
|
+
SharedStorage.set(:xss_params, nil)
|
253
|
+
SharedStorage.set(:request_overtime, nil)
|
254
|
+
end
|
255
|
+
|
256
|
+
def remaining_perf_budget
|
257
|
+
SharedStorage.get(:performance_budget)
|
258
|
+
end
|
259
|
+
|
260
|
+
def remaining_perf_budget=(value)
|
261
|
+
SharedStorage.set(:performance_budget, value)
|
240
262
|
end
|
241
263
|
|
242
264
|
def request_params
|
@@ -347,6 +369,23 @@ module Sqreen
|
|
347
369
|
request.env['REMOTE_ADDR']
|
348
370
|
end
|
349
371
|
|
372
|
+
def xss_params(regexp = nil)
|
373
|
+
p = SharedStorage.get(:xss_params)
|
374
|
+
return p unless p.nil?
|
375
|
+
p = request_params
|
376
|
+
parm = Set.new
|
377
|
+
each_key_value_for_hash(p) do |value|
|
378
|
+
next unless value.is_a?(String)
|
379
|
+
next if value.size < 5
|
380
|
+
next if regexp && !regexp.match(value)
|
381
|
+
parm << value
|
382
|
+
end
|
383
|
+
p = parm.to_a
|
384
|
+
Sqreen.log.debug { "Filtered XSS params: #{p.inspect}" }
|
385
|
+
SharedStorage.set(:xss_params, p)
|
386
|
+
p
|
387
|
+
end
|
388
|
+
|
350
389
|
protected
|
351
390
|
|
352
391
|
# Is this a whitelisted path?
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'set'
|
4
4
|
require 'sqreen/shared_storage'
|
5
5
|
require 'sqreen/events/request_record'
|
6
|
+
require 'sqreen/performance_notifications/log_performance'
|
7
|
+
require 'sqreen/performance_notifications/newrelic'
|
6
8
|
|
7
9
|
module Sqreen
|
8
10
|
# Store event/observations that happened in this request
|
@@ -45,6 +47,8 @@ module Sqreen
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def close_request_record(queue, observations_queue, payload_creator)
|
50
|
+
Sqreen::PerformanceNotifications::LogPerformance.next_request
|
51
|
+
Sqreen::PerformanceNotifications::NewRelic.next_request
|
48
52
|
clean_request_record if observed_items.nil?
|
49
53
|
if only_metric_observation
|
50
54
|
push_metrics(observations_queue, queue)
|
@@ -34,6 +34,7 @@ require 'set'
|
|
34
34
|
module Sqreen
|
35
35
|
class Instrumentation
|
36
36
|
WHITELISTED_METRIC='whitelisted'.freeze
|
37
|
+
OVERTIME_METRIC='request_overtime'.freeze
|
37
38
|
@@override_semaphore = Mutex.new
|
38
39
|
|
39
40
|
## Overriden methods and callbacks globals
|
@@ -64,38 +65,52 @@ module Sqreen
|
|
64
65
|
callbacks = callbacks.reject(&:whitelisted?)
|
65
66
|
end
|
66
67
|
|
68
|
+
cb_with_framework = callbacks.find(&:framework)
|
69
|
+
budget = nil
|
70
|
+
budget = cb_with_framework.framework.remaining_perf_budget if cb_with_framework
|
67
71
|
returns = []
|
68
72
|
callbacks.each do |cb|
|
69
73
|
# If record_request is part of callbacks we should filter after it ran
|
70
74
|
next if cb.whitelisted?
|
71
75
|
rule = cb.rule_name if cb.respond_to?(:rule_name)
|
76
|
+
if !budget.nil? && budget <= 0
|
77
|
+
next if cb.overtime!
|
78
|
+
end
|
72
79
|
Sqreen.log.debug { "running pre cb #{cb}" }
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
84
|
-
returns << res
|
85
|
-
rescue => e
|
86
|
-
Sqreen.log.warn "we catch an exception: #{e.inspect}"
|
87
|
-
Sqreen.log.debug e.backtrace
|
88
|
-
if cb.respond_to?(:record_exception)
|
89
|
-
cb.record_exception(e)
|
90
|
-
else
|
91
|
-
Sqreen::RemoteException.record(e)
|
80
|
+
begin
|
81
|
+
start = Sqreen::PerformanceNotifications.time
|
82
|
+
res = cb.send(:pre, instance, *args, &block)
|
83
|
+
stop = Sqreen::PerformanceNotifications.time
|
84
|
+
# The first few pre callbacks could not have a request & hence a budget just yet so we try harder to find it
|
85
|
+
budget = cb_with_framework.framework.remaining_perf_budget if budget.nil? && !Sqreen.performance_budget.nil? && cb.framework
|
86
|
+
budget -= (stop - start) unless budget.nil?
|
87
|
+
if !res.nil? && cb.respond_to?(:block) && (!cb.block && !Sqreen.config_get(:block_all_rules))
|
88
|
+
Sqreen.log.debug do
|
89
|
+
"#{cb} cannot block, overriding return value"
|
92
90
|
end
|
93
|
-
|
91
|
+
res = nil
|
92
|
+
elsif res.is_a?(Hash)
|
93
|
+
res[:rule_name] = rule
|
94
|
+
end
|
95
|
+
returns << res
|
96
|
+
rescue StandardError => e
|
97
|
+
Sqreen.log.warn "we catch an exception: #{e.inspect}"
|
98
|
+
Sqreen.log.debug e.backtrace
|
99
|
+
if cb.respond_to?(:record_exception)
|
100
|
+
cb.record_exception(e)
|
101
|
+
else
|
102
|
+
Sqreen::RemoteException.record(e)
|
94
103
|
end
|
104
|
+
next
|
95
105
|
end
|
106
|
+
Sqreen::PerformanceNotifications.notify("Callbacks/#{rule || cb.class.name}/pre", start, stop)
|
96
107
|
end
|
108
|
+
cb_with_framework.framework.remaining_perf_budget=budget if cb_with_framework && !budget.nil?
|
97
109
|
returns
|
98
110
|
end
|
111
|
+
rescue StandardError => e
|
112
|
+
Sqreen.log.warn "we catched an exception between cbs: #{e.inspect}"
|
113
|
+
Sqreen::RemoteException.record(e)
|
99
114
|
end
|
100
115
|
|
101
116
|
def self.callback_wrapper_post(klass, method, return_val, instance, *args, &block)
|
@@ -105,36 +120,48 @@ module Sqreen
|
|
105
120
|
callbacks = callbacks.reject(&:whitelisted?)
|
106
121
|
end
|
107
122
|
|
123
|
+
cb_with_framework = callbacks.find(&:framework)
|
124
|
+
budget = nil
|
125
|
+
budget = cb_with_framework.framework.remaining_perf_budget if cb_with_framework
|
108
126
|
returns = []
|
109
127
|
callbacks.reverse_each do |cb|
|
110
128
|
rule = cb.rule_name if cb.respond_to?(:rule_name)
|
129
|
+
if !budget.nil? && budget <= 0
|
130
|
+
next if cb.overtime!
|
131
|
+
end
|
111
132
|
Sqreen.log.debug { "running post cb #{cb}" }
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
elsif res.is_a?(Hash)
|
121
|
-
res[:rule_name] = rule
|
122
|
-
end
|
123
|
-
returns << res
|
124
|
-
rescue => e
|
125
|
-
Sqreen.log.warn "we catch an exception: #{e.inspect}"
|
126
|
-
Sqreen.log.debug e.backtrace
|
127
|
-
if cb.respond_to?(:record_exception)
|
128
|
-
cb.record_exception(e)
|
129
|
-
else
|
130
|
-
Sqreen::RemoteException.record(e)
|
133
|
+
begin
|
134
|
+
start = Sqreen::PerformanceNotifications.time
|
135
|
+
res = cb.send(:post, return_val, instance, *args, &block)
|
136
|
+
stop = Sqreen::PerformanceNotifications.time
|
137
|
+
budget -= (stop - start) unless budget.nil?
|
138
|
+
if !res.nil? && cb.respond_to?(:block) && (!cb.block && !Sqreen.config_get(:block_all_rules))
|
139
|
+
Sqreen.log.debug do
|
140
|
+
"#{cb} cannot block, overriding return value"
|
131
141
|
end
|
132
|
-
|
142
|
+
res = nil
|
143
|
+
elsif res.is_a?(Hash)
|
144
|
+
res[:rule_name] = rule
|
145
|
+
end
|
146
|
+
returns << res
|
147
|
+
rescue => e
|
148
|
+
Sqreen.log.warn "we catch an exception: #{e.inspect}"
|
149
|
+
Sqreen.log.debug e.backtrace
|
150
|
+
if cb.respond_to?(:record_exception)
|
151
|
+
cb.record_exception(e)
|
152
|
+
else
|
153
|
+
Sqreen::RemoteException.record(e)
|
133
154
|
end
|
155
|
+
next
|
134
156
|
end
|
157
|
+
Sqreen::PerformanceNotifications.notify("Callbacks/#{rule || cb.class.name}/post", start, stop)
|
135
158
|
end
|
159
|
+
cb_with_framework.framework.remaining_perf_budget=budget if cb_with_framework && !budget.nil? && !cb_with_framework.framework.remaining_perf_budget.nil?
|
136
160
|
returns
|
137
161
|
end
|
162
|
+
rescue StandardError => e
|
163
|
+
Sqreen.log.warn "we catched an exception between cbs: #{e.inspect}"
|
164
|
+
Sqreen::RemoteException.record(e)
|
138
165
|
end
|
139
166
|
|
140
167
|
def self.callback_wrapper_failing(exception, klass, method, instance, *args, &block)
|
@@ -144,36 +171,48 @@ module Sqreen
|
|
144
171
|
callbacks = callbacks.reject(&:whitelisted?)
|
145
172
|
end
|
146
173
|
|
174
|
+
cb_with_framework = callbacks.find(&:framework)
|
175
|
+
budget = nil
|
176
|
+
budget = cb_with_framework.framework.remaining_perf_budget if cb_with_framework
|
147
177
|
returns = []
|
148
178
|
callbacks.each do |cb|
|
149
179
|
rule = cb.rule_name if cb.respond_to?(:rule_name)
|
180
|
+
if !budget.nil? && budget <= 0
|
181
|
+
next if cb.overtime!
|
182
|
+
end
|
150
183
|
Sqreen.log.debug { "running failing cb #{cb}" }
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
elsif res.is_a?(Hash)
|
160
|
-
res[:rule_name] = rule
|
184
|
+
begin
|
185
|
+
start = Sqreen::PerformanceNotifications.time
|
186
|
+
res = cb.send(:failing, exception, instance, *args, &block)
|
187
|
+
stop = Sqreen::PerformanceNotifications.time
|
188
|
+
budget -= (stop - start) unless budget.nil?
|
189
|
+
if !res.nil? && cb.respond_to?(:block) && (!cb.block && !Sqreen.config_get(:block_all_rules))
|
190
|
+
Sqreen.log.debug do
|
191
|
+
"#{cb} cannot block, overriding return value"
|
161
192
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
Sqreen.log.debug e.backtrace
|
166
|
-
if cb.respond_to?(:record_exception)
|
167
|
-
cb.record_exception(e)
|
168
|
-
else
|
169
|
-
Sqreen::RemoteException.record(e)
|
170
|
-
end
|
171
|
-
next
|
193
|
+
res = nil
|
194
|
+
elsif res.is_a?(Hash)
|
195
|
+
res[:rule_name] = rule
|
172
196
|
end
|
197
|
+
returns << res
|
198
|
+
rescue => e
|
199
|
+
Sqreen.log.warn "we catch an exception: #{e.inspect}"
|
200
|
+
Sqreen.log.debug e.backtrace
|
201
|
+
if cb.respond_to?(:record_exception)
|
202
|
+
cb.record_exception(e)
|
203
|
+
else
|
204
|
+
Sqreen::RemoteException.record(e)
|
205
|
+
end
|
206
|
+
next
|
173
207
|
end
|
208
|
+
Sqreen::PerformanceNotifications.notify("Callbacks/#{rule || cb.class.name}/failing", start, stop)
|
174
209
|
end
|
210
|
+
cb_with_framework.framework.remaining_perf_budget=budget if cb_with_framework && !budget.nil? && !cb_with_framework.framework.remaining_perf_budget.nil?
|
175
211
|
returns
|
176
212
|
end
|
213
|
+
rescue StandardError => e
|
214
|
+
Sqreen.log.warn "we catched an exception between cbs: #{e.inspect}"
|
215
|
+
Sqreen::RemoteException.record(e)
|
177
216
|
end
|
178
217
|
|
179
218
|
def self.guard_multi_call(instance, method, original_method, args, block)
|
@@ -594,6 +633,9 @@ module Sqreen
|
|
594
633
|
metrics_engine.create_metric('name' => WHITELISTED_METRIC,
|
595
634
|
'period' => 60,
|
596
635
|
'kind' => 'Sum')
|
636
|
+
metrics_engine.create_metric('name' => OVERTIME_METRIC,
|
637
|
+
'period' => 60,
|
638
|
+
'kind' => 'Sum')
|
597
639
|
end
|
598
640
|
end
|
599
641
|
end
|
data/lib/sqreen/log.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'logger'
|
5
5
|
|
6
6
|
require 'sqreen/performance_notifications/log'
|
7
|
+
require 'sqreen/performance_notifications/log_performance'
|
7
8
|
require 'sqreen/configuration'
|
8
9
|
|
9
10
|
module Sqreen
|
@@ -105,6 +106,8 @@ module Sqreen
|
|
105
106
|
log_level = ::Logger.const_get(level)
|
106
107
|
@logger.level = log_level
|
107
108
|
Sqreen::PerformanceNotifications::Log.enable if level == 'DEBUG'
|
109
|
+
return if level != 'DEBUG' && !Sqreen.config_get(:report_perf)
|
110
|
+
Sqreen::PerformanceNotifications::LogPerformance.enable
|
108
111
|
end
|
109
112
|
|
110
113
|
def create_error_logger
|
@@ -1,6 +1,13 @@
|
|
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
|
+
begin
|
5
|
+
Process.clock_gettime Process::CLOCK_MONOTONIC
|
6
|
+
SQREEN_MONO_TIME = Process::CLOCK_MONOTONIC
|
7
|
+
rescue StandardError
|
8
|
+
SQREEN_MONO_TIME = nil
|
9
|
+
end
|
10
|
+
|
4
11
|
module Sqreen
|
5
12
|
# This module enable us to keep track of sqreen resource usage
|
6
13
|
#
|
@@ -27,6 +34,16 @@ module Sqreen
|
|
27
34
|
id
|
28
35
|
end
|
29
36
|
|
37
|
+
if SQREEN_MONO_TIME
|
38
|
+
def time
|
39
|
+
Process.clock_gettime(SQREEN_MONO_TIME)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
def time
|
43
|
+
Time.now
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
30
47
|
# Is there a subscriber for this key
|
31
48
|
def listen_for?(key)
|
32
49
|
return true unless @subscriptions_all.empty?
|
@@ -40,6 +57,13 @@ module Sqreen
|
|
40
57
|
_instrument(key, meta, &block)
|
41
58
|
end
|
42
59
|
|
60
|
+
def notify(key, start, stop, meta = {})
|
61
|
+
return unless listen_for?(key)
|
62
|
+
notifiers_for(key).each do |callable|
|
63
|
+
callable.call(key, start, stop, meta)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
43
67
|
# Unsubscrube for a given subscription
|
44
68
|
def unsubscribe(subscription)
|
45
69
|
return unless @subscriptions_all.delete(subscription).nil?
|
@@ -72,13 +96,21 @@ module Sqreen
|
|
72
96
|
@subscriptions_all.values + str + reg
|
73
97
|
end
|
74
98
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
99
|
+
if SQREEN_MONO_TIME
|
100
|
+
def _instrument(key, meta)
|
101
|
+
start = Process.clock_gettime(SQREEN_MONO_TIME)
|
102
|
+
yield
|
103
|
+
ensure
|
104
|
+
stop = Process.clock_gettime(SQREEN_MONO_TIME)
|
105
|
+
notify(key, start, stop, meta)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
def _instrument(key, meta)
|
109
|
+
start = Time.now
|
110
|
+
yield
|
111
|
+
ensure
|
112
|
+
stop = Time.now
|
113
|
+
notify(key, start, stop, meta)
|
82
114
|
end
|
83
115
|
end
|
84
116
|
end
|
@@ -0,0 +1,70 @@
|
|
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 'sqreen/performance_notifications'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module PerformanceNotifications
|
8
|
+
# Log performances on the console
|
9
|
+
class LogPerformance < Log
|
10
|
+
@subid = nil
|
11
|
+
@facility = nil
|
12
|
+
class << self
|
13
|
+
def timings
|
14
|
+
v = SharedStorage.get(:log_performance_timings)
|
15
|
+
if v.nil?
|
16
|
+
v = []
|
17
|
+
self.timings = v
|
18
|
+
end
|
19
|
+
v
|
20
|
+
end
|
21
|
+
|
22
|
+
def timings=(value)
|
23
|
+
SharedStorage.set(:log_performance_timings, value)
|
24
|
+
end
|
25
|
+
|
26
|
+
def log(event, start, finish, _meta)
|
27
|
+
timings << [event, start, finish]
|
28
|
+
end
|
29
|
+
|
30
|
+
def enabled?
|
31
|
+
!@subid.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def next_request
|
35
|
+
return unless enabled?
|
36
|
+
(@facility || Sqreen.log).warn do
|
37
|
+
output = timings.map do |evt, start, finish|
|
38
|
+
[evt.split('/')[1], (finish - start) * 1000]
|
39
|
+
end
|
40
|
+
self.timings = []
|
41
|
+
total = output.map(&:last).inject(0, &:+)
|
42
|
+
rules = output.inject({}) do |acc, (e, t)|
|
43
|
+
tt, cc = (acc[e] || [0, 0])
|
44
|
+
acc[e] = [tt + t, cc + 1]
|
45
|
+
acc
|
46
|
+
end
|
47
|
+
format(
|
48
|
+
"Sqreen request overhead:\n" +
|
49
|
+
("%s: %.2fms (%d calls)\n" * rules.size) +
|
50
|
+
'Total: %.2fms', *rules.to_a.sort_by { |e| e[1] }.flatten, total
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def enable(facility = nil)
|
56
|
+
return unless @subid.nil?
|
57
|
+
@facility = facility
|
58
|
+
@subid = Sqreen::PerformanceNotifications.subscribe(nil,
|
59
|
+
&method(:log))
|
60
|
+
end
|
61
|
+
|
62
|
+
def disable
|
63
|
+
return if @subid.nil?
|
64
|
+
Sqreen::PerformanceNotifications.unsubscribe(@subid)
|
65
|
+
@subid = nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -12,7 +12,7 @@ module Sqreen
|
|
12
12
|
class << self
|
13
13
|
EVENT_CAT = 'sqreen_time'.freeze
|
14
14
|
def log(event, start, finish, _meta)
|
15
|
-
evt = [EVENT_CAT, event, (finish - start) * 1000, finish]
|
15
|
+
evt = [EVENT_CAT, event, (finish - start) * 1000, SQREEN_MONO_TIME ? Time.now.utc : finish]
|
16
16
|
Sqreen.observations_queue.push(evt)
|
17
17
|
end
|
18
18
|
|
@@ -8,18 +8,73 @@ module Sqreen
|
|
8
8
|
# Log performances on the console
|
9
9
|
class NewRelic
|
10
10
|
@subid = nil
|
11
|
-
|
12
|
-
@nr_name_regexp = %r{/([^/]+)$}
|
11
|
+
@level = 0
|
13
12
|
|
14
13
|
class << self
|
14
|
+
def timings
|
15
|
+
v = SharedStorage.get(:log_performance_nr_timings)
|
16
|
+
if v.nil?
|
17
|
+
v = []
|
18
|
+
self.timings = v
|
19
|
+
end
|
20
|
+
v
|
21
|
+
end
|
22
|
+
|
23
|
+
def timings=(value)
|
24
|
+
SharedStorage.set(:log_performance_nr_timings, value)
|
25
|
+
end
|
26
|
+
|
15
27
|
def log(event, start, finish, _meta)
|
16
|
-
|
17
|
-
|
28
|
+
timings << [event, start, finish]
|
29
|
+
end
|
30
|
+
|
31
|
+
def enabled?
|
32
|
+
!@subid.nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def report(hash)
|
36
|
+
if ::NewRelic::Agent.respond_to?(:add_custom_attributes)
|
37
|
+
return ::NewRelic::Agent.add_custom_attributes(hash)
|
38
|
+
end
|
39
|
+
::NewRelic::Agent.add_custom_parameters(:user_id => @user.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
def next_request
|
43
|
+
return unless enabled?
|
44
|
+
if @level == 1
|
45
|
+
overhead = timings.map do |_evt, start, finish|
|
46
|
+
(finish - start) * 1000
|
47
|
+
end.inject(0, &:+)
|
48
|
+
report('sqreen_time' => overhead)
|
49
|
+
else
|
50
|
+
output = timings.map do |evt, start, finish|
|
51
|
+
[evt.split('/')[1], (finish - start) * 1000]
|
52
|
+
end
|
53
|
+
total = 0
|
54
|
+
count = 0
|
55
|
+
rules = output.inject({}) do |acc, (e, t)|
|
56
|
+
tt, cc = (acc[e] || [0, 0])
|
57
|
+
acc[e] = [tt + t, cc + 1]
|
58
|
+
total += t
|
59
|
+
count += 1
|
60
|
+
acc
|
61
|
+
end
|
62
|
+
attrs = rules.inject('sqreen_time' => total, 'sqreen_count' => count) do |acc, (rule, values)|
|
63
|
+
acc["sqreen_#{rule}_time"] = values[0]
|
64
|
+
acc["sqreen_#{rule}_count"] = values[1]
|
65
|
+
acc
|
66
|
+
end
|
67
|
+
report(attrs)
|
68
|
+
end
|
69
|
+
self.timings = []
|
18
70
|
end
|
19
71
|
|
20
|
-
def enable
|
72
|
+
def enable(level = 0)
|
21
73
|
return unless @subid.nil?
|
22
74
|
return unless defined?(::NewRelic::Agent)
|
75
|
+
return unless ::NewRelic::Agent.respond_to?(:add_custom_attributes) || ::NewRelic::Agent.respond_to?(:add_custom_parameters)
|
76
|
+
return unless level > 0
|
77
|
+
@level = level
|
23
78
|
Sqreen.log.debug('Enabling New Relic reporting')
|
24
79
|
@subid = Sqreen::PerformanceNotifications.subscribe(nil,
|
25
80
|
&method(:log))
|
@@ -29,6 +84,7 @@ module Sqreen
|
|
29
84
|
return if @subid.nil?
|
30
85
|
Sqreen::PerformanceNotifications.unsubscribe(@subid)
|
31
86
|
@subid = nil
|
87
|
+
@level = 0
|
32
88
|
end
|
33
89
|
end
|
34
90
|
end
|
data/lib/sqreen/rule_callback.rb
CHANGED
@@ -103,6 +103,17 @@ module Sqreen
|
|
103
103
|
}
|
104
104
|
framework.observe(:sqreen_exceptions, payload)
|
105
105
|
end
|
106
|
+
|
107
|
+
def overtime!
|
108
|
+
Sqreen.log.debug { "rulecb #{self} is overtime!" }
|
109
|
+
return true if framework.nil? || !framework.mark_request_overtime!
|
110
|
+
record_observation(
|
111
|
+
'request_overtime',
|
112
|
+
rule_name,
|
113
|
+
1
|
114
|
+
)
|
115
|
+
true
|
116
|
+
end
|
106
117
|
end
|
107
118
|
end
|
108
119
|
end
|
@@ -12,6 +12,15 @@ module Sqreen
|
|
12
12
|
module Rules
|
13
13
|
# XSSCB abstract common behaviour of tpls
|
14
14
|
class XSSCB < RegexpRuleCB
|
15
|
+
def initialize(*args)
|
16
|
+
super(*args)
|
17
|
+
@union_pattern = Regexp.union(*@patterns)
|
18
|
+
end
|
19
|
+
|
20
|
+
def xss_params
|
21
|
+
return nil unless framework
|
22
|
+
framework.xss_params(@union_pattern)
|
23
|
+
end
|
15
24
|
# The remaining code is only to find out if user entry was an attack,
|
16
25
|
# and record it. Since we don't rely on it to respond to user, it would
|
17
26
|
# be better to do it in background.
|
@@ -35,7 +44,7 @@ module Sqreen
|
|
35
44
|
|
36
45
|
# Sqreen::log.debug value
|
37
46
|
|
38
|
-
return unless
|
47
|
+
return unless xss_params.any? { |p| p == value }
|
39
48
|
|
40
49
|
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
41
50
|
|
@@ -62,7 +71,7 @@ module Sqreen
|
|
62
71
|
|
63
72
|
# Sqreen::log.debug value
|
64
73
|
|
65
|
-
return unless
|
74
|
+
return unless xss_params.any? { |p| p == value }
|
66
75
|
|
67
76
|
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
68
77
|
|
@@ -70,9 +79,7 @@ module Sqreen
|
|
70
79
|
return unless report_dangerous_xss?(saved_value)
|
71
80
|
|
72
81
|
# potential XSS! let's escape
|
73
|
-
if block
|
74
|
-
args[0].replace(CGI.escape_html(value))
|
75
|
-
end
|
82
|
+
args[0].replace(CGI.escape_html(value)) if block
|
76
83
|
|
77
84
|
advise_action(nil)
|
78
85
|
end
|
@@ -85,16 +92,13 @@ module Sqreen
|
|
85
92
|
class ReflectedXSSHamlCB < XSSCB
|
86
93
|
def post(ret, _inst, *_args, &_block)
|
87
94
|
value = ret
|
88
|
-
return
|
89
|
-
|
90
|
-
# Sqreen::log.debug value
|
95
|
+
return unless value.is_a?(String)
|
91
96
|
|
92
|
-
return
|
97
|
+
# 99% of the time we return here
|
98
|
+
return unless xss_params.any? { |p| p == value }
|
93
99
|
|
94
100
|
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
95
101
|
|
96
|
-
return unless value.is_a?(String)
|
97
|
-
|
98
102
|
return unless report_dangerous_xss?(value)
|
99
103
|
|
100
104
|
return unless block
|
@@ -117,6 +121,10 @@ module Sqreen
|
|
117
121
|
end
|
118
122
|
nil
|
119
123
|
end
|
124
|
+
|
125
|
+
def overtime!
|
126
|
+
false
|
127
|
+
end
|
120
128
|
end
|
121
129
|
|
122
130
|
# Hook into haml4 tag parser
|
@@ -132,6 +140,10 @@ module Sqreen
|
|
132
140
|
end
|
133
141
|
nil
|
134
142
|
end
|
143
|
+
|
144
|
+
def overtime!
|
145
|
+
false
|
146
|
+
end
|
135
147
|
end
|
136
148
|
|
137
149
|
class Haml4UtilInterpolationHookCB < RuleCB
|
@@ -147,13 +159,18 @@ module Sqreen
|
|
147
159
|
if escapes.odd?
|
148
160
|
res << '#{'
|
149
161
|
else
|
162
|
+
# Use eval to get rid of string escapes
|
150
163
|
content = eval('"' + Haml::Util.balance(scan, '{', '}', 1)[0][0...-1] + '"')
|
151
164
|
content = "Haml::Helpers.html_escape((#{content}))" if escape_html
|
152
|
-
res << '#{Sqreen.escape_haml((' + content + '))}'
|
165
|
+
res << '#{Sqreen.escape_haml((' + content + '))}'
|
153
166
|
end
|
154
167
|
end
|
155
168
|
{ :status => :skip, :new_return_value => res + rest }
|
156
169
|
end
|
170
|
+
|
171
|
+
def overtime!
|
172
|
+
false
|
173
|
+
end
|
157
174
|
end
|
158
175
|
|
159
176
|
# Hook build attributes
|
@@ -161,8 +178,9 @@ module Sqreen
|
|
161
178
|
def pre(inst, *args, &_block)
|
162
179
|
return unless Haml::VERSION < '5'
|
163
180
|
attrs = args[-1]
|
181
|
+
params = xss_params
|
164
182
|
new_attrs, found_xss = Haml4CompilerBuildAttributeCB.clean_hash_key(attrs) do |key|
|
165
|
-
if !key.nil? && key.is_a?(String) &&
|
183
|
+
if !key.nil? && key.is_a?(String) && params.any? { |p| p == key } && report_dangerous_xss?(key)
|
166
184
|
Sqreen.log.debug { format('Found unescaped user param: %s', key) }
|
167
185
|
[CGI.escape_html(key), true]
|
168
186
|
else
|
@@ -199,6 +217,10 @@ module Sqreen
|
|
199
217
|
end
|
200
218
|
[new_h, has_xss]
|
201
219
|
end
|
220
|
+
|
221
|
+
def overtime!
|
222
|
+
false
|
223
|
+
end
|
202
224
|
end
|
203
225
|
|
204
226
|
class Haml5EscapableHookCB < RuleCB
|
@@ -206,6 +228,10 @@ module Sqreen
|
|
206
228
|
args[0] = "Sqreen.escape_haml((#{args[0]}))"
|
207
229
|
{ :status => :modify_args, :args => args }
|
208
230
|
end
|
231
|
+
|
232
|
+
def overtime!
|
233
|
+
false
|
234
|
+
end
|
209
235
|
end
|
210
236
|
|
211
237
|
# Hook into temple template rendering
|
@@ -214,6 +240,10 @@ module Sqreen
|
|
214
240
|
ret[1] = "Sqreen.escape_temple((#{ret[1]}))"
|
215
241
|
{ :status => :override, :new_return_value => ret }
|
216
242
|
end
|
243
|
+
|
244
|
+
def overtime!
|
245
|
+
false
|
246
|
+
end
|
217
247
|
end
|
218
248
|
|
219
249
|
# Hook into temple template rendering
|
@@ -222,7 +252,7 @@ module Sqreen
|
|
222
252
|
value = args[0]
|
223
253
|
return if value.nil?
|
224
254
|
|
225
|
-
return unless
|
255
|
+
return unless xss_params.any? { |p| p == value }
|
226
256
|
|
227
257
|
Sqreen.log.debug { format('Found unescaped user param: %s', value) }
|
228
258
|
|
data/lib/sqreen/runner.rb
CHANGED
@@ -59,6 +59,12 @@ module Sqreen
|
|
59
59
|
def update_whitelisted_ips(paths)
|
60
60
|
@whitelisted_ips = Hash[paths.map { |v| [v, IPAddr.new(v)] }].freeze
|
61
61
|
end
|
62
|
+
|
63
|
+
attr_reader :performance_budget
|
64
|
+
def update_performance_budget(value)
|
65
|
+
return @performance_budget = nil if value.nil?
|
66
|
+
@performance_budget = value.to_f / 1000
|
67
|
+
end
|
62
68
|
end
|
63
69
|
|
64
70
|
# Main running job class for the agent
|
@@ -94,6 +100,7 @@ module Sqreen
|
|
94
100
|
@url = @configuration.get(:url)
|
95
101
|
Sqreen.update_whitelisted_paths([])
|
96
102
|
Sqreen.update_whitelisted_ips({})
|
103
|
+
Sqreen.update_performance_budget(nil)
|
97
104
|
raise(Sqreen::Exception, 'no url found') unless @url
|
98
105
|
raise(Sqreen::TokenNotFoundException, 'no token found') unless @token
|
99
106
|
|
@@ -251,6 +258,13 @@ module Sqreen
|
|
251
258
|
true
|
252
259
|
end
|
253
260
|
|
261
|
+
def change_performance_budget(budget, _context_infos = {})
|
262
|
+
return false unless budget.nil? || budget.to_f > 0
|
263
|
+
prev = Sqreen.performance_budget
|
264
|
+
Sqreen.update_performance_budget(budget)
|
265
|
+
{ :was => prev }
|
266
|
+
end
|
267
|
+
|
254
268
|
def upload_bundle(_context_infos = {})
|
255
269
|
t = Time.now
|
256
270
|
session.post_bundle(RuntimeInfos.dependencies_signature, RuntimeInfos.dependencies)
|
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.11.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Sqreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execjs
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- lib/sqreen/payload_creator.rb
|
87
87
|
- lib/sqreen/performance_notifications.rb
|
88
88
|
- lib/sqreen/performance_notifications/log.rb
|
89
|
+
- lib/sqreen/performance_notifications/log_performance.rb
|
89
90
|
- lib/sqreen/performance_notifications/metrics.rb
|
90
91
|
- lib/sqreen/performance_notifications/newrelic.rb
|
91
92
|
- lib/sqreen/remote_command.rb
|