sqreen 1.10.5-java → 1.11.0-java
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/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
|