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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +39 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/rbirch.xml +9 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/snoopit.iml +233 -0
- data/.idea/vcs.xml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +411 -0
- data/Rakefile +1 -0
- data/bin/snoopit +173 -0
- data/lib/snoopit.rb +22 -0
- data/lib/snoopit/detected.rb +50 -0
- data/lib/snoopit/file_info.rb +104 -0
- data/lib/snoopit/file_tracker.rb +83 -0
- data/lib/snoopit/logger.rb +30 -0
- data/lib/snoopit/notification_manager.rb +123 -0
- data/lib/snoopit/notifier.rb +25 -0
- data/lib/snoopit/notifiers/email.rb +61 -0
- data/lib/snoopit/notifiers/http.rb +85 -0
- data/lib/snoopit/notifiers/https.rb +21 -0
- data/lib/snoopit/notifiers/stomp.rb +59 -0
- data/lib/snoopit/register.rb +69 -0
- data/lib/snoopit/sniffer.rb +51 -0
- data/lib/snoopit/snooper.rb +149 -0
- data/lib/snoopit/snoopy.rb +67 -0
- data/lib/snoopit/version.rb +3 -0
- data/snoopit.gemspec +27 -0
- data/spec/bin/snoopit_spec.rb +258 -0
- data/spec/file_info_spec.rb +131 -0
- data/spec/file_tracker_spec.rb +172 -0
- data/spec/notification_manager_spec.rb +103 -0
- data/spec/notifiers/email_spec.rb +36 -0
- data/spec/notifiers/http_spec.rb +37 -0
- data/spec/notifiers/https_spec.rb +38 -0
- data/spec/notifiers/stomp_spec.rb +34 -0
- data/spec/register_spec.rb +105 -0
- data/spec/snooper_spec.rb +538 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/log/snoop_log.test +593 -0
- data/spec/support/log/snoop_log_2.test +593 -0
- data/spec/support/multiple_snoopies.json +82 -0
- data/spec/support/regexp_tester.rb +10 -0
- data/spec/support/snoopies.json +93 -0
- data/spec/support/snoopies_notifiers.json +66 -0
- data/spec/support/test_notifier.rb +18 -0
- data/spec/support/test_notifier_load.rb +18 -0
- data/support/snoopies.json +110 -0
- 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
|