sqreen 1.17.2.rc1-java → 1.18.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/CHANGELOG.md +6 -14
- data/lib/sqreen/encoding_sanitizer.rb +22 -0
- data/lib/sqreen/events/request_record.rb +1 -21
- data/lib/sqreen/log.rb +20 -0
- data/lib/sqreen/rules_callbacks.rb +1 -0
- data/lib/sqreen/rules_callbacks/waf.rb +102 -0
- data/lib/sqreen/runtime_infos.rb +26 -1
- data/lib/sqreen/session.rb +10 -10
- data/lib/sqreen/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 922051da24f2022f3a83abf7f948dc5736755b1470e3c07e51ea620b4ad79d38
|
4
|
+
data.tar.gz: 286a940f0c898061965af56bc612cff127123743c81f29460bdb50b74f336cac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 366be041f989a6668942f1e7f41be437572a9b128ff8622f916b5482aaf21b5f45f0ecdd7df11c44f1fa69b4823abc518e8c200fcc6b421d394f98e2b942c426
|
7
|
+
data.tar.gz: aec4842b37b82db5d7603a225fd1b14ece321793cf5d0f9e52f96be4acfc335427ea8056144bd3a8f0bfe960a8709b990e407c6329f746fe7a3aa03ebc30957e
|
data/CHANGELOG.md
CHANGED
@@ -1,23 +1,15 @@
|
|
1
|
-
## 1.
|
1
|
+
## 1.18.0
|
2
2
|
|
3
|
-
*
|
4
|
-
* Fix user signup tracking issue
|
3
|
+
* Support In-App WAF
|
5
4
|
|
6
|
-
## 1.17.2
|
5
|
+
## 1.17.2
|
7
6
|
|
7
|
+
* Support Rails 6.0 (single database)
|
8
|
+
* Improve output of logging
|
9
|
+
* Fix user signup tracking issue
|
8
10
|
* Improve performance of user tracking
|
9
11
|
* Improve reliability of user tracking against performance budget
|
10
|
-
* Restore compatibility with Ruby 1.9.3, 2.0, and 2.1 and JRuby 9.2
|
11
|
-
|
12
|
-
## 1.17.2.beta2
|
13
|
-
|
14
|
-
* Important note: this beta release supports Ruby 2.2 or above only
|
15
12
|
* Add support for Sinatra 2.0
|
16
|
-
|
17
|
-
## 1.17.2.beta1
|
18
|
-
|
19
|
-
* Important note: this beta release supports Rails only, and notably excludes Sinatra support
|
20
|
-
* Important note: this beta release supports Ruby 2.2 or above only
|
21
13
|
* Improve Sqreen thread boot when using Unicorn, Rainbows, Puma, Passenger, Thin, Webrick
|
22
14
|
* Improve performance cap consistency with specification
|
23
15
|
* Improve consistency of rule precondition argument passing
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sqreen
|
2
|
+
class EncodingSanitizer
|
3
|
+
def self.sanitize(obj)
|
4
|
+
case obj
|
5
|
+
when String
|
6
|
+
sanitize_string(obj)
|
7
|
+
when Array
|
8
|
+
obj.map { |e| sanitize(e) }
|
9
|
+
when Hash
|
10
|
+
obj.each_with_object({}) { |(k, v), h| h[k] = sanitize(v) }
|
11
|
+
else
|
12
|
+
obj
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.sanitize_string(s)
|
17
|
+
return s if s.encoding.name == 'UTF-8' && s.valid_encoding?
|
18
|
+
|
19
|
+
s.encode('UTF-16', :invalid => :replace, :undef => :replace).encode('UTF-8')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
require 'json'
|
5
5
|
require 'sqreen/event'
|
6
|
+
require 'sqreen/encoding_sanitizer'
|
6
7
|
|
7
8
|
module Sqreen
|
8
9
|
# When a request is deeemed worthy of being sent to the backend
|
@@ -115,27 +116,6 @@ module Sqreen
|
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
118
|
-
class EncodingSanitizer
|
119
|
-
def self.sanitize(obj)
|
120
|
-
case obj
|
121
|
-
when String
|
122
|
-
sanitize_string(obj)
|
123
|
-
when Array
|
124
|
-
obj.map { |e| sanitize(e) }
|
125
|
-
when Hash
|
126
|
-
obj.each_with_object({}) { |(k, v), h| h[k] = sanitize(v) }
|
127
|
-
else
|
128
|
-
obj
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def self.sanitize_string(s)
|
133
|
-
return s if s.encoding.name == 'UTF-8' && s.valid_encoding?
|
134
|
-
|
135
|
-
s.encode('UTF-16', :invalid => :replace, :undef => :replace).encode('UTF-8')
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
119
|
# For redacting sensitive data and avoid having it sent to our servers
|
140
120
|
class SensitiveDataRedactor
|
141
121
|
DEFAULT_SENSITIVE_KEYS = Set.new(%w[password secret passwd authorization api_key apikey access_token]).freeze
|
data/lib/sqreen/log.rb
CHANGED
@@ -62,6 +62,10 @@ module Sqreen
|
|
62
62
|
|
63
63
|
# Wrapper class for sqreen logging
|
64
64
|
class Logger
|
65
|
+
SEVERITY_TO_METHOD = ::Logger::Severity.constants.each_with_object({}) do |s, h|
|
66
|
+
h[::Logger::Severity.const_get(s)] = s.downcase
|
67
|
+
end
|
68
|
+
|
65
69
|
def initialize(desired_level, log_location, force_logger = nil)
|
66
70
|
if force_logger
|
67
71
|
@logger = force_logger
|
@@ -90,6 +94,10 @@ module Sqreen
|
|
90
94
|
@logger.error(msg, &block)
|
91
95
|
end
|
92
96
|
|
97
|
+
def add(severity, msg = nil, &block)
|
98
|
+
send(SEVERITY_TO_METHOD[severity], msg, &block)
|
99
|
+
end
|
100
|
+
|
93
101
|
protected
|
94
102
|
|
95
103
|
def init_logger_output(path)
|
@@ -135,6 +143,10 @@ module Sqreen
|
|
135
143
|
|
136
144
|
def error(_msg = nil); end
|
137
145
|
|
146
|
+
def fatal(_msg = nil); end
|
147
|
+
|
148
|
+
def add(_severity, _msg = nil); end
|
149
|
+
|
138
150
|
def formatter=(_); end
|
139
151
|
end
|
140
152
|
|
@@ -162,6 +174,14 @@ module Sqreen
|
|
162
174
|
@logger.error(msg, &block)
|
163
175
|
end
|
164
176
|
|
177
|
+
def fatal(msg = nil, &block)
|
178
|
+
@logger.error(msg, &block)
|
179
|
+
end
|
180
|
+
|
181
|
+
def add(severity, msg = nil, &block)
|
182
|
+
send(Sqreen::Logger::SEVERITY_TO_METHOD[severity], msg, &block)
|
183
|
+
end
|
184
|
+
|
165
185
|
def formatter=(value)
|
166
186
|
@logger.formatter = value
|
167
187
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'sqreen/rule_attributes'
|
3
|
+
require 'sqreen/binding_accessor'
|
4
|
+
require 'sqreen/rule_callback'
|
5
|
+
require 'sqreen/safe_json'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
module Rules
|
9
|
+
class WAFCB < RuleCB
|
10
|
+
BUDGET_MAX = 5000
|
11
|
+
|
12
|
+
# TODO: move to Dependency
|
13
|
+
begin
|
14
|
+
require 'libsqreen'
|
15
|
+
@libsqreen = true
|
16
|
+
rescue LoadError
|
17
|
+
Sqreen.log.warn('libsqreen gem not found')
|
18
|
+
@libsqreen = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.libsqreen?
|
22
|
+
@libsqreen
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :binding_accessors, :budget, :waf_rule_name
|
26
|
+
|
27
|
+
def initialize(*args)
|
28
|
+
super(*args)
|
29
|
+
@overtimeable = false
|
30
|
+
|
31
|
+
unless WAFCB.libsqreen?
|
32
|
+
Sqreen.log.warn('libsqreen gem not found')
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
unless @data['values']
|
37
|
+
Sqreen.log.warn('no values in data')
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
::LibSqreen::WAF.logger = Sqreen.log
|
42
|
+
|
43
|
+
name = format("%s_%s", SecureRandom.uuid, rule_name)
|
44
|
+
unless @data['values']['waf_rules'] && (::LibSqreen::WAF[name] = @data['values']['waf_rules'])
|
45
|
+
Sqreen.log.error("WAF rule #{name} failed to be set, from #<#{self.class.name}:0x#{object_id.to_s(16).rjust(16, '0')}>")
|
46
|
+
return
|
47
|
+
end
|
48
|
+
@waf_rule_name = name
|
49
|
+
Sqreen.log.debug("WAF rule #{name} set, from #<#{self.class.name}:0x#{object_id.to_s(16).rjust(16, '0')}>")
|
50
|
+
|
51
|
+
@binding_accessors = @data['values'].fetch('binding_accessors', []).each_with_object({}) do |e, h|
|
52
|
+
h[e] = BindingAccessor.new(e)
|
53
|
+
end
|
54
|
+
@budget = @data['values'].fetch('budget', BUDGET_MAX)
|
55
|
+
|
56
|
+
ObjectSpace.define_finalizer(self, WAFCB.finalizer(@waf_rule_name.dup))
|
57
|
+
end
|
58
|
+
|
59
|
+
def pre(instance, args, _budget)
|
60
|
+
unless WAFCB.libsqreen?
|
61
|
+
Sqreen.log.warn('libsqreen not required')
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
request = framework.request
|
66
|
+
return if !waf_rule_name || !request
|
67
|
+
|
68
|
+
env = [binding, framework, instance, args]
|
69
|
+
|
70
|
+
waf_args = Hash[binding_accessors.map { |e, b| [e, b.resolve(*env)] }]
|
71
|
+
waf_args = Sqreen::EncodingSanitizer.sanitize(waf_args)
|
72
|
+
action, data = ::LibSqreen::WAF.run(waf_rule_name, waf_args, budget)
|
73
|
+
|
74
|
+
case action
|
75
|
+
when :monitor
|
76
|
+
record_event({ 'waf_data' => data })
|
77
|
+
advise_action(nil)
|
78
|
+
when :block
|
79
|
+
record_event({ 'waf_data' => data })
|
80
|
+
advise_action(:raise)
|
81
|
+
when :good
|
82
|
+
advise_action(nil)
|
83
|
+
when :timeout, :invalid_call, :invalid_rule, :invalid_flow, :no_rule
|
84
|
+
Sqreen.log.warn("error from waf: #{action}")
|
85
|
+
advise_action(nil)
|
86
|
+
else
|
87
|
+
Sqreen.log.warn("unexpected action returned from waf")
|
88
|
+
advise_action(nil)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.finalizer(rule_name)
|
93
|
+
lambda do |object_id|
|
94
|
+
return unless WAFCB.libsqreen?
|
95
|
+
|
96
|
+
::LibSqreen::WAF.delete(waf_rule_name, waf_args, budget)
|
97
|
+
Sqreen.log.debug("WAF rule #{rule_name} deleted, from #<#{name}:0x#{object_id.to_s(16).rjust(16, '0')}>")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/sqreen/runtime_infos.rb
CHANGED
@@ -65,7 +65,32 @@ module Sqreen
|
|
65
65
|
{
|
66
66
|
:agent_type => :ruby,
|
67
67
|
:agent_version => ::Sqreen::VERSION,
|
68
|
-
}
|
68
|
+
}.tap do |h|
|
69
|
+
h[:libsqreen_version] = libsqreen_version if libsqreen?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def libsqreen?
|
74
|
+
libsqreen_loaded? && !libsqreen_stub?
|
75
|
+
end
|
76
|
+
|
77
|
+
def libsqreen_loaded?
|
78
|
+
Kernel.const_defined?('LibSqreen')
|
79
|
+
end
|
80
|
+
|
81
|
+
def libsqreen_stub?
|
82
|
+
!::LibSqreen.respond_to?(:version)
|
83
|
+
end
|
84
|
+
|
85
|
+
def libsqreen_version
|
86
|
+
return unless libsqreen?
|
87
|
+
|
88
|
+
@libsqreen_version ||= case (version = ::LibSqreen.version)
|
89
|
+
when Array
|
90
|
+
version.map(&:to_s).join('.')
|
91
|
+
else
|
92
|
+
version
|
93
|
+
end
|
69
94
|
end
|
70
95
|
|
71
96
|
def os
|
data/lib/sqreen/session.rb
CHANGED
@@ -100,7 +100,7 @@ module Sqreen
|
|
100
100
|
rescue StandardError => e
|
101
101
|
Sqreen.log.debug { "Caught exception during request: #{e.inspect}" }
|
102
102
|
Sqreen.log.debug { e.backtrace }
|
103
|
-
Sqreen.log.debug "Cannot connect, retry in #{RETRY_CONNECT_SECONDS} seconds"
|
103
|
+
Sqreen.log.debug { "Cannot connect, retry in #{RETRY_CONNECT_SECONDS} seconds" }
|
104
104
|
sleep RETRY_CONNECT_SECONDS
|
105
105
|
@conn_retry += 1
|
106
106
|
retry
|
@@ -129,7 +129,7 @@ module Sqreen
|
|
129
129
|
raise e if max_retry != RETRY_FOREVER && current_retry >= max_retry || e.is_a?(Sqreen::NotImplementedYet) || e.is_a?(Sqreen::Unauthorized)
|
130
130
|
|
131
131
|
sleep_delay = [MAX_DELAY, retry_request_seconds * current_retry].min
|
132
|
-
Sqreen.log.debug format("Sleeping %ds before retry #{current_retry}/#{max_retry}", sleep_delay)
|
132
|
+
Sqreen.log.debug { format("Sleeping %ds before retry #{current_retry}/#{max_retry}", sleep_delay) }
|
133
133
|
sleep(sleep_delay)
|
134
134
|
|
135
135
|
retry
|
@@ -164,10 +164,10 @@ module Sqreen
|
|
164
164
|
@req_nb += 1
|
165
165
|
|
166
166
|
path = prefix_path(path)
|
167
|
-
Sqreen.log.debug format('%s %s (%s)', method, path, @token)
|
168
167
|
|
169
168
|
payload = {}
|
170
169
|
resiliently(RETRY_REQUEST_SECONDS, max_retry) do
|
170
|
+
Sqreen.log.debug { format('%s %s %s %s (%s)', method, path, JSON.dump(data), headers.inspect, @token) }
|
171
171
|
res = nil
|
172
172
|
MUTEX.synchronize do
|
173
173
|
res = case method.upcase
|
@@ -181,7 +181,7 @@ module Sqreen
|
|
181
181
|
end
|
182
182
|
@con.post(path, json_data, headers)
|
183
183
|
else
|
184
|
-
Sqreen.log.debug format('unknown method %s', method)
|
184
|
+
Sqreen.log.debug { format('unknown method %s', method) }
|
185
185
|
raise Sqreen::NotImplementedYet
|
186
186
|
end
|
187
187
|
end
|
@@ -192,17 +192,17 @@ module Sqreen
|
|
192
192
|
if res['Content-Type'] && res['Content-Type'].start_with?('application/json')
|
193
193
|
payload = JSON.parse(res.body)
|
194
194
|
unless payload['status']
|
195
|
-
Sqreen.log.debug
|
195
|
+
Sqreen.log.debug { format('Cannot %s %s. Parsed response body was: %s', method, path, payload.inspect) }
|
196
196
|
end
|
197
197
|
else
|
198
|
-
Sqreen.log.debug "Unexpected response Content-Type: #{res['Content-Type']}"
|
199
|
-
Sqreen.log.debug "Unexpected response body: #{res.body.inspect}"
|
198
|
+
Sqreen.log.debug { "Unexpected response Content-Type: #{res['Content-Type']}" }
|
199
|
+
Sqreen.log.debug { "Unexpected response body: #{res.body.inspect}" }
|
200
200
|
end
|
201
201
|
else
|
202
|
-
Sqreen.log.debug 'warning: empty return value'
|
202
|
+
Sqreen.log.debug { 'warning: empty return value' }
|
203
203
|
end
|
204
|
+
Sqreen.log.debug { format('%s %s (DONE: %s in %f ms)', method, path, res && res.code, (Time.now.utc - now) * 1000) }
|
204
205
|
end
|
205
|
-
Sqreen.log.debug format('%s %s (DONE in %f ms)', method, path, (Time.now.utc - now) * 1000)
|
206
206
|
payload
|
207
207
|
end
|
208
208
|
|
@@ -233,7 +233,7 @@ module Sqreen
|
|
233
233
|
end
|
234
234
|
Sqreen.log.info 'Login success.'
|
235
235
|
@session_id = res['session_id']
|
236
|
-
Sqreen.log.debug "received session_id #{@session_id}"
|
236
|
+
Sqreen.log.debug { "received session_id #{@session_id}" }
|
237
237
|
Sqreen.logged_in = true
|
238
238
|
res
|
239
239
|
end
|
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.18.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Sqreen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- lib/sqreen/dependency/rails.rb
|
79
79
|
- lib/sqreen/dependency/sentry.rb
|
80
80
|
- lib/sqreen/dependency/sinatra.rb
|
81
|
+
- lib/sqreen/encoding_sanitizer.rb
|
81
82
|
- lib/sqreen/event.rb
|
82
83
|
- lib/sqreen/events/attack.rb
|
83
84
|
- lib/sqreen/events/remote_exception.rb
|
@@ -140,6 +141,7 @@ files:
|
|
140
141
|
- lib/sqreen/rules_callbacks/shell_env.rb
|
141
142
|
- lib/sqreen/rules_callbacks/url_matches.rb
|
142
143
|
- lib/sqreen/rules_callbacks/user_agent_matches.rb
|
144
|
+
- lib/sqreen/rules_callbacks/waf.rb
|
143
145
|
- lib/sqreen/rules_signature.rb
|
144
146
|
- lib/sqreen/runner.rb
|
145
147
|
- lib/sqreen/runtime_infos.rb
|
@@ -163,9 +165,7 @@ files:
|
|
163
165
|
homepage: https://www.sqreen.io/
|
164
166
|
licenses: []
|
165
167
|
metadata: {}
|
166
|
-
post_install_message:
|
167
|
-
This is a Sqreen rc release and may not work in all situations.
|
168
|
-
Make sure to review CHANGELOG.md for important details.
|
168
|
+
post_install_message:
|
169
169
|
rdoc_options: []
|
170
170
|
require_paths:
|
171
171
|
- lib
|
@@ -176,9 +176,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
176
|
version: 1.9.3
|
177
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
|
-
- - "
|
179
|
+
- - ">="
|
180
180
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
181
|
+
version: '0'
|
182
182
|
requirements: []
|
183
183
|
rubyforge_project:
|
184
184
|
rubygems_version: 2.7.7
|