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.
@@ -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,13 @@
1
+ # dmarc.yml
2
+
3
+ host: smtp.example.com
4
+ port: 993
5
+ # ca_file: root.crt
6
+ login: username
7
+ password: plain-password
8
+ folder: INBOX
9
+ rules: rules/dmarc.yml
10
+ create_folder: true
11
+ translate_slash: false
12
+
13
+ # eof
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ EXPIRE_DAYS="365"
4
+
5
+ RSYNC_TARGET="server.example.com:"
6
+
7
+ # 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: []