dmarc_report 1.0
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/.gitignore +14 -0
- data/.rubocop.yml +44 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +84 -0
- data/bin/dmarc_dns.rb +109 -0
- data/bin/dmarc_dump.rb +47 -0
- data/bin/dmarc_imap.rb +416 -0
- data/bin/dmarc_report.rb +161 -0
- data/bin/dmarc_ripmime.rb +144 -0
- data/bin/install-dmarc_report.rb +126 -0
- data/bin/rename_rfc.rb +44 -0
- data/examples/config/dmarc.yml +13 -0
- data/examples/dmarc-profile.sh +7 -0
- data/examples/rules/dmarc.yml +33 -0
- data/lib/xmltohash.rb +61 -0
- metadata +106 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# = install-dmarc_report.rb
|
|
4
|
+
#
|
|
5
|
+
# Author:: Dirk Meyer
|
|
6
|
+
# Copyright:: Copyright (c) 2023 Dirk Meyer
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
# SPDX-FileCopyrightText: 2023 Dirk Meyer
|
|
9
|
+
# SPDX-License-Identifier: Ruby
|
|
10
|
+
#
|
|
11
|
+
# Installs a working directory for DMARC reports
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
require 'fileutils'
|
|
15
|
+
|
|
16
|
+
bindir = File.dirname( File.expand_path( __FILE__ ) )
|
|
17
|
+
user_dir = Gem.user_dir
|
|
18
|
+
|
|
19
|
+
def install_sample( src, dest )
|
|
20
|
+
return unless File.exist?( src )
|
|
21
|
+
|
|
22
|
+
if File.exist?( dest )
|
|
23
|
+
warn "skipped: #{dest}"
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
FileUtils.copy_file( src, dest, verbose: true )
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
[ 'config', 'rules' ].each do |dir|
|
|
31
|
+
FileUtils.mkdir_p( dir )
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
[ 'examples', "#{user_dir}/gems/dmarc_report-*/examples" ].each do |dir|
|
|
35
|
+
list = Dir.glob( dir )
|
|
36
|
+
next if list.empty?
|
|
37
|
+
|
|
38
|
+
dir2 = list.last
|
|
39
|
+
[ 'config/*', 'rules/*', '*' ].each do |dir3|
|
|
40
|
+
list3 = Dir.glob( "#{dir2}/#{dir3}" )
|
|
41
|
+
list3.each do |file|
|
|
42
|
+
next unless File.exist?( file )
|
|
43
|
+
next if File.directory?( file )
|
|
44
|
+
|
|
45
|
+
target = file.sub( /^.*\/examples\//, '' )
|
|
46
|
+
install_sample( file, target )
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
[ '.', "#{user_dir}/gems/dmarc_report-*" ].each do |dir|
|
|
52
|
+
list = Dir.glob( dir )
|
|
53
|
+
next if list.empty?
|
|
54
|
+
|
|
55
|
+
dir2 = list.last
|
|
56
|
+
[ 'README.md', '' ].each do |dir3|
|
|
57
|
+
list3 = Dir.glob( "#{dir2}/#{dir3}" )
|
|
58
|
+
list3.each do |file|
|
|
59
|
+
next unless File.exist?( file )
|
|
60
|
+
next if File.directory?( file )
|
|
61
|
+
|
|
62
|
+
target = file.sub( /^.*\//, '' )
|
|
63
|
+
install_sample( file, target )
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
Dir.glob( 'config/*.yml' ).each do |file|
|
|
69
|
+
File.chmod( 0o600, file )
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
datadir = File.expand_path( '.' )
|
|
73
|
+
|
|
74
|
+
run = "#!/bin/sh
|
|
75
|
+
# SPDX-FileCopyrightText: 2023 Dirk Meyer
|
|
76
|
+
# SPDX-License-Identifier: Ruby
|
|
77
|
+
|
|
78
|
+
bindir=\"#{bindir}\"
|
|
79
|
+
datadir=\"#{datadir}\"
|
|
80
|
+
cd \"${datadir}\" || exit 69
|
|
81
|
+
|
|
82
|
+
PATH=\"${PATH}:#{bindir}\"
|
|
83
|
+
export PATH
|
|
84
|
+
|
|
85
|
+
mkdir -p log DMARC/in DMARC/seen DMARC/ripmime DMARC/zips DMARC/xml DMARC/old
|
|
86
|
+
\"${bindir}/dmarc_imap.rb\" > log/dmarc_imap.log
|
|
87
|
+
\"${bindir}/dmarc_ripmime.rb\" > log/dmarc_ripmime.log
|
|
88
|
+
\"${bindir}/dmarc_report.rb\" > log/dmarc_report.log
|
|
89
|
+
\"${bindir}/dmarc_dns.rb\"
|
|
90
|
+
|
|
91
|
+
# get config
|
|
92
|
+
. ./dmarc-profile.sh
|
|
93
|
+
|
|
94
|
+
if test \"${EXPIRE_DAYS}\" != \"\"
|
|
95
|
+
then
|
|
96
|
+
find DMARC/xml -name '*.xml' -and -mtime \"+${EXPIRE_DAYS}\" \
|
|
97
|
+
-exec mv '{}' 'DMARC/old/' ';'
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if test \"${RSYNC_TARGET}\" != \"\"
|
|
101
|
+
then
|
|
102
|
+
rsync -a dmarc-report.csv dmarc-dns.json \"${RSYNC_TARGET}\"
|
|
103
|
+
fi
|
|
104
|
+
exit 0
|
|
105
|
+
# eof
|
|
106
|
+
"
|
|
107
|
+
|
|
108
|
+
File.write( 'run-dmarc.sh', run )
|
|
109
|
+
File.chmod( 0o755, 'run-dmarc.sh' )
|
|
110
|
+
puts 'writing run-dmarc.sh'
|
|
111
|
+
|
|
112
|
+
crontab = `crontab -l`
|
|
113
|
+
unless crontab.include?( 'dmarc' )
|
|
114
|
+
crontab << \
|
|
115
|
+
"# crontab for dmarc_report\n" \
|
|
116
|
+
"#minute\thour\tmday\tmonth\twday\tcommand\n" \
|
|
117
|
+
"#13\t8,12,18\t*\t*\t*\t#{datadir}/run-dmarc.sh\n" \
|
|
118
|
+
"#\n"
|
|
119
|
+
File.write( 'crontab.new', crontab )
|
|
120
|
+
system( 'crontab crontab.new' )
|
|
121
|
+
puts 'writing crontab'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
exit 0
|
|
125
|
+
#
|
|
126
|
+
# eof
|
data/bin/rename_rfc.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# = rename_rfc
|
|
4
|
+
#
|
|
5
|
+
# Author:: Dirk Meyer
|
|
6
|
+
# Copyright:: Copyright (c) 2023 - 2023 Dirk Meyer
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
# SPDX-FileCopyrightText: 2023-2023 Dirk Meyer
|
|
9
|
+
# SPDX-License-Identifier: Ruby
|
|
10
|
+
#
|
|
11
|
+
# Rename RFC2047 encoden filenames to UTF-8
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
# dependecies:
|
|
15
|
+
# gem install --user-install new_rfc_2047
|
|
16
|
+
|
|
17
|
+
require 'rfc_2047'
|
|
18
|
+
|
|
19
|
+
def save_decode( key, text )
|
|
20
|
+
return nil if text.nil?
|
|
21
|
+
|
|
22
|
+
begin
|
|
23
|
+
text = Rfc2047.decode( text )
|
|
24
|
+
rescue Encoding::CompatibilityError => e
|
|
25
|
+
warn "Encoding::CompatibilityError #{e}"
|
|
26
|
+
warn "in #{key}: #{text}"
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
warn e
|
|
29
|
+
warn "in #{key}: #{text}"
|
|
30
|
+
end
|
|
31
|
+
text
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
dir = 'DMARC/seen'
|
|
35
|
+
Dir.entries( dir ).each do |file|
|
|
36
|
+
file2 = save_decode( 'filename', file )
|
|
37
|
+
next if file2 == file
|
|
38
|
+
|
|
39
|
+
puts file
|
|
40
|
+
File.rename( "#{dir}/#{file}", "#{dir}/#{file2}" )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
exit 0
|
|
44
|
+
# eof
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# dmarc.yml
|
|
2
|
+
-
|
|
3
|
+
subject: 'Report Domain: '
|
|
4
|
+
save:
|
|
5
|
+
dir: 'DMARC/in'
|
|
6
|
+
move: 'DMARC/parsed'
|
|
7
|
+
-
|
|
8
|
+
subject: 'Report domain: '
|
|
9
|
+
save:
|
|
10
|
+
dir: 'DMARC/in'
|
|
11
|
+
move: 'DMARC/parsed'
|
|
12
|
+
-
|
|
13
|
+
subject: 'DMARC Report'
|
|
14
|
+
save:
|
|
15
|
+
dir: 'DMARC/in'
|
|
16
|
+
move: 'DMARC/parsed'
|
|
17
|
+
-
|
|
18
|
+
subject: 'DMARC report'
|
|
19
|
+
save:
|
|
20
|
+
dir: 'DMARC/in'
|
|
21
|
+
move: 'DMARC/parsed'
|
|
22
|
+
-
|
|
23
|
+
subject: 'Dmarc aggregate report'
|
|
24
|
+
save:
|
|
25
|
+
dir: 'DMARC/in'
|
|
26
|
+
move: 'DMARC/parsed'
|
|
27
|
+
-
|
|
28
|
+
subject: 'DMARC Forensic Report'
|
|
29
|
+
save:
|
|
30
|
+
dir: 'DMARC/in'
|
|
31
|
+
move: 'DMARC/parsed'
|
|
32
|
+
|
|
33
|
+
# eof
|
data/lib/xmltohash.rb
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# https://gist.github.com/huy/819999#file-hash-from_xml
|
|
3
|
+
# USAGE: Hash.from_xml(YOUR_XML_STRING)require 'rubygems'
|
|
4
|
+
require 'nokogiri'
|
|
5
|
+
# modified from http://stackoverflow.com/questions/1230741/convert-a-nokogiri-document-to-a-ruby-hash/1231297#1231297
|
|
6
|
+
|
|
7
|
+
# extend standard class
|
|
8
|
+
class Hash
|
|
9
|
+
class << self
|
|
10
|
+
def from_xml( xml_io )
|
|
11
|
+
result = Nokogiri::XML( xml_io )
|
|
12
|
+
{ result.root.name.to_sym => xml_node_to_hash( result.root ) }
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
# raise your custom exception here
|
|
15
|
+
raise e
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def xml_node_to_hash( node )
|
|
19
|
+
# If we are at the root of the document, start the hash
|
|
20
|
+
return node.content.to_s unless node.element?
|
|
21
|
+
|
|
22
|
+
result_hash = {}
|
|
23
|
+
if node.attributes != {}
|
|
24
|
+
attributes = {}
|
|
25
|
+
node.attributes.each_key do |key|
|
|
26
|
+
attributes[ node.attributes[ key ].name.to_sym ] = node.attributes[ key ].value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
return attributes unless node.children.size.positive?
|
|
30
|
+
|
|
31
|
+
node.children.each do |child|
|
|
32
|
+
result = xml_node_to_hash( child )
|
|
33
|
+
|
|
34
|
+
if child.name == 'text'
|
|
35
|
+
unless child.next_sibling || child.previous_sibling
|
|
36
|
+
return result unless attributes
|
|
37
|
+
|
|
38
|
+
result_hash[ child.name.to_sym ] = result
|
|
39
|
+
end
|
|
40
|
+
elsif result_hash[ child.name.to_sym ]
|
|
41
|
+
|
|
42
|
+
if result_hash[ child.name.to_sym ].is_a?( Object::Array )
|
|
43
|
+
result_hash[ child.name.to_sym ] << result
|
|
44
|
+
else
|
|
45
|
+
result_hash[ child.name.to_sym ] = [ result_hash[ child.name.to_sym ] ] << result
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
result_hash[ child.name.to_sym ] = result
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
if attributes
|
|
52
|
+
# add code to remove non-data attributes e.g. xml schema, namespace here
|
|
53
|
+
# if there is a collision then node content supersets attributes
|
|
54
|
+
result_hash = attributes.merge( result_hash )
|
|
55
|
+
end
|
|
56
|
+
result_hash
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# eof
|
metadata
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: dmarc_report
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '1.0'
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dirk Meyer
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-12-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: new_rfc_2047
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 1.0.0
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '1.0'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 1.0.0
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: nokogiri
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 1.15.0
|
|
43
|
+
type: :runtime
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - "~>"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '1.0'
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: 1.15.0
|
|
53
|
+
description: Fetch DMARC report mails via IMAP and extract and convert the data to
|
|
54
|
+
CSV.
|
|
55
|
+
email:
|
|
56
|
+
executables:
|
|
57
|
+
- dmarc_dns.rb
|
|
58
|
+
- dmarc_imap.rb
|
|
59
|
+
- dmarc_ripmime.rb
|
|
60
|
+
- dmarc_report.rb
|
|
61
|
+
- rename_rfc.rb
|
|
62
|
+
- dmarc_dump.rb
|
|
63
|
+
- install-dmarc_report.rb
|
|
64
|
+
extensions: []
|
|
65
|
+
extra_rdoc_files: []
|
|
66
|
+
files:
|
|
67
|
+
- ".gitignore"
|
|
68
|
+
- ".rubocop.yml"
|
|
69
|
+
- CHANGELOG.md
|
|
70
|
+
- LICENSE.txt
|
|
71
|
+
- README.md
|
|
72
|
+
- bin/dmarc_dns.rb
|
|
73
|
+
- bin/dmarc_dump.rb
|
|
74
|
+
- bin/dmarc_imap.rb
|
|
75
|
+
- bin/dmarc_report.rb
|
|
76
|
+
- bin/dmarc_ripmime.rb
|
|
77
|
+
- bin/install-dmarc_report.rb
|
|
78
|
+
- bin/rename_rfc.rb
|
|
79
|
+
- examples/config/dmarc.yml
|
|
80
|
+
- examples/dmarc-profile.sh
|
|
81
|
+
- examples/rules/dmarc.yml
|
|
82
|
+
- lib/xmltohash.rb
|
|
83
|
+
homepage: https://rubygems.org/gems/dmarc-report
|
|
84
|
+
licenses:
|
|
85
|
+
- MIT
|
|
86
|
+
metadata: {}
|
|
87
|
+
post_install_message:
|
|
88
|
+
rdoc_options: []
|
|
89
|
+
require_paths:
|
|
90
|
+
- lib
|
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - ">="
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '0'
|
|
101
|
+
requirements: []
|
|
102
|
+
rubygems_version: 3.4.19
|
|
103
|
+
signing_key:
|
|
104
|
+
specification_version: 4
|
|
105
|
+
summary: fetch and parse DMARC reports
|
|
106
|
+
test_files: []
|