snoopit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|