sqreen 0.7.01461158029-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 +7 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/README.md +77 -0
- data/Rakefile +40 -0
- data/lib/sqreen.rb +67 -0
- data/lib/sqreen/binding_accessor.rb +184 -0
- data/lib/sqreen/ca.crt +72 -0
- data/lib/sqreen/callback_tree.rb +78 -0
- data/lib/sqreen/callbacks.rb +120 -0
- data/lib/sqreen/capped_queue.rb +23 -0
- data/lib/sqreen/condition_evaluator.rb +169 -0
- data/lib/sqreen/conditionable.rb +50 -0
- data/lib/sqreen/configuration.rb +151 -0
- data/lib/sqreen/context.rb +22 -0
- data/lib/sqreen/deliveries/batch.rb +80 -0
- data/lib/sqreen/deliveries/simple.rb +36 -0
- data/lib/sqreen/detect.rb +14 -0
- data/lib/sqreen/detect/shell_injection.rb +61 -0
- data/lib/sqreen/detect/sql_injection.rb +115 -0
- data/lib/sqreen/event.rb +16 -0
- data/lib/sqreen/events/attack.rb +60 -0
- data/lib/sqreen/events/remote_exception.rb +53 -0
- data/lib/sqreen/exception.rb +31 -0
- data/lib/sqreen/frameworks.rb +40 -0
- data/lib/sqreen/frameworks/generic.rb +243 -0
- data/lib/sqreen/frameworks/rails.rb +155 -0
- data/lib/sqreen/frameworks/rails3.rb +36 -0
- data/lib/sqreen/frameworks/sinatra.rb +34 -0
- data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
- data/lib/sqreen/instrumentation.rb +504 -0
- data/lib/sqreen/log.rb +116 -0
- data/lib/sqreen/metrics.rb +6 -0
- data/lib/sqreen/metrics/average.rb +39 -0
- data/lib/sqreen/metrics/base.rb +41 -0
- data/lib/sqreen/metrics/collect.rb +22 -0
- data/lib/sqreen/metrics/sum.rb +20 -0
- data/lib/sqreen/metrics_store.rb +94 -0
- data/lib/sqreen/parsers/sql.rb +98 -0
- data/lib/sqreen/parsers/sql_tokenizer.rb +266 -0
- data/lib/sqreen/parsers/unix.rb +110 -0
- data/lib/sqreen/payload_creator.rb +132 -0
- data/lib/sqreen/performance_notifications.rb +86 -0
- data/lib/sqreen/performance_notifications/log.rb +36 -0
- data/lib/sqreen/performance_notifications/metrics.rb +36 -0
- data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
- data/lib/sqreen/remote_command.rb +82 -0
- data/lib/sqreen/rule_attributes.rb +25 -0
- data/lib/sqreen/rule_callback.rb +97 -0
- data/lib/sqreen/rules.rb +116 -0
- data/lib/sqreen/rules_callbacks.rb +29 -0
- data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +18 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +25 -0
- data/lib/sqreen/rules_callbacks/execjs.rb +136 -0
- data/lib/sqreen/rules_callbacks/headers_insert.rb +20 -0
- data/lib/sqreen/rules_callbacks/inspect_rule.rb +20 -0
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +103 -0
- data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
- data/lib/sqreen/rules_callbacks/record_request_context.rb +23 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +40 -0
- data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
- data/lib/sqreen/rules_callbacks/shell.rb +33 -0
- data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
- data/lib/sqreen/rules_callbacks/sql.rb +41 -0
- data/lib/sqreen/rules_callbacks/system_shell.rb +25 -0
- data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
- data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
- data/lib/sqreen/rules_signature.rb +142 -0
- data/lib/sqreen/runner.rb +312 -0
- data/lib/sqreen/runtime_infos.rb +127 -0
- data/lib/sqreen/session.rb +340 -0
- data/lib/sqreen/stats.rb +18 -0
- data/lib/sqreen/version.rb +6 -0
- metadata +143 -0
data/lib/sqreen/log.rb
ADDED
@@ -0,0 +1,116 @@
|
|
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 'logger'
|
5
|
+
|
6
|
+
require 'sqreen/performance_notifications/log'
|
7
|
+
require 'sqreen/configuration'
|
8
|
+
|
9
|
+
module Sqreen
|
10
|
+
def self::log
|
11
|
+
return @logger unless @logger.nil?
|
12
|
+
@logger = Logger.new(
|
13
|
+
Sqreen.config_get(:log_level),
|
14
|
+
Sqreen.config_get(:log_location)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ruby default formatter modified to display current thread_id
|
19
|
+
class FormatterWithTid
|
20
|
+
Format = "%s, [%s#%d.%s] %5s -- %s: %s\n".freeze
|
21
|
+
DatetimeFormat = '%Y-%m-%dT%H:%M:%S.%6N '.freeze
|
22
|
+
|
23
|
+
attr_accessor :datetime_format
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@datetime_format = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(severity, time, progname, msg)
|
30
|
+
format(Format,
|
31
|
+
severity[0..0], format_datetime(time), $$,
|
32
|
+
Thread.current.object_id.to_s(36),
|
33
|
+
severity, progname, msg2str(msg)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def format_datetime(time)
|
40
|
+
time.strftime(DatetimeFormat)
|
41
|
+
end
|
42
|
+
|
43
|
+
def msg2str(msg)
|
44
|
+
case msg
|
45
|
+
when ::String
|
46
|
+
msg
|
47
|
+
when ::Exception
|
48
|
+
"#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
|
49
|
+
else
|
50
|
+
msg.inspect
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Wrapper class for sqreen logging
|
56
|
+
class Logger
|
57
|
+
def initialize(desired_level, log_location, force_logger = nil)
|
58
|
+
if force_logger
|
59
|
+
@logger = force_logger
|
60
|
+
else
|
61
|
+
init_logger_output(log_location)
|
62
|
+
end
|
63
|
+
init_log_level(desired_level)
|
64
|
+
enforce_log_format(@logger)
|
65
|
+
create_error_logger
|
66
|
+
end
|
67
|
+
|
68
|
+
def debug(msg = nil, &block)
|
69
|
+
@logger.debug(msg, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def info(msg = nil, &block)
|
73
|
+
@logger.info(msg, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def warn(msg = nil, &block)
|
77
|
+
@logger.warn(msg, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def error(msg = nil, &block)
|
81
|
+
@error_logger.error(msg, &block)
|
82
|
+
@logger.error(msg, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def init_logger_output(path)
|
88
|
+
path = File.expand_path(path)
|
89
|
+
if File.writable?(path) || File.writable?(File.dirname(path))
|
90
|
+
@logger = ::Logger.new(path)
|
91
|
+
else
|
92
|
+
@logger = ::Logger.new(STDOUT)
|
93
|
+
@logger.info("Cannot access #{path} for writing. Defaulting to stdout")
|
94
|
+
end
|
95
|
+
rescue => e
|
96
|
+
@logger = ::Logger.new(STDOUT)
|
97
|
+
@logger.error('Got error while trying to setting logger up, '\
|
98
|
+
"falling back to stdout #{e.inspect}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def init_log_level(level)
|
102
|
+
log_level = ::Logger.const_get(level)
|
103
|
+
@logger.level = log_level
|
104
|
+
Sqreen::PerformanceNotifications::Log.enable if level == 'DEBUG'
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_error_logger
|
108
|
+
@error_logger = ::Logger.new(STDERR)
|
109
|
+
enforce_log_format(@error_logger)
|
110
|
+
end
|
111
|
+
|
112
|
+
def enforce_log_format(logger)
|
113
|
+
logger.formatter = FormatterWithTid.new
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,39 @@
|
|
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/metrics/base'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Metric
|
8
|
+
# This perform an average aggregation
|
9
|
+
class Average < Base
|
10
|
+
# from class attr_accessor :aggregate
|
11
|
+
|
12
|
+
def update(_at, key, value)
|
13
|
+
super
|
14
|
+
@sums[key] ||= 0
|
15
|
+
@sums[key] += value
|
16
|
+
@counts[key] ||= 0
|
17
|
+
@counts[key] += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def new_sample(time)
|
23
|
+
super(time)
|
24
|
+
@sums = {}
|
25
|
+
@counts = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def finalize_sample(time)
|
29
|
+
super(time)
|
30
|
+
@sample[FINISH_KEY] = time
|
31
|
+
h = {}
|
32
|
+
@sums.each do |k, v|
|
33
|
+
h[k] = v.to_f / @counts[k]
|
34
|
+
end
|
35
|
+
@sample[OBSERVATION_KEY] = h
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,41 @@
|
|
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/exception'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Metric
|
8
|
+
OBSERVATION_KEY = 'observation'.freeze
|
9
|
+
START_KEY = 'start'.freeze
|
10
|
+
FINISH_KEY = 'finish'.freeze
|
11
|
+
# Base interface for a metric
|
12
|
+
class Base
|
13
|
+
# Update the current metric with a new observation
|
14
|
+
# @param _at [Time] when was the observation made
|
15
|
+
# @param _key [String] which aggregation key was it made for
|
16
|
+
# @param _value [Object] The observation
|
17
|
+
def update(_at, _key, _value)
|
18
|
+
raise Sqreen::Exception, 'No current sample' unless @sample
|
19
|
+
end
|
20
|
+
|
21
|
+
# create a new empty sample and publish the last one
|
22
|
+
# @param time [Time] Time of start/finish
|
23
|
+
def next_sample(time)
|
24
|
+
finalize_sample(time) unless @sample.nil?
|
25
|
+
current_sample = @sample
|
26
|
+
new_sample(time)
|
27
|
+
current_sample
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def new_sample(time)
|
33
|
+
@sample = { OBSERVATION_KEY => {}, START_KEY => time }
|
34
|
+
end
|
35
|
+
|
36
|
+
def finalize_sample(time)
|
37
|
+
@sample[FINISH_KEY] = time
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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/metrics/base'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Metric
|
8
|
+
# This is an aggregated statistic definition
|
9
|
+
# This is a base class to collect metrics in a hash based structure
|
10
|
+
# that does not aggregate anything
|
11
|
+
class Collect < Base
|
12
|
+
# from class attr_accessor :aggregate
|
13
|
+
|
14
|
+
def update(_at, key, value)
|
15
|
+
super
|
16
|
+
s = @sample[OBSERVATION_KEY]
|
17
|
+
s[key] ||= []
|
18
|
+
s[key] << value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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/metrics/base'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Metric
|
8
|
+
# This perform a sum aggregation
|
9
|
+
class Sum < Base
|
10
|
+
# from class attr_accessor :aggregate
|
11
|
+
|
12
|
+
def update(_at, key, value)
|
13
|
+
super
|
14
|
+
s = @sample[OBSERVATION_KEY]
|
15
|
+
s[key] ||= 0
|
16
|
+
s[key] += value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,94 @@
|
|
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/exception'
|
5
|
+
require 'sqreen/metrics'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
# This store and register metrics
|
9
|
+
class MetricsStore
|
10
|
+
# When a metric is not yet created
|
11
|
+
class UnregisteredMetric < Sqreen::Exception
|
12
|
+
end
|
13
|
+
# When the metric is unknown
|
14
|
+
class UnknownMetric < Sqreen::Exception
|
15
|
+
end
|
16
|
+
# When this name as already been declared with another kind
|
17
|
+
class AlreadyRegisteredMetric < Sqreen::Exception
|
18
|
+
end
|
19
|
+
|
20
|
+
NAME_KEY = 'name'.freeze
|
21
|
+
KIND_KEY = 'kind'.freeze
|
22
|
+
PERIOD_KEY = 'period'.freeze
|
23
|
+
|
24
|
+
# Currently ready samples
|
25
|
+
attr_reader :store
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@store = []
|
29
|
+
@metrics = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Definition contains a name,period and aggregate at least
|
33
|
+
# @param definition [Hash] a metric definition
|
34
|
+
# @param klass [Object] Override metric class (used in testing)
|
35
|
+
def create_metric(definition, mklass = nil)
|
36
|
+
name = definition[NAME_KEY]
|
37
|
+
kind = definition[KIND_KEY]
|
38
|
+
klass = valid_metric(kind, name)
|
39
|
+
metric = mklass || klass.new
|
40
|
+
@metrics[name] = [
|
41
|
+
metric,
|
42
|
+
definition[PERIOD_KEY],
|
43
|
+
nil # Start
|
44
|
+
]
|
45
|
+
metric
|
46
|
+
end
|
47
|
+
|
48
|
+
def update(name, at, key, value)
|
49
|
+
at = at.utc
|
50
|
+
metric, period, start = @metrics[name]
|
51
|
+
raise UnregisteredMetric, "Unknown metric #{name}" unless metric
|
52
|
+
next_sample(name, at) if start.nil? || start + period < at
|
53
|
+
metric.update(at, key, value)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Drains every metrics and returns the store content
|
57
|
+
# @params at [Time] when is the store emptied
|
58
|
+
def publish(at = Time.now.utc)
|
59
|
+
@metrics.each_key do |name|
|
60
|
+
next_sample(name, at)
|
61
|
+
end
|
62
|
+
out = @store
|
63
|
+
@store = []
|
64
|
+
out
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def next_sample(name, at)
|
70
|
+
metric = @metrics[name][0]
|
71
|
+
r = metric.next_sample(at)
|
72
|
+
@metrics[name][2] = at
|
73
|
+
if r
|
74
|
+
r[NAME_KEY] = name
|
75
|
+
obs = r[Metric::OBSERVATION_KEY]
|
76
|
+
@store << r if obs && (!obs.respond_to?(:empty?) || !obs.empty?)
|
77
|
+
end
|
78
|
+
r
|
79
|
+
end
|
80
|
+
|
81
|
+
def valid_metric(kind, name)
|
82
|
+
unless Sqreen::Metric.const_defined?(kind)
|
83
|
+
raise UnknownMetric, "No such #{kind} metric"
|
84
|
+
end
|
85
|
+
klass = Sqreen::Metric.const_get(kind)
|
86
|
+
metric = @metrics[name] && @metrics[name][0]
|
87
|
+
if metric && metric.class != klass
|
88
|
+
msg = "Was a #{metric.class.name} not a #{klass.name} "
|
89
|
+
raise AlreadyRegisteredMetric, msg
|
90
|
+
end
|
91
|
+
klass
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,98 @@
|
|
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/parsers/sql_tokenizer'
|
5
|
+
require 'sqreen/exception'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
module Parsers
|
9
|
+
# SELECT * FROM contacts WHERE name = #{name}
|
10
|
+
# name = 'a' 'b'
|
11
|
+
|
12
|
+
class SQLAtom
|
13
|
+
attr_reader :val, :type, :delim
|
14
|
+
def initialize(val, typ = :unkown, delim = nil, token_type = nil)
|
15
|
+
@val = val
|
16
|
+
@type = typ
|
17
|
+
@token_type = token_type
|
18
|
+
@delim = delim
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
return false if other.nil?
|
23
|
+
@val == other.val &&
|
24
|
+
@type == other.type &&
|
25
|
+
@delim == other.delim
|
26
|
+
end
|
27
|
+
|
28
|
+
def is_literal?
|
29
|
+
[:literal_string, :literal_number].include? @type
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
res = ":#{@type || @token_type}: #{@val}"
|
34
|
+
res += " (#{@delim})" if delim
|
35
|
+
res
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class SQL
|
40
|
+
attr_reader :atoms, :tokenizer
|
41
|
+
|
42
|
+
def initialize(db_type, db_infos)
|
43
|
+
klass = case db_type
|
44
|
+
when :mysql
|
45
|
+
MySQLTokenizer
|
46
|
+
when :sqlite
|
47
|
+
SQLiteTokenizer
|
48
|
+
else
|
49
|
+
Sqreen.log.warn format('warning: db_type %s not found, falling back to default SQL type', db_type)
|
50
|
+
SQLTokenizer
|
51
|
+
end
|
52
|
+
@tokenizer = klass.new(db_type, db_infos)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
res = "Parsed: #{@req}\n"
|
57
|
+
nb = 0
|
58
|
+
@atoms.each do |atom|
|
59
|
+
res << "\t" + nb.to_s + ': ' + atom.to_s + "\n"
|
60
|
+
nb += 1
|
61
|
+
end
|
62
|
+
res
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse(req)
|
66
|
+
@req = req
|
67
|
+
@atoms = []
|
68
|
+
@tokenizer.tokenize(req)
|
69
|
+
@tokenizer.each_token do |token, meta_type|
|
70
|
+
value = token[1]
|
71
|
+
type = token[0]
|
72
|
+
delim = case type
|
73
|
+
when :SINGLE_QUOTED_STRING
|
74
|
+
"'"
|
75
|
+
when :DOUBLE_QUOTED_STRING
|
76
|
+
'"'
|
77
|
+
end
|
78
|
+
atom = SQLAtom.new(value, meta_type, delim, type)
|
79
|
+
@atoms << atom
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_atoms(ary, type = :unknown)
|
84
|
+
ary.map { |frag| to_atom(frag, nil, type) }
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_atom(_frag, _type = :unknown, _delim = nil)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if $0 == __FILE__
|
94
|
+
s = Sqreen::Parsers::SQL.new :mysql, {}
|
95
|
+
s.parse(ARGV[0])
|
96
|
+
puts s
|
97
|
+
|
98
|
+
end
|