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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +22 -0
  3. data/README.md +77 -0
  4. data/Rakefile +40 -0
  5. data/lib/sqreen.rb +67 -0
  6. data/lib/sqreen/binding_accessor.rb +184 -0
  7. data/lib/sqreen/ca.crt +72 -0
  8. data/lib/sqreen/callback_tree.rb +78 -0
  9. data/lib/sqreen/callbacks.rb +120 -0
  10. data/lib/sqreen/capped_queue.rb +23 -0
  11. data/lib/sqreen/condition_evaluator.rb +169 -0
  12. data/lib/sqreen/conditionable.rb +50 -0
  13. data/lib/sqreen/configuration.rb +151 -0
  14. data/lib/sqreen/context.rb +22 -0
  15. data/lib/sqreen/deliveries/batch.rb +80 -0
  16. data/lib/sqreen/deliveries/simple.rb +36 -0
  17. data/lib/sqreen/detect.rb +14 -0
  18. data/lib/sqreen/detect/shell_injection.rb +61 -0
  19. data/lib/sqreen/detect/sql_injection.rb +115 -0
  20. data/lib/sqreen/event.rb +16 -0
  21. data/lib/sqreen/events/attack.rb +60 -0
  22. data/lib/sqreen/events/remote_exception.rb +53 -0
  23. data/lib/sqreen/exception.rb +31 -0
  24. data/lib/sqreen/frameworks.rb +40 -0
  25. data/lib/sqreen/frameworks/generic.rb +243 -0
  26. data/lib/sqreen/frameworks/rails.rb +155 -0
  27. data/lib/sqreen/frameworks/rails3.rb +36 -0
  28. data/lib/sqreen/frameworks/sinatra.rb +34 -0
  29. data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
  30. data/lib/sqreen/instrumentation.rb +504 -0
  31. data/lib/sqreen/log.rb +116 -0
  32. data/lib/sqreen/metrics.rb +6 -0
  33. data/lib/sqreen/metrics/average.rb +39 -0
  34. data/lib/sqreen/metrics/base.rb +41 -0
  35. data/lib/sqreen/metrics/collect.rb +22 -0
  36. data/lib/sqreen/metrics/sum.rb +20 -0
  37. data/lib/sqreen/metrics_store.rb +94 -0
  38. data/lib/sqreen/parsers/sql.rb +98 -0
  39. data/lib/sqreen/parsers/sql_tokenizer.rb +266 -0
  40. data/lib/sqreen/parsers/unix.rb +110 -0
  41. data/lib/sqreen/payload_creator.rb +132 -0
  42. data/lib/sqreen/performance_notifications.rb +86 -0
  43. data/lib/sqreen/performance_notifications/log.rb +36 -0
  44. data/lib/sqreen/performance_notifications/metrics.rb +36 -0
  45. data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
  46. data/lib/sqreen/remote_command.rb +82 -0
  47. data/lib/sqreen/rule_attributes.rb +25 -0
  48. data/lib/sqreen/rule_callback.rb +97 -0
  49. data/lib/sqreen/rules.rb +116 -0
  50. data/lib/sqreen/rules_callbacks.rb +29 -0
  51. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
  52. data/lib/sqreen/rules_callbacks/count_http_codes.rb +18 -0
  53. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
  54. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +25 -0
  55. data/lib/sqreen/rules_callbacks/execjs.rb +136 -0
  56. data/lib/sqreen/rules_callbacks/headers_insert.rb +20 -0
  57. data/lib/sqreen/rules_callbacks/inspect_rule.rb +20 -0
  58. data/lib/sqreen/rules_callbacks/matcher_rule.rb +103 -0
  59. data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
  60. data/lib/sqreen/rules_callbacks/record_request_context.rb +23 -0
  61. data/lib/sqreen/rules_callbacks/reflected_xss.rb +40 -0
  62. data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
  63. data/lib/sqreen/rules_callbacks/shell.rb +33 -0
  64. data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
  65. data/lib/sqreen/rules_callbacks/sql.rb +41 -0
  66. data/lib/sqreen/rules_callbacks/system_shell.rb +25 -0
  67. data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
  68. data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
  69. data/lib/sqreen/rules_signature.rb +142 -0
  70. data/lib/sqreen/runner.rb +312 -0
  71. data/lib/sqreen/runtime_infos.rb +127 -0
  72. data/lib/sqreen/session.rb +340 -0
  73. data/lib/sqreen/stats.rb +18 -0
  74. data/lib/sqreen/version.rb +6 -0
  75. 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,6 @@
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/collect'
5
+ require 'sqreen/metrics/average'
6
+ require 'sqreen/metrics/sum'
@@ -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