snoopit 0.0.1

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +39 -0
  4. data/.idea/.name +1 -0
  5. data/.idea/.rakeTasks +7 -0
  6. data/.idea/dictionaries/rbirch.xml +9 -0
  7. data/.idea/encodings.xml +5 -0
  8. data/.idea/misc.xml +5 -0
  9. data/.idea/modules.xml +9 -0
  10. data/.idea/scopes/scope_settings.xml +5 -0
  11. data/.idea/snoopit.iml +233 -0
  12. data/.idea/vcs.xml +7 -0
  13. data/.rspec +2 -0
  14. data/.travis.yml +7 -0
  15. data/Gemfile +15 -0
  16. data/LICENSE.txt +22 -0
  17. data/README.md +411 -0
  18. data/Rakefile +1 -0
  19. data/bin/snoopit +173 -0
  20. data/lib/snoopit.rb +22 -0
  21. data/lib/snoopit/detected.rb +50 -0
  22. data/lib/snoopit/file_info.rb +104 -0
  23. data/lib/snoopit/file_tracker.rb +83 -0
  24. data/lib/snoopit/logger.rb +30 -0
  25. data/lib/snoopit/notification_manager.rb +123 -0
  26. data/lib/snoopit/notifier.rb +25 -0
  27. data/lib/snoopit/notifiers/email.rb +61 -0
  28. data/lib/snoopit/notifiers/http.rb +85 -0
  29. data/lib/snoopit/notifiers/https.rb +21 -0
  30. data/lib/snoopit/notifiers/stomp.rb +59 -0
  31. data/lib/snoopit/register.rb +69 -0
  32. data/lib/snoopit/sniffer.rb +51 -0
  33. data/lib/snoopit/snooper.rb +149 -0
  34. data/lib/snoopit/snoopy.rb +67 -0
  35. data/lib/snoopit/version.rb +3 -0
  36. data/snoopit.gemspec +27 -0
  37. data/spec/bin/snoopit_spec.rb +258 -0
  38. data/spec/file_info_spec.rb +131 -0
  39. data/spec/file_tracker_spec.rb +172 -0
  40. data/spec/notification_manager_spec.rb +103 -0
  41. data/spec/notifiers/email_spec.rb +36 -0
  42. data/spec/notifiers/http_spec.rb +37 -0
  43. data/spec/notifiers/https_spec.rb +38 -0
  44. data/spec/notifiers/stomp_spec.rb +34 -0
  45. data/spec/register_spec.rb +105 -0
  46. data/spec/snooper_spec.rb +538 -0
  47. data/spec/spec_helper.rb +24 -0
  48. data/spec/support/log/snoop_log.test +593 -0
  49. data/spec/support/log/snoop_log_2.test +593 -0
  50. data/spec/support/multiple_snoopies.json +82 -0
  51. data/spec/support/regexp_tester.rb +10 -0
  52. data/spec/support/snoopies.json +93 -0
  53. data/spec/support/snoopies_notifiers.json +66 -0
  54. data/spec/support/test_notifier.rb +18 -0
  55. data/spec/support/test_notifier_load.rb +18 -0
  56. data/support/snoopies.json +110 -0
  57. metadata +190 -0
