osa 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/lib/osa/scripts/dashboard_server.rb +9 -1
- data/lib/osa/scripts/scan_junk_folder.rb +14 -1
- data/lib/osa/util/db.rb +1 -1
- data/lib/osa/version.rb +1 -1
- data/lib/osa/views/index.erb +31 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3bc9c2b204260bd0cecd1650fdbfca0676c53e6e44c59aef8926caf9a5f0246
|
4
|
+
data.tar.gz: f32d6566bc6195847fc12fe680f8c22e6cbfe91f587bc7a35937efb1582eaccc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76462c72b811ecb6b2224725ec954aa3ff3c6d2fc99a4cc91ae66698d72de709c740683ce6c4690095853199c7ef67c836ddc0bf6c0d3c4eb33751ee0fec3058
|
7
|
+
data.tar.gz: f4e79f61caa68f2c1ecd646ffa3d127070addf334f827bb33066bfdefc1e44f8b2986b6983c120c94867fa907e65184fd679ef73ad0eaa6b1b34f49956d5393d
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
osa (0.2.
|
4
|
+
osa (0.2.2)
|
5
5
|
activerecord (~> 6.0)
|
6
6
|
faraday (~> 1.1)
|
7
7
|
mail (~> 2.7.1)
|
@@ -37,7 +37,7 @@ GEM
|
|
37
37
|
mail (2.7.1)
|
38
38
|
mini_mime (>= 0.1.1)
|
39
39
|
mini_mime (1.0.2)
|
40
|
-
minitest (5.14.
|
40
|
+
minitest (5.14.4)
|
41
41
|
multi_json (1.15.0)
|
42
42
|
multipart-post (2.1.1)
|
43
43
|
mustermann (1.1.1)
|
@@ -6,6 +6,7 @@ require 'osa/util/db'
|
|
6
6
|
class DashboardServer < Sinatra::Base
|
7
7
|
set :views, File.absolute_path(File.dirname(__FILE__) + '/../views')
|
8
8
|
set :port, ENV['SERVER_PORT'] || 8080
|
9
|
+
after { ActiveRecord::Base.connection.close }
|
9
10
|
|
10
11
|
get '/' do
|
11
12
|
erb :index
|
@@ -54,7 +55,11 @@ class DashboardServer < Sinatra::Base
|
|
54
55
|
end
|
55
56
|
|
56
57
|
get '/api/stats/spammers' do
|
57
|
-
spammers = OSA::Report.select('sender_domain as domain', 'COUNT(*) as count')
|
58
|
+
spammers = OSA::Report.select('sender_domain as domain', 'COUNT(*) as count')
|
59
|
+
unless params[:interval].blank?
|
60
|
+
spammers = spammers.where('received_at > ?', Time.now - params[:interval].to_i.days)
|
61
|
+
end
|
62
|
+
spammers = spammers.limit(50).order(count: :desc).group(:sender_domain)
|
58
63
|
json spammers
|
59
64
|
end
|
60
65
|
|
@@ -63,6 +68,9 @@ class DashboardServer < Sinatra::Base
|
|
63
68
|
unless params[:spammer].blank?
|
64
69
|
historical_data = historical_data.where(sender: params[:spammer]).or(OSA::Report.where(sender_domain: params[:spammer]))
|
65
70
|
end
|
71
|
+
unless params[:interval].blank?
|
72
|
+
historical_data = historical_data.where('received_at > ?', Time.now - params[:interval].to_i.days)
|
73
|
+
end
|
66
74
|
json historical_data.group(:date)
|
67
75
|
end
|
68
76
|
end
|
@@ -21,13 +21,26 @@ def resolve_blacklist(mail_id, email_address, domain, context, dns_blacklists)
|
|
21
21
|
dns_blacklists.find { |bl| bl.blacklisted?(ip) }&.server
|
22
22
|
end
|
23
23
|
|
24
|
+
def extract_email_address(mail)
|
25
|
+
email_address = mail['sender']['emailAddress']['address']
|
26
|
+
return email_address unless email_address.nil?
|
27
|
+
|
28
|
+
# Sometimes the SMTP From header is misformatted and the email address is
|
29
|
+
# parsed as part of the name by Outlook. Try to extract the email address
|
30
|
+
# from the name.
|
31
|
+
sender_name = mail['sender']['emailAddress']['name']
|
32
|
+
return nil if sender_name.nil?
|
33
|
+
|
34
|
+
sender_name.scan(/<(.+@.+)>/).first&.first
|
35
|
+
end
|
36
|
+
|
24
37
|
while continue
|
25
38
|
mails = context.graph_client.mails(context.config.junk_folder_id)
|
26
39
|
continue = false
|
27
40
|
loop do
|
28
41
|
break if mails.nil?
|
29
42
|
mails['value'].each do |mail|
|
30
|
-
email_address = mail
|
43
|
+
email_address = extract_email_address(mail)
|
31
44
|
next if email_address.nil?
|
32
45
|
domain = PublicSuffix.domain(email_address.split('@', 2)[1])
|
33
46
|
|
data/lib/osa/util/db.rb
CHANGED
data/lib/osa/version.rb
CHANGED
data/lib/osa/views/index.erb
CHANGED
@@ -77,6 +77,15 @@
|
|
77
77
|
<!-- ./col -->
|
78
78
|
</div>
|
79
79
|
|
80
|
+
<div class="row">
|
81
|
+
<div id="interval-btn-group" class="btn-group">
|
82
|
+
<button onclick="updateInterval(this, '')" type="button" class="btn btn-info active">All time</button>
|
83
|
+
<button onclick="updateInterval(this, '1')" type="button" class="btn btn-info">1 day</button>
|
84
|
+
<button onclick="updateInterval(this, '7')" type="button" class="btn btn-info">7 days</button>
|
85
|
+
<button onclick="updateInterval(this, '30')" type="button" class="btn btn-info">30 days</button>
|
86
|
+
</div>
|
87
|
+
</div>
|
88
|
+
|
80
89
|
<div class="row">
|
81
90
|
<div class="card">
|
82
91
|
<div class="card-header">
|
@@ -136,6 +145,21 @@
|
|
136
145
|
|
137
146
|
<script>
|
138
147
|
let totalReportCount = 0;
|
148
|
+
let interval = "";
|
149
|
+
|
150
|
+
function updateInterval(element, newInterval) {
|
151
|
+
interval = newInterval;
|
152
|
+
let children = Array.from(document.getElementById("interval-btn-group").children);
|
153
|
+
console.log(children);
|
154
|
+
children.forEach((child) => {
|
155
|
+
if (child === element) {
|
156
|
+
child.classList.add("active");
|
157
|
+
} else {
|
158
|
+
child.classList.remove("active");
|
159
|
+
}
|
160
|
+
});
|
161
|
+
update();
|
162
|
+
}
|
139
163
|
|
140
164
|
function sec2time(timeInSeconds) {
|
141
165
|
const time = parseFloat(timeInSeconds).toFixed(3);
|
@@ -179,7 +203,7 @@
|
|
179
203
|
}
|
180
204
|
|
181
205
|
async function updateSpammers() {
|
182
|
-
const response = await fetch(
|
206
|
+
const response = await fetch(`/api/stats/spammers?interval=${interval}`);
|
183
207
|
if (response.ok) {
|
184
208
|
const body = await response.json();
|
185
209
|
|
@@ -270,7 +294,7 @@
|
|
270
294
|
const historicalChart = createHistoricalChart();
|
271
295
|
|
272
296
|
async function updateHistoricalChart() {
|
273
|
-
const response = await fetch(
|
297
|
+
const response = await fetch(`/api/stats/reports/historical?interval=${interval}`);
|
274
298
|
if (response.ok) {
|
275
299
|
const body = await response.json();
|
276
300
|
|
@@ -285,11 +309,11 @@
|
|
285
309
|
|
286
310
|
function update() {
|
287
311
|
updateStats()
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
312
|
+
.then(updateSpammers)
|
313
|
+
.then(updateHistoricalChart)
|
314
|
+
.then(() => {
|
315
|
+
setTimeout(update, 10 * 60 * 1000);
|
316
|
+
})
|
293
317
|
}
|
294
318
|
|
295
319
|
update();
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: osa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moray Baruh
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|