log_sense 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.org +12 -0
- data/Gemfile.lock +20 -14
- data/Rakefile +9 -10
- data/exe/log_sense +36 -19
- data/ip_locations/dbip-country-lite.sqlite3 +0 -0
- data/lib/log_sense/apache/log_line_parser.rb +59 -0
- data/lib/log_sense/apache/log_parser.rb +101 -0
- data/lib/log_sense/emitter.rb +29 -23
- data/lib/log_sense/ip_locator.rb +8 -5
- data/lib/log_sense/options/checker.rb +26 -0
- data/lib/log_sense/options/parser.rb +170 -0
- data/lib/log_sense/rails/log_parser.rb +409 -0
- data/lib/log_sense/rails_report_shaper.rb +1 -1
- data/lib/log_sense/templates/_cdn_links.html.erb +0 -4
- data/lib/log_sense/templates/_log_structure.html.erb +2 -2
- data/lib/log_sense/templates/_rails.css.erb +3 -2
- data/lib/log_sense/templates/_report_data.html.erb +1 -1
- data/lib/log_sense/templates/_stylesheet.css +21 -19
- data/lib/log_sense/templates/_summary.html.erb +2 -2
- data/lib/log_sense/templates/report_html.erb +1 -0
- data/lib/log_sense/version.rb +1 -1
- data/lib/log_sense.rb +4 -4
- metadata +8 -8
- data/lib/log_sense/apache_log_line_parser.rb +0 -57
- data/lib/log_sense/apache_log_parser.rb +0 -100
- data/lib/log_sense/options_checker.rb +0 -24
- data/lib/log_sense/options_parser.rb +0 -147
- data/lib/log_sense/rails_log_parser.rb +0 -313
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 126257c949e11f090cc1928a1458572529f71c44a5c20baae35861241dfa7b7b
|
4
|
+
data.tar.gz: 33a1ee650598a90ca9adb6a6c2795746dfe8a7735414b22b930d3806b205b318
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f135c70480994434dea0b5ff11bac5b1239d071ae38afeda7dd3672a43e127bba06136d550f29844c60dd2d2640a87c57f7fda683240512d1fe37794d124a433
|
7
|
+
data.tar.gz: 65abbe86864aba7d9e6499e6f3ec4a0ef2facf00505ea8611eec3b44279772641bf3a4374045acac0892c8bde19ce37c74a88501fa7905342b1e30032f2b50b4
|
data/CHANGELOG.org
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
#+AUTHOR: Adolfo Villafiorita
|
3
3
|
#+STARTUP: showall
|
4
4
|
|
5
|
+
* 1.7.0
|
6
|
+
|
7
|
+
- [User] Fixes a bug with the geolocator
|
8
|
+
- [User] Fixes a bug causing a crash when no country was found by the geolocator
|
9
|
+
- [User] Fixes bugs related to corner cases (empty logs, wrong parser for log,
|
10
|
+
empty geolocation data)
|
11
|
+
- [User] Updated DB-IP country file to Jun 2024 version.
|
12
|
+
- [User] Refreshed the style a bit, removed Fira Sans and updated versions of
|
13
|
+
CSS and JS frameworks
|
14
|
+
- [Code] Move options and some code in their own dir
|
15
|
+
- [Code] Add rendering view parsing (useful in development; no views yet)
|
16
|
+
|
5
17
|
* 1.6.1
|
6
18
|
|
7
19
|
- Country DB now stores country name.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
log_sense (1.
|
4
|
+
log_sense (1.7.0)
|
5
5
|
browser
|
6
6
|
ipaddr
|
7
7
|
iso_country_codes
|
@@ -12,24 +12,30 @@ GEM
|
|
12
12
|
remote: https://rubygems.org/
|
13
13
|
specs:
|
14
14
|
browser (5.3.1)
|
15
|
-
debug (1.
|
16
|
-
irb (
|
17
|
-
reline (>= 0.3.
|
18
|
-
io-console (0.
|
19
|
-
ipaddr (1.2.
|
20
|
-
irb (1.
|
21
|
-
|
15
|
+
debug (1.9.2)
|
16
|
+
irb (~> 1.10)
|
17
|
+
reline (>= 0.3.8)
|
18
|
+
io-console (0.7.2)
|
19
|
+
ipaddr (1.2.6)
|
20
|
+
irb (1.13.1)
|
21
|
+
rdoc (>= 4.0.0)
|
22
|
+
reline (>= 0.4.2)
|
22
23
|
iso_country_codes (0.7.8)
|
23
|
-
mini_portile2 (2.8.
|
24
|
-
minitest (5.
|
24
|
+
mini_portile2 (2.8.7)
|
25
|
+
minitest (5.23.1)
|
26
|
+
psych (5.1.2)
|
27
|
+
stringio
|
25
28
|
rake (12.3.3)
|
26
|
-
|
29
|
+
rdoc (6.7.0)
|
30
|
+
psych (>= 4.0.0)
|
31
|
+
reline (0.5.8)
|
27
32
|
io-console (~> 0.5)
|
28
|
-
sqlite3 (
|
33
|
+
sqlite3 (2.0.2)
|
29
34
|
mini_portile2 (~> 2.8.0)
|
35
|
+
stringio (3.1.0)
|
30
36
|
terminal-table (3.0.2)
|
31
37
|
unicode-display_width (>= 1.1.1, < 3)
|
32
|
-
unicode-display_width (2.
|
38
|
+
unicode-display_width (2.5.0)
|
33
39
|
|
34
40
|
PLATFORMS
|
35
41
|
ruby
|
@@ -41,4 +47,4 @@ DEPENDENCIES
|
|
41
47
|
rake (~> 12.0)
|
42
48
|
|
43
49
|
BUNDLED WITH
|
44
|
-
2.
|
50
|
+
2.5.3
|
data/Rakefile
CHANGED
@@ -8,9 +8,9 @@ end
|
|
8
8
|
|
9
9
|
require_relative './lib/log_sense/ip_locator.rb'
|
10
10
|
|
11
|
-
desc "Convert Geolocation DB to sqlite"
|
11
|
+
desc "Convert Geolocation DB to sqlite (arg YYYY_MM or filename)"
|
12
12
|
task :dbip, [:filename] do |tasks, args|
|
13
|
-
filename_or_yyyy_mm = args[:filename]
|
13
|
+
filename_or_yyyy_mm = args[:filename] || ""
|
14
14
|
|
15
15
|
filename = if /\d{4}-\d{2}/.match(filename_or_yyyy_mm)
|
16
16
|
"ip_locations/dbip-country-lite-#{filename_or_yyyy_mm}.csv"
|
@@ -18,13 +18,15 @@ task :dbip, [:filename] do |tasks, args|
|
|
18
18
|
filename_or_yyyy_mm
|
19
19
|
end
|
20
20
|
|
21
|
-
# if the filename has a .gz extension or a gzipped version
|
22
|
-
# exists, gunzip it
|
21
|
+
# if the filename passed as argument has a .gz extension or a gzipped version
|
22
|
+
# of the file passed as argument exists, gunzip it
|
23
23
|
if File.extname(filename) == ".gz" || File.exist?("#{filename}.gz")
|
24
24
|
system "gunzip #{filename}.gz"
|
25
25
|
end
|
26
26
|
|
27
|
-
if
|
27
|
+
if File.exist? filename
|
28
|
+
LogSense::IpLocator::dbip_to_sqlite filename
|
29
|
+
else
|
28
30
|
puts <<-EOS
|
29
31
|
Error. Could not find: #{filename}
|
30
32
|
|
@@ -37,13 +39,10 @@ I see the following files:
|
|
37
39
|
3. Relaunch with YYYY-MM (will build: dbip-country-lite-YYYY-MM.csv)
|
38
40
|
or with filename.
|
39
41
|
|
40
|
-
Remark. If the filename has the extension .gz or if the
|
41
|
-
|
42
|
-
exists, it is gunzipped first
|
42
|
+
Remark. If the filename has the extension .gz or if the filename does not exist,
|
43
|
+
but a file with the same name and .gz extension exists, it is gunzipped first
|
43
44
|
EOS
|
44
45
|
|
45
46
|
exit
|
46
|
-
else
|
47
|
-
LogSense::IpLocator::dbip_to_sqlite filename
|
48
47
|
end
|
49
48
|
end
|
data/exe/log_sense
CHANGED
@@ -9,7 +9,7 @@ require "sqlite3"
|
|
9
9
|
|
10
10
|
# this better be here... OptionsParser consumes ARGV
|
11
11
|
@command_line = ARGV.join(" ")
|
12
|
-
@options = LogSense::
|
12
|
+
@options = LogSense::Options::Parser.parse ARGV
|
13
13
|
@input_filenames = @options[:input_filenames] + ARGV
|
14
14
|
@output_filename = @options[:output_filename]
|
15
15
|
|
@@ -20,33 +20,36 @@ require "sqlite3"
|
|
20
20
|
#
|
21
21
|
# Check input files
|
22
22
|
#
|
23
|
-
@non_existing = @input_filenames.reject { |x| File.exist?(x) }
|
24
23
|
|
24
|
+
@non_existing = @input_filenames.reject { |x| File.exist?(x) }
|
25
25
|
if @non_existing.any?
|
26
26
|
warn "Error: some input file(s) \"#{@non_existing.join(", ")}\" do not exist"
|
27
27
|
exit 1
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if @input_filenames.size > 0 &&
|
34
|
-
File.extname(@input_filenames.first) == "sqlite3" &&
|
35
|
-
@input_filenames.size > 1
|
36
|
-
warn "Error: you can pass only one sqlite3 file as input"
|
30
|
+
@sqlite3_files = @input_filenames.select { |x| File.extname(x).include?("sqlite") }
|
31
|
+
if @sqlite3_files.any? && @input_filenames.size != 1
|
32
|
+
warn "Error: when passing an SQLite3 DB, this has to be the only input file"
|
37
33
|
exit 1
|
38
34
|
end
|
39
35
|
|
36
|
+
#
|
37
|
+
# Check output files
|
38
|
+
#
|
39
|
+
|
40
|
+
# Nothing to be done, here, since we output to STDOUT if no output filename is
|
41
|
+
# specified
|
42
|
+
|
40
43
|
#
|
41
44
|
# Supported input/output chains
|
42
45
|
#
|
43
46
|
iformat = @options[:input_format]
|
44
47
|
oformat = @options[:output_format]
|
45
48
|
|
46
|
-
if !LogSense::
|
49
|
+
if !LogSense::Options::Checker.compatible?(iformat, oformat)
|
47
50
|
warn "Error: don't know how to make #{iformat} into #{oformat}."
|
48
51
|
warn "Possible transformation chains:"
|
49
|
-
warn LogSense::
|
52
|
+
warn LogSense::Options::Checker.chains_to_s
|
50
53
|
exit 1
|
51
54
|
end
|
52
55
|
|
@@ -56,8 +59,11 @@ end
|
|
56
59
|
|
57
60
|
@started_at = Time.now
|
58
61
|
|
59
|
-
|
60
|
-
|
62
|
+
#
|
63
|
+
# Input
|
64
|
+
#
|
65
|
+
|
66
|
+
if @input_filenames.size > 0 && File.extname(@input_filenames.first) == ".sqlite3"
|
61
67
|
warn "Reading SQLite3 DB ..." if @options[:verbose]
|
62
68
|
@db = SQLite3::Database.open @input_filenames.first
|
63
69
|
else
|
@@ -67,12 +73,19 @@ else
|
|
67
73
|
else
|
68
74
|
@input_filenames.map { |fname| File.open(fname, "r") }
|
69
75
|
end
|
70
|
-
|
76
|
+
|
77
|
+
class_name = "LogSense::#{@options[:input_format].capitalize}::LogParser"
|
71
78
|
parser_class = Object.const_get class_name
|
72
79
|
parser = parser_class.new
|
73
80
|
@db = parser.parse @input_files
|
74
81
|
end
|
75
82
|
|
83
|
+
#
|
84
|
+
# Output
|
85
|
+
#
|
86
|
+
|
87
|
+
# TODO this code could benefit from some classes abstracting the work a bit
|
88
|
+
|
76
89
|
if @options[:output_format] == "sqlite3"
|
77
90
|
warn "Saving SQLite3 DB ..." if @options[:verbose]
|
78
91
|
|
@@ -83,7 +96,7 @@ if @options[:output_format] == "sqlite3"
|
|
83
96
|
|
84
97
|
exit 0
|
85
98
|
elsif @options[:output_format] == "ufw"
|
86
|
-
pattern = @options[:pattern]
|
99
|
+
pattern = @options[:pattern]
|
87
100
|
|
88
101
|
if @options[:input_format] == "rails"
|
89
102
|
query = "select distinct event.ip,event.url
|
@@ -113,14 +126,18 @@ else
|
|
113
126
|
aggr = aggr_class.new(@db, @options)
|
114
127
|
@data = aggr.aggregate
|
115
128
|
|
116
|
-
if @options[:geolocation]
|
129
|
+
if @options[:geolocation] && @data[:ips].size != 0
|
117
130
|
warn "Geolocating ..." if @options[:verbose]
|
118
|
-
|
131
|
+
geolocated_data = LogSense::IpLocator.geolocate @data
|
119
132
|
|
120
133
|
warn "Grouping IPs by country ..." if @options[:verbose]
|
121
|
-
country_col =
|
122
|
-
@data[:countries] =
|
134
|
+
country_col = geolocated_data[0].size - 1
|
135
|
+
@data[:countries] = geolocated_data.group_by { |x| x[country_col] }
|
136
|
+
elsif @options[:geolocation] && @data[:ips].size == 0
|
137
|
+
warn "Skipping geolocation: no IP found" if @options[:verbose]
|
138
|
+
@data[:countries] = {}
|
123
139
|
else
|
140
|
+
warn "Skipping geolocation." if @options[:verbose]
|
124
141
|
@data[:countries] = {}
|
125
142
|
end
|
126
143
|
|
Binary file
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module LogSense
|
2
|
+
module Apache
|
3
|
+
# parses a log line and returns a hash
|
4
|
+
# LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
|
5
|
+
#
|
6
|
+
# %h: IP
|
7
|
+
# %l: ident or -
|
8
|
+
# %u: userid or -
|
9
|
+
# %t: [10/Oct/2000:13:55:36 -0700]
|
10
|
+
# day = 2*digit
|
11
|
+
# month = 3*letter
|
12
|
+
# year = 4*digit
|
13
|
+
# hour = 2*digit
|
14
|
+
# minute = 2*digit
|
15
|
+
# second = 2*digit
|
16
|
+
# zone = (`+' | `-') 4*digit
|
17
|
+
# %r: GET /apache_pb.gif HTTP/1.0
|
18
|
+
# %{User-agent}: "
|
19
|
+
#
|
20
|
+
# Example
|
21
|
+
# 116.179.32.16 - - [19/Dec/2021:22:35:11 +0100] "GET / HTTP/1.1" 200 135 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
|
22
|
+
#
|
23
|
+
class LogLineParser
|
24
|
+
DAY = /[0-9]{2}/
|
25
|
+
MONTH = /[A-Za-z]{3}/
|
26
|
+
YEAR = /[0-9]{4}/
|
27
|
+
TIMEC = /[0-9]{2}/
|
28
|
+
TIMEZONE = /(\+|-)[0-9]{4}/
|
29
|
+
|
30
|
+
IP = /(?<ip>[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}|::1|unknown)/
|
31
|
+
IDENT = /(?<ident>[^ ]+|-)/
|
32
|
+
USERID = /(?<userid>[^ ]+|-)/
|
33
|
+
|
34
|
+
TIMESTAMP = /(?<date>#{DAY}\/#{MONTH}\/#{YEAR}):(?<time>#{TIMEC}:#{TIMEC}:#{TIMEC} #{TIMEZONE})/
|
35
|
+
|
36
|
+
HTTP_METHODS = /GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH/
|
37
|
+
WEBDAV_METHODS = /COPY|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|UNLOCK/
|
38
|
+
OTHER_METHODS = /SEARCH|REPORT|PRI|HEAD\/robots.txt/
|
39
|
+
METHOD = /(?<method>#{HTTP_METHODS}|#{WEBDAV_METHODS}|#{OTHER_METHODS})/
|
40
|
+
PROTOCOL = /(?<protocol>HTTP\/[0-9]\.[0-9]|-|.*)/
|
41
|
+
URL = /(?<url>[^ ]+)/
|
42
|
+
REFERER = /(?<referer>[^"]*)/
|
43
|
+
RETURN_CODE = /(?<status>[1-5][0-9][0-9])/
|
44
|
+
SIZE = /(?<size>[0-9]+|-)/
|
45
|
+
USER_AGENT = /(?<user_agent>[^"]*)/
|
46
|
+
|
47
|
+
attr_reader :format
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@format = /#{IP} #{IDENT} #{USERID} \[#{TIMESTAMP}\] "(#{METHOD} #{URL} #{PROTOCOL}|-|.+)" #{RETURN_CODE} #{SIZE} "#{REFERER}" "#{USER_AGENT}"/
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse(line)
|
54
|
+
@format.match(line) ||
|
55
|
+
raise("Apache LogLine Parser Error: Could not parse #{line}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
require "browser"
|
3
|
+
require_relative "log_line_parser"
|
4
|
+
|
5
|
+
module LogSense
|
6
|
+
module Apache
|
7
|
+
#
|
8
|
+
# parse an Apache log file and return a SQLite3 DB
|
9
|
+
#
|
10
|
+
class LogParser
|
11
|
+
def parse(streams, options = {})
|
12
|
+
db = SQLite3::Database.new ":memory:"
|
13
|
+
|
14
|
+
db.execute "CREATE TABLE IF NOT EXISTS LogLine(
|
15
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
16
|
+
datetime TEXT,
|
17
|
+
ip TEXT,
|
18
|
+
user TEXT,
|
19
|
+
unique_visitor TEXT,
|
20
|
+
method TEXT,
|
21
|
+
path TEXT,
|
22
|
+
extension TEXT,
|
23
|
+
status TEXT,
|
24
|
+
size INTEGER,
|
25
|
+
referer TEXT,
|
26
|
+
user_agent TEXT,
|
27
|
+
bot INTEGER,
|
28
|
+
browser TEXT,
|
29
|
+
browser_version TEXT,
|
30
|
+
platform TEXT,
|
31
|
+
platform_version TEXT,
|
32
|
+
source_file TEXT,
|
33
|
+
line_number INTEGER
|
34
|
+
)"
|
35
|
+
|
36
|
+
ins = db.prepare("insert into LogLine (
|
37
|
+
datetime,
|
38
|
+
ip,
|
39
|
+
user,
|
40
|
+
unique_visitor,
|
41
|
+
method,
|
42
|
+
path,
|
43
|
+
extension,
|
44
|
+
status,
|
45
|
+
size,
|
46
|
+
referer,
|
47
|
+
user_agent,
|
48
|
+
bot,
|
49
|
+
browser,
|
50
|
+
browser_version,
|
51
|
+
platform,
|
52
|
+
platform_version,
|
53
|
+
source_file,
|
54
|
+
line_number
|
55
|
+
)
|
56
|
+
values (#{Array.new(18, '?').join(', ')})")
|
57
|
+
|
58
|
+
parser = LogLineParser.new
|
59
|
+
|
60
|
+
streams.each do |stream|
|
61
|
+
stream.readlines.each_with_index do |line, line_number|
|
62
|
+
begin
|
63
|
+
hash = parser.parse line
|
64
|
+
ua = Browser.new(hash[:user_agent], accept_language: 'en-us')
|
65
|
+
ins.execute(
|
66
|
+
DateTime.parse("#{hash[:date]}T#{hash[:time]}").iso8601,
|
67
|
+
hash[:ip],
|
68
|
+
hash[:userid],
|
69
|
+
unique_visitor_id(hash),
|
70
|
+
hash[:method],
|
71
|
+
hash[:url],
|
72
|
+
(hash[:url] ? File.extname(hash[:url]) : ''),
|
73
|
+
hash[:status],
|
74
|
+
hash[:size].to_i,
|
75
|
+
hash[:referer],
|
76
|
+
hash[:user_agent],
|
77
|
+
ua.bot? ? 1 : 0,
|
78
|
+
(ua.name || ''),
|
79
|
+
(ua.version || ''),
|
80
|
+
(ua.platform.name || ''),
|
81
|
+
(ua.platform.version || ''),
|
82
|
+
stream == $stdin ? "stdin" : stream.path,
|
83
|
+
line_number
|
84
|
+
)
|
85
|
+
rescue StandardError => e
|
86
|
+
warn e.message
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
db
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def unique_visitor_id hash
|
97
|
+
"#{hash[:date]} #{hash[:ip]} #{hash[:user_agent]}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/log_sense/emitter.rb
CHANGED
@@ -10,18 +10,18 @@ module LogSense
|
|
10
10
|
#
|
11
11
|
class Emitter
|
12
12
|
CDN_CSS = [
|
13
|
-
"https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css",
|
14
|
-
"https://cdn.jsdelivr.net/npm/foundation-sites@6.
|
15
|
-
"https://cdn.datatables.net/v/zf/dt-
|
13
|
+
# "https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css",
|
14
|
+
"https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/css/foundation.min.css",
|
15
|
+
"https://cdn.datatables.net/v/zf/dt-2.0.8/datatables.min.css"
|
16
16
|
].freeze
|
17
17
|
|
18
18
|
CDN_JS = [
|
19
|
-
"https://code.jquery.com/jquery-3.
|
20
|
-
"https://cdn.datatables.net/v/zf/dt-
|
21
|
-
"https://cdn.jsdelivr.net/npm/foundation-sites@6.
|
22
|
-
"https://cdn.jsdelivr.net/npm/vega@5.
|
23
|
-
"https://cdn.jsdelivr.net/npm/vega-lite@5.
|
24
|
-
"https://cdn.jsdelivr.net/npm/vega-embed@6.
|
19
|
+
"https://code.jquery.com/jquery-3.7.1.min.js",
|
20
|
+
"https://cdn.datatables.net/v/zf/dt-2.0.8/datatables.min.js",
|
21
|
+
"https://cdn.jsdelivr.net/npm/foundation-sites@6.8.1/dist/js/foundation.min.js",
|
22
|
+
"https://cdn.jsdelivr.net/npm/vega@5.28.0",
|
23
|
+
"https://cdn.jsdelivr.net/npm/vega-lite@5.18.1",
|
24
|
+
"https://cdn.jsdelivr.net/npm/vega-embed@6.25.0"
|
25
25
|
].freeze
|
26
26
|
|
27
27
|
def self.emit(reports = {}, data = {}, options = {})
|
@@ -66,21 +66,27 @@ module LogSense
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
69
|
+
# taken from Ruby on Rails
|
70
|
+
JS_ESCAPE_MAP = {
|
71
|
+
"\\" => "\\\\",
|
72
|
+
"</" => '<\/',
|
73
|
+
"\r\n" => '\n',
|
74
|
+
"\n" => '\n',
|
75
|
+
"\r" => '\n',
|
76
|
+
'"' => '\\"',
|
77
|
+
"'" => "\\'",
|
78
|
+
"`" => "\\`",
|
79
|
+
"$" => "\\$"
|
80
|
+
}
|
81
|
+
|
82
|
+
# taken from Ruby on Rails
|
83
|
+
def self.escape_javascript(javascript)
|
84
|
+
javascript = javascript.to_s
|
85
|
+
if javascript.empty?
|
86
|
+
""
|
87
|
+
else
|
88
|
+
javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
82
89
|
end
|
83
|
-
string
|
84
90
|
end
|
85
91
|
|
86
92
|
def self.slugify(string)
|
data/lib/log_sense/ip_locator.rb
CHANGED
@@ -46,7 +46,7 @@ module LogSense
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def merge(parser_db)
|
49
|
-
|
49
|
+
Sqlite3::Database.open DB_FILE
|
50
50
|
parser_db
|
51
51
|
end
|
52
52
|
|
@@ -75,11 +75,14 @@ module LogSense
|
|
75
75
|
def self.geolocate(data)
|
76
76
|
@location_db = IpLocator.load_db
|
77
77
|
|
78
|
-
data[:ips].
|
79
|
-
|
80
|
-
|
78
|
+
data[:ips].map do |line|
|
79
|
+
begin
|
80
|
+
country_code = IpLocator.locate_ip line[0], @location_db
|
81
|
+
line + [country_code]
|
82
|
+
rescue
|
83
|
+
line + ["INVALID IP"]
|
84
|
+
end
|
81
85
|
end
|
82
|
-
data
|
83
86
|
end
|
84
87
|
end
|
85
88
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module LogSense
|
2
|
+
#
|
3
|
+
# Check options and return appropriate error if
|
4
|
+
# combinations of command arguments are wrong
|
5
|
+
#
|
6
|
+
module Options
|
7
|
+
module Checker
|
8
|
+
SUPPORTED_CHAINS = {
|
9
|
+
rails: %i[txt html sqlite3 ufw],
|
10
|
+
apache: %i[txt html sqlite3 ufw]
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def self.compatible?(iformat, oformat)
|
14
|
+
(SUPPORTED_CHAINS[iformat.to_sym] || []).include? oformat.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.chains_to_s
|
18
|
+
string = ""
|
19
|
+
SUPPORTED_CHAINS.each do |iformat, oformat|
|
20
|
+
string << "- #{iformat}: #{oformat.join(", ")}\n"
|
21
|
+
end
|
22
|
+
string
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|