@@ -0,0 +1,123 @@
1
+ module Snoopit
2
+
3
+ class NotificationManager
4
+
5
+ attr :active, :config
6
+
7
+ def initialize(config=nil)
8
+ @active = {}
9
+ @config = config
10
+ load_default_notifiers unless @config.nil?
11
+ end
12
+
13
+ def load_notifier_config(config)
14
+ @config = config
15
+ load_default_notifiers
16
+ load = @config['load']
17
+ load_files(load) unless load.nil?
18
+ end
19
+
20
+ def register(notifier)
21
+ raise NameError.new "Notifier missing valid name: #{notifier.inspect}" if notifier.name.nil?
22
+ Snoopit.logger.debug "Registering notifier #{notifier.name}"
23
+ @active[notifier.name] = notifier
24
+ end
25
+
26
+ def unregister(notifier)
27
+ self.unregister_by_name notifier.name
28
+ end
29
+
30
+ def unregister_by_name(notifier_name)
31
+ @active.delete notifier_name
32
+ end
33
+
34
+ def get_notifier(name)
35
+ @active[name]
36
+ end
37
+
38
+ def notify(snoopies)
39
+ snoopies.each do |snoopy|
40
+ snoopy.sniffers.each do |sniffer|
41
+ sniffer_notify(sniffer)
42
+ end
43
+ end
44
+ end
45
+
46
+ def sniffer_notify(sniffer)
47
+ messages = get_sniffed_messages sniffer
48
+ sniffer.notifiers.each do |key, value|
49
+ n = @active[key]
50
+ next if n.nil?
51
+ messages.each do |message|
52
+ n.notify(message, value)
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def load_default_notifiers
60
+ path = File.expand_path('../notifiers', __FILE__)
61
+ Dir.entries(path).each do |file|
62
+ next if File.directory? file
63
+ file_require "#{path}/#{file}"
64
+ end
65
+ create_default_notifiers
66
+ end
67
+
68
+ def create_default_notifiers
69
+ Snoopit::Notifiers.constants.select do |c|
70
+ if Class === Snoopit::Notifiers.const_get(c)
71
+ config = @config[c.to_s.downcase]
72
+ unless config.nil?
73
+ o = Snoopit::Notifiers.const_get(c).new config
74
+ @active[o.name] = o
75
+ else
76
+ Snoopit.logger.debug "Notifier #{c.to_s.downcase} not loaded due to no configuration specified"
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def file_require(file)
83
+ Snoopit.logger.debug "Requiring notifier file: #{file}"
84
+ require file
85
+ end
86
+
87
+ def load_files(load)
88
+ load.keys.each do |key|
89
+ entry = load[key]
90
+ next if entry['file'].nil?
91
+ next if entry['class'].nil?
92
+ file_require entry['file']
93
+ create_notifier entry['class'], entry['config']
94
+ end
95
+ end
96
+
97
+ def create_notifier(klass, notifier_config=nil)
98
+ o = Object.const_get(klass).new notifier_config
99
+ @active[o.name] = o
100
+ end
101
+
102
+ def get_sniffed_messages(sniffer)
103
+ messages = []
104
+ sniffer.sniffed.each do |sniffed|
105
+ messages << build_message(sniffed)
106
+ end
107
+ messages
108
+ end
109
+
110
+ def build_message(sniffed)
111
+ {
112
+ comment: sniffed.comment,
113
+ file: sniffed.file,
114
+ before: sniffed.before.register.reverse,
115
+ match: sniffed.match,
116
+ match_line_no: sniffed.line_no,
117
+ after: sniffed.after.register.reverse
118
+ }
119
+ end
120
+
121
+ end
122
+
123
+ end
@@ -0,0 +1,25 @@
1
+ module Snoopit
2
+
3
+ # @abstract Subclass
4
+ # Override {#notify} method
5
+ class Notifier
6
+
7
+ attr_accessor :name, :configuration, :klass
8
+
9
+ # The name is used by the Snooper to identify type of notifier to create
10
+ # @param name [String] name of notifier if the name is nil the class name is used
11
+ # The empty constructor is used to initialize the class when loaded dynamically
12
+ # After loaded dynamically the method set_config is called to set the configuration
13
+ def initialize(config=nil, name=nil, klass=nil)
14
+ @name = name.nil? ? self.class.name : name
15
+ @klass = klass.nil? ? self.class.name : klass
16
+ @config = config
17
+ end
18
+
19
+ def notify(found, notify_params)
20
+ raise NotImplementedError.new 'Notifier#notify'
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,61 @@
1
+ require 'net/smtp'
2
+ require 'date'
3
+
4
+ module Snoopit
5
+ module Notifiers
6
+
7
+ class Email < Snoopit::Notifier
8
+
9
+ attr :smtp_server, :port, :tls
10
+ # The name 'email' is used by the Snooper to identify type of notifier to create
11
+ # The empty constructor is used to initialize the class when loaded dynamically
12
+ # After loaded dynamically the method set_config is called by the base class to set the configuration
13
+ def initialize(config=nil)
14
+ super config, 'email'
15
+ @smtp_server = @config['smtp-server']
16
+ @port = @config['port']
17
+ @tls = @config['tls']
18
+ @user = @config['user']
19
+ @password = @config['password']
20
+ end
21
+
22
+ def notify(found, notifier_params)
23
+ send found, notifier_params
24
+ end
25
+
26
+ def send(msg, notifier_params)
27
+ begin
28
+ auth = notifier_params['authentication'].nil? ? :login : notifier_params['authentication'].to_sym
29
+ from = notifier_params['from'].nil? ? 'snoopit@snooper.notifier.com' : notifier_params['from']
30
+ formatted = build_msg from, notifier_params['to'], msg
31
+ smtp = Net::SMTP.new @smtp_server, @port
32
+ helo = @user.split('@')[1] || 'localhost'
33
+ smtp.enable_starttls_auto
34
+ smtp.start helo, @user, @password, auth do |smtp|
35
+ smtp.send_message(formatted, from, notifier_params['to'])
36
+ end
37
+ rescue => e
38
+ Snoopit.logger.warn e.message
39
+ Snoopit.logger.warn e.backtrace
40
+ end
41
+ end
42
+
43
+ def build_msg(from, to, msg)
44
+ msg = <<-MSG
45
+ From: #{from}
46
+ To: #{to.kind_of?(Array) ? to.join(', ') : to }
47
+ Content-Type: text/plain
48
+ Subject: #{msg[:comment]}
49
+ Date: #{DateTime.now.rfc822}
50
+
51
+ Snooper event on line: #{msg[:match_line_no]} in file: #{msg[:file]}
52
+
53
+ #{msg[:before].join('')}#{msg[:match]}#{msg[:after].join('')}
54
+ MSG
55
+ msg
56
+ end
57
+
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,85 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'awesome_print'
5
+
6
+ module Snoopit
7
+ module Notifiers
8
+
9
+ class Http < Snoopit::Notifier
10
+
11
+ # The name 'http' is used by the Snooper to identify type of notifier to create
12
+ # The empty constructor is used to initialize the class when loaded dynamically
13
+ # After loaded dynamically the method set_config is called by the base class to set the configuration
14
+ def initialize(config=nil)
15
+ super config, 'http'
16
+ @api_key = config['api-key']
17
+ end
18
+
19
+ def notify(found, notify_params)
20
+ raise ArgumentError.new 'URL parameter must be set' if notify_params['url'].nil?
21
+ post_it found, notify_params, URI(notify_params['url'])
22
+ end
23
+
24
+ def post_it(found, notify_params, uri)
25
+ conn = get_conn uri
26
+ request = build_request uri, found, notify_params
27
+ post conn, request
28
+ end
29
+
30
+ def post(conn, request)
31
+ response = conn.request(request)
32
+ case response.code
33
+ when 200..299, 300..399
34
+ logger.debug 'Posted notification to: ' + response.uri.to_s
35
+ logger.debug.ap found
36
+ when 400..599
37
+ logger.warn 'Failed to send notification to uri: ' + response.uri.to_s
38
+ logger.warn 'Failed to send notification to uri: ' + response.message
39
+ end
40
+ rescue => e
41
+ Snoopit.logger.warn 'Failed to send notification to uri: ' + response.uri.to_s unless response.nil?
42
+ Snoopit.logger.warn 'Failed to send notification to uri: ' + e.message
43
+ Snoopit.logger.warn 'Failed to send notification to uri: ' + e.backtrace.join("\n")
44
+ ensure
45
+ conn.finish if conn.started?
46
+ end
47
+
48
+ def build_request(uri, found, notify_params)
49
+ request = Net::HTTP::Post.new(uri.path)
50
+ request.basic_auth notify_params['user'], notify_params['password'] unless notify_params['user'].nil?
51
+ request.body = found.to_json
52
+ set_headers request
53
+ request
54
+ end
55
+
56
+ def get_conn(uri)
57
+ case uri.scheme
58
+ when 'http'
59
+ set_http(uri)
60
+ when 'https'
61
+ set_https(uri)
62
+ end
63
+ end
64
+
65
+ def set_http(uri)
66
+ Net::HTTP.new uri.host, uri.port
67
+ end
68
+
69
+ def set_https(uri)
70
+ conn = Net::HTTP.new uri.host, uri.port
71
+ conn.use_ssl = true
72
+ conn.verify_mode = OpenSSL::SSL::VERIFY_PEER
73
+ conn
74
+ end
75
+
76
+ def set_headers(request)
77
+ request['Content-Type'] = 'application/json'
78
+ request['Authorization'] = 'Token token=' + @api_key unless @api_key.nil?
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Snoopit
3
+ module Notifiers
4
+
5
+ class Https < Snoopit::Notifier
6
+
7
+ # The name 'https' is used by the Snooper to identify type of notifier to create
8
+ # The empty constructor is used to initialize the class when loaded dynamically
9
+ # After loaded dynamically the method set_config is called by the base class to set the configuration
10
+ def initialize(config=nil)
11
+ super config, 'https'
12
+ @http = Snoopit::Notifiers::Http.new config
13
+ end
14
+
15
+ def notify(found, notify_params)
16
+ @http.notify(found, notify_params)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,59 @@
1
+ require 'stomp'
2
+ require 'awesome_print'
3
+
4
+ module Snoopit
5
+ module Notifiers
6
+
7
+ class Stomp < Snoopit::Notifier
8
+
9
+ # The name 'stomp' is used by the Snooper to identify type of notifier to create
10
+ # The empty constructor is used to initialize the class when loaded dynamically
11
+ # After loaded dynamically the method set_config is called by the base class to set the configuration
12
+ def initialize(config=nil)
13
+ super config, 'stomp'
14
+ @host = config['host'] || 'localhost'
15
+ @port = config['port'] || 61613
16
+ @login = config['login']
17
+ @passcode = config['passcode']
18
+ @headers = config['headers']
19
+ end
20
+
21
+ def get_connection
22
+ params = { hosts: [ get_connect_params ] }
23
+ set_connect_headers params
24
+ ::Stomp::Connection.new params
25
+ rescue => e
26
+ Snoopit.logger.warn 'Stomp failed to connect: ' + params.to_json
27
+ Snoopit.logger.warn 'Stomp failed to connect: ' + e.message
28
+ Snoopit.logger.warn 'Stomp failed to connect: ' + e.backtrace.join('\n')
29
+ nil
30
+ end
31
+
32
+ def get_connect_params
33
+ params = { host: @host, port: @port }
34
+ params[:login] = @login unless @login.nil?
35
+ params[:passcode] = @passcode unless @passcode.nil?
36
+ params
37
+ end
38
+
39
+ def set_connect_headers(params)
40
+ params[:connect_headers] = @headers unless @headers.nil?
41
+ end
42
+
43
+ def notify(found, notify_params)
44
+ conn = get_connection
45
+ send_message(conn, found, notify_params) unless conn.nil?
46
+ rescue => e
47
+ Snoopit.logger.warn e.message
48
+ Snoopit.logger.warn e.backtrace.join('\n')
49
+ ensure
50
+ conn.disconnect unless conn.nil?
51
+ end
52
+
53
+ def send_message(conn, found, notify_params)
54
+ conn.publish notify_params['queue'], found.to_json, notify_params['headers']
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,69 @@
1
+ module Snoopit
2
+
3
+ # Behaves a bit like a CPU register
4
+ class Register
5
+
6
+ include Enumerable
7
+
8
+ attr :size, :register
9
+
10
+ # index always points to where the next element will be added
11
+ def initialize(size=10, data=nil)
12
+ if data.nil?
13
+ @register = Array.new size
14
+ else
15
+ @register = Array.new size
16
+ cloned = data.clone
17
+ [0...size].each { |i| @register[i] = cloned[i] }
18
+ end
19
+ end
20
+
21
+ # pushes one object to the front of the array and pops one off the end and returns is
22
+ def unshift(object)
23
+ return if @register.size == 0
24
+ @register.unshift object
25
+ @register.pop unless @register.size == 1
26
+ end
27
+
28
+ # pushes one object on to the end of the array and pops one off the front and returns is
29
+ def shift(object)
30
+ return if @register.size == 0
31
+ @register.push object
32
+ @register.slice! 0 unless @register.size == 1
33
+ end
34
+
35
+ alias_method :push_front, :unshift
36
+ alias_method :push_back, :shift
37
+
38
+ def [](index)
39
+ @register[index]
40
+ end
41
+
42
+ def []=(index, value)
43
+ @register[index] = value
44
+ end
45
+
46
+ def size
47
+ @register.size
48
+ end
49
+
50
+ def length
51
+ @register.length
52
+ end
53
+
54
+ def as_json(options=nil)
55
+ @register
56
+ end
57
+
58
+ def to_json(*a)
59
+ as_json.to_json(*a)
60
+ end
61
+
62
+ def each
63
+ @register.each { |r| yield r }
64
+ end
65
+
66
+ end
67
+
68
+
69
+ end