sqreen 0.7.01461158029-java

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,29 @@
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/rules_callbacks/regexp_rule'
5
+ require 'sqreen/rules_callbacks/matcher_rule'
6
+
7
+ require 'sqreen/rules_callbacks/record_request_context'
8
+ require 'sqreen/rules_callbacks/rails_parameters'
9
+
10
+ require 'sqreen/rules_callbacks/headers_insert'
11
+
12
+ require 'sqreen/rules_callbacks/inspect_rule'
13
+
14
+ require 'sqreen/rules_callbacks/shell'
15
+ require 'sqreen/rules_callbacks/system_shell'
16
+ require 'sqreen/rules_callbacks/shell_env'
17
+
18
+ require 'sqreen/rules_callbacks/sql'
19
+
20
+ require 'sqreen/rules_callbacks/url_matches'
21
+ require 'sqreen/rules_callbacks/user_agent_matches'
22
+ require 'sqreen/rules_callbacks/crawler_user_agent_matches'
23
+
24
+ require 'sqreen/rules_callbacks/reflected_xss'
25
+ require 'sqreen/rules_callbacks/execjs'
26
+
27
+ require 'sqreen/rules_callbacks/binding_accessor_metrics'
28
+ require 'sqreen/rules_callbacks/count_http_codes'
29
+ require 'sqreen/rules_callbacks/crawler_user_agent_matches_metrics'
@@ -0,0 +1,79 @@
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/rule_callback'
5
+ require 'sqreen/binding_accessor'
6
+ require 'sqreen/events/remote_exception'
7
+
8
+ module Sqreen
9
+ module Rules
10
+ # Publish metrics about data taken from the binding accessor
11
+ class BindingAccessorMetrics < RuleCB
12
+ # Exception thrown when no expression are present
13
+ class NoExpressions < Sqreen::Exception
14
+ def initialize(expr)
15
+ super("No valid expressions defined in #{expr.inspect}")
16
+ end
17
+ end
18
+
19
+ def initialize(klass, method, rule_hash)
20
+ super(klass, method, rule_hash)
21
+ @expr = {}
22
+ build_expressions(rule_hash[Attrs::CALLBACKS])
23
+ end
24
+
25
+ PRE_CB = 'pre'.freeze
26
+ POST_CB = 'post'.freeze
27
+ FAILING_CB = 'failing'.freeze
28
+
29
+ def pre?
30
+ @expr[PRE_CB]
31
+ end
32
+
33
+ def post?
34
+ @expr[POST_CB]
35
+ end
36
+
37
+ def failing?
38
+ @expr[FAILING_CB]
39
+ end
40
+
41
+ def pre(inst, *args, &_block)
42
+ return unless pre?
43
+
44
+ add_metrics(PRE_CB, inst, args)
45
+ end
46
+
47
+ def post(rv, inst, *args, &_block)
48
+ return unless post?
49
+
50
+ add_metrics(POST_CB, inst, args, rv)
51
+ end
52
+
53
+ def failing(exception, inst, *args, &_block)
54
+ return unless failing?
55
+
56
+ add_metrics(FAILING_CB, inst, args, exception)
57
+ end
58
+
59
+ protected
60
+
61
+ def add_metrics(name, inst, args, rv = nil)
62
+ category, key, value, = @expr[name].map do |accessor|
63
+ accessor.resolve(binding, framework, inst, args, @data, rv)
64
+ end
65
+ record_observation(category, key, value)
66
+ nil
67
+ end
68
+
69
+ def build_expressions(callbacks)
70
+ raise NoExpressions, callbacks if callbacks.nil? || callbacks.empty?
71
+ [PRE_CB, POST_CB, FAILING_CB].each do |c|
72
+ next if callbacks[c].nil? || callbacks[c].size < 3
73
+ @expr[c] = callbacks[c].map { |req| BindingAccessor.new(req, true) }
74
+ end
75
+ raise NoExpressions, callbacks if @expr.empty?
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,18 @@
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/rule_callback'
5
+
6
+ module Sqreen
7
+ module Rules
8
+ # Save request context for handling further down
9
+ class CountHTTPCodes < RuleCB
10
+ METRIC_CATEGORY = 'http_code'.freeze
11
+ def post(rv, _inst, *_args, &_block)
12
+ return unless rv.is_a?(Array) && !rv.empty?
13
+ record_observation(METRIC_CATEGORY, rv[0], 1)
14
+ nil
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
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/rules_callbacks/matcher_rule'
5
+ require 'sqreen/frameworks'
6
+
7
+ module Sqreen
8
+ module Rules
9
+ # FIXME: Factor with UserAgentMatchesCB
10
+ # Look for crawlers
11
+ class CrawlerUserAgentMatchesCB < MatcherRuleCB
12
+ def pre(_inst, *_args, &_block)
13
+ ua = framework.client_user_agent
14
+ return unless ua
15
+ found = match(ua)
16
+ return unless found
17
+ Sqreen.log.debug { "Found UA #{ua} - found: #{found}" }
18
+ infos = { :found => found }
19
+ record_event(infos)
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
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/rules_callbacks/matcher_rule'
5
+ require 'sqreen/frameworks'
6
+
7
+ module Sqreen
8
+ module Rules
9
+ # Look for crawlers and post them in metrics
10
+ class CrawlerUserAgentMatchesMetricsCB < MatcherRuleCB
11
+ CRAWLER_CATEGORY = 'crawler'.freeze
12
+
13
+ def pre(_inst, *_args, &_block)
14
+ ua = framework.client_user_agent
15
+ return unless ua
16
+ ua = ua.freeze
17
+ found = match(ua)
18
+ return unless found
19
+ Sqreen.log.debug { "Found UA #{ua} - found: #{found}" }
20
+ record_observation(CRAWLER_CATEGORY, ua, 1)
21
+ nil
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,136 @@
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
+ if defined?(::JRUBY_VERSION)
5
+ require 'rhino'
6
+ else
7
+ require 'therubyracer'
8
+ end
9
+
10
+ require 'execjs'
11
+
12
+ require 'sqreen/rule_callback'
13
+ require 'sqreen/binding_accessor'
14
+ require 'sqreen/events/remote_exception'
15
+
16
+ module Sqreen
17
+ module Rules
18
+ # Exec js callbacks
19
+ class ExecJSCB < RuleCB
20
+ def initialize(klass, method, rule_hash)
21
+ super(klass, method, rule_hash)
22
+ callbacks = @rule['callbacks']
23
+
24
+ if callbacks['pre'].nil? &&
25
+ callbacks['post'].nil? &&
26
+ callbacks['failing'].nil?
27
+ raise(Sqreen::Exception, 'no JS CB provided')
28
+ end
29
+
30
+ build_runnable(callbacks)
31
+ @compiled = ExecJS.compile(@source)
32
+ end
33
+
34
+ def pre?
35
+ @js_pre
36
+ end
37
+
38
+ def post?
39
+ @js_post
40
+ end
41
+
42
+ def failing?
43
+ @js_failing
44
+ end
45
+
46
+ def pre(inst, *args, &_block)
47
+ return unless pre?
48
+
49
+ call_callback('pre', inst, args)
50
+ end
51
+
52
+ def post(rv, inst, *args, &_block)
53
+ return unless post?
54
+
55
+ call_callback('post', inst, args, rv)
56
+ end
57
+
58
+ def failing(rv, inst, *args, &_block)
59
+ return unless failing?
60
+
61
+ call_callback('failing', inst, args, rv)
62
+ end
63
+
64
+ protected
65
+
66
+ def record_and_continue?(ret)
67
+ case ret
68
+ when NilClass
69
+ return false
70
+ when Hash
71
+ ret.keys.each do |k|
72
+ ret[(begin
73
+ k.to_sym
74
+ rescue
75
+ k
76
+ end)] = ret[k] end
77
+ record_event(ret[:record]) unless ret[:record].nil?
78
+ return !ret[:call].nil?
79
+ else
80
+ raise Sqreen::Exception, "Invalid return type #{ret.inspect}"
81
+ end
82
+ end
83
+
84
+ def call_callback(name, inst, args, rv = nil)
85
+ ret = nil
86
+ args_override = nil
87
+ arguments = nil
88
+ loop do
89
+ arguments = (args_override || @argument_requirements[name]).map do |accessor|
90
+ accessor.resolve(binding, framework, inst, args, @data, rv)
91
+ end
92
+ Sqreen.log.debug { [name, arguments].inspect }
93
+ ret = @compiled.call("callbacks.#{name}", *arguments)
94
+ return ret unless record_and_continue?(ret)
95
+ name = ret[:call]
96
+ rv = ret[:data]
97
+ args_override = ret[:args]
98
+ args_override = build_accessor(args_override) if args_override
99
+ end
100
+ rescue => e
101
+ Sqreen.log.error "we catch a JScb exception: #{e.inspect}"
102
+ Sqreen.log.error e.backtrace
103
+ record_exception(e, :cb => name, :args => arguments)
104
+ nil
105
+ end
106
+
107
+ def build_accessor(reqs)
108
+ reqs.map do |req|
109
+ BindingAccessor.new(req, true)
110
+ end
111
+ end
112
+
113
+ def build_runnable(callbacks)
114
+ @argument_requirements = {}
115
+ @source = 'var callbacks = {'
116
+ @js_pre = !callbacks['pre'].nil?
117
+ @js_post = !callbacks['post'].nil?
118
+ @js_failing = !callbacks['failing'].nil?
119
+ callbacks.each do |name, args_or_func|
120
+ @source << name
121
+ @source << ': '
122
+ if args_or_func.is_a?(Array)
123
+ @source << args_or_func.pop
124
+ @argument_requirements[name] = build_accessor(args_or_func)
125
+ else
126
+ @source << args_or_func
127
+ @argument_requirements[name] = []
128
+ end
129
+ @source << ",\n"
130
+ end
131
+ @source << "\n"
132
+ @source << '}'
133
+ end
134
+ end
135
+ end
136
+ 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/rule_callback'
5
+
6
+ module Sqreen
7
+ module Rules
8
+ SQREEN_HEADER_NAME = 'X-Protected-By'.freeze
9
+ SQREEN_HEADER_VALUE = 'Sqreen'.freeze
10
+
11
+ # Display sqreen presence
12
+ class HeadersInsertCB < RuleCB
13
+ def post(rv, _inst, *_args, &_block)
14
+ return unless rv && rv.respond_to?(:[]) && rv[1].is_a?(Hash)
15
+ rv[1][SQREEN_HEADER_NAME] = SQREEN_HEADER_VALUE
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ 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/rule_callback'
5
+
6
+ module Sqreen
7
+ module Rules
8
+ class InspectRuleCB < RuleCB
9
+ def pre(_inst, *args, &_block)
10
+ Sqreen.log.debug { "<< #{@klass} #{@method} #{Thread.current}" }
11
+ Sqreen.log.debug { args.join ' ' }
12
+ end
13
+
14
+ def post(_rv, _inst, *_args, &_block)
15
+ Sqreen.log.debug { ">> #{@klass} #{@method} #{Thread.current}" }
16
+ byebug if defined? byebug and @data.is_a?(Hash) and @data[:break] == 1
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,103 @@
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/rule_callback'
5
+
6
+ module Sqreen
7
+ module Rules
8
+ # A configurable matcher rule
9
+ class MatcherRuleCB < RuleCB
10
+ def initialize(*args)
11
+ super(*args)
12
+ prepare
13
+ end
14
+
15
+ def self.prepare_re_pattern(value, options, case_sensitive)
16
+ res = 0
17
+ res |= Regexp::MULTILINE if options.include?('multiline')
18
+ res |= Regexp::IGNORECASE unless case_sensitive
19
+ Regexp.compile(value, res)
20
+ end
21
+
22
+ def prepare
23
+ @string = {}
24
+ @regex_patterns = []
25
+
26
+ patterns = @data['values']
27
+ if patterns.nil?
28
+ msg = "no key 'values' in data (had #{@data.keys})"
29
+ raise Sqreen::Exception, msg
30
+ end
31
+
32
+ @funs = {
33
+ 'anywhere' => lambda { |value, str| str.include?(value) },
34
+ 'starts_with' => lambda { |value, str| str.start_with?(value) },
35
+ 'ends_with' => lambda { |value, str| str.end_with?(value) },
36
+ 'equals' => lambda { |value, str| str == value },
37
+ }
38
+
39
+ patterns.each do |entry|
40
+ next unless entry
41
+ type = entry['type']
42
+ val = entry['value']
43
+ opts = entry['options']
44
+ opt = (opts && opts.first && opts.first != '') ? opts.first : 'anywhere'
45
+ case_sensitive = entry['case_sensitive'] || false
46
+ case type
47
+ when 'string'
48
+ if case_sensitive
49
+ case_type = :cs
50
+ else
51
+ case_type = :ci
52
+ val = val.downcase
53
+ end
54
+
55
+ unless @funs.keys.include?(opt)
56
+ Sqreen.log.debug { "Error: unknown string option '#{opt}' " }
57
+ next
58
+ end
59
+ @string[opt] = { :ci => [], :cs => [] } unless @string.key?(opt)
60
+ @string[opt][case_type] << val
61
+
62
+ when 'regex'
63
+ pattern = MatcherRuleCB.prepare_re_pattern(val, opt, case_sensitive)
64
+ next unless pattern
65
+ @regex_patterns << pattern
66
+ end
67
+ end
68
+
69
+ if [@regex_patterns, @string].map { |s| s == 0 }.all?
70
+ msg = "no key 'regex' nor 'match' in data (had #{@data.keys})"
71
+ raise Sqreen::Exception, msg
72
+ end
73
+ end
74
+
75
+ def match(str)
76
+ return if str.nil? || str.empty?
77
+ istr = str.downcase
78
+
79
+ @string.each do |type, cases|
80
+ fun = @funs[type]
81
+ if fun.nil?
82
+ Sqreen.log.debug { "no matching function found for type #{type}" }
83
+ end
84
+ cases.each do |case_type, patterns|
85
+ input_str = if case_type == :ci
86
+ istr
87
+ else
88
+ str
89
+ end
90
+ patterns.each do |pat|
91
+ return pat if fun.call(pat, input_str)
92
+ end
93
+ end
94
+ end
95
+
96
+ @regex_patterns.each do |p|
97
+ return p if p.match(str)
98
+ end
99
+ nil
100
+ end
101
+ end
102
+ end
103
+ end