dmarcator 0.11.0 → 1.0.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.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../xml_helper'
3
+ require_relative '../internal/xml_helper'
4
4
 
5
5
  module DMARCator
6
6
  class Report
@@ -8,37 +8,37 @@ module DMARCator
8
8
  # Represents an `auth_results` entry (DKIM/SPF) in a DMARC XML report.
9
9
  #
10
10
  class AuthResult
11
- include XmlHelper
11
+ include Internal::XmlHelper
12
12
 
13
13
  # Attribute names exposed by this class.
14
14
  ATTRIBUTES = [:type, :domain, :result, :selector, :scope]
15
15
 
16
- # @!attribute [rw] type
16
+ # @!attribute [r] type
17
17
  # @return [String] the entry name (`dkim` or `spf`).
18
- # @!attribute [rw] domain
18
+ # @!attribute [r] domain
19
19
  # @return [String, nil] the authenticated domain.
20
- # @!attribute [rw] result
20
+ # @!attribute [r] result
21
21
  # @return [String, nil] the authentication result.
22
- # @!attribute [rw] selector
22
+ # @!attribute [r] selector
23
23
  # @return [String, nil] the DKIM selector.
24
- # @!attribute [rw] scope
24
+ # @!attribute [r] scope
25
25
  # @return [String, nil] the SPF scope.
26
- ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
26
+ ATTRIBUTES.each { |attribute| attr_reader(attribute) }
27
27
 
28
28
  def initialize(node)
29
29
  @node = node
30
- self.type = node.name
30
+ @type = node.name.freeze
31
31
 
32
32
  node.element_children.each do |element|
33
33
  case element.name
34
34
  when 'domain'
35
- self.domain ||= element.text
35
+ @domain ||= element.text.freeze
36
36
  when 'result'
37
- self.result ||= element.text
37
+ @result ||= element.text.freeze
38
38
  when 'selector'
39
- self.selector ||= element.text
39
+ @selector ||= element.text.freeze
40
40
  when 'scope'
41
- self.scope ||= element.text
41
+ @scope ||= element.text.freeze
42
42
  end
43
43
  end
44
44
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../xml_helper'
3
+ require_relative '../internal/xml_helper'
4
4
 
5
5
  module DMARCator
6
6
  class Report
@@ -8,7 +8,7 @@ module DMARCator
8
8
  # Represents the `report_metadata` element of a DMARC XML report.
9
9
  #
10
10
  class Metadata
11
- include XmlHelper
11
+ include Internal::XmlHelper
12
12
 
13
13
  # Attribute names exposed by this class.
14
14
  ATTRIBUTES = [
@@ -16,45 +16,46 @@ module DMARCator
16
16
  :begin_at, :end_at, :errors
17
17
  ]
18
18
 
19
- # @!attribute [rw] org_name
19
+ # @!attribute [r] org_name
20
20
  # @return [String, nil] the reporting organization.
21
- # @!attribute [rw] email
21
+ # @!attribute [r] email
22
22
  # @return [String, nil] the reporting organization's contact address.
23
- # @!attribute [rw] extra_contact_info
23
+ # @!attribute [r] extra_contact_info
24
24
  # @return [String, nil] additional contact information.
25
- # @!attribute [rw] report_id
25
+ # @!attribute [r] report_id
26
26
  # @return [String, nil] the unique report identifier.
27
- # @!attribute [rw] begin_at
27
+ # @!attribute [r] begin_at
28
28
  # @return [Time, nil] the start of the reporting interval.
29
- # @!attribute [rw] end_at
29
+ # @!attribute [r] end_at
30
30
  # @return [Time, nil] the end of the reporting interval.
31
- # @!attribute [rw] errors
31
+ # @!attribute [r] errors
32
32
  # @return [Array<String>] errors reported by the reporting organization.
33
- ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
33
+ ATTRIBUTES.each { |attribute| attr_reader(attribute) }
34
34
 
35
35
  def initialize(node)
36
36
  @node = node
37
- self.errors = []
37
+ @errors = []
38
38
 
39
39
  metadata = node.at_xpath('feedback/report_metadata')
40
- return unless metadata
41
40
 
42
41
  metadata.element_children.each do |element|
43
42
  case element.name
44
43
  when 'org_name'
45
- self.org_name ||= element.text
44
+ @org_name ||= element.text.freeze
46
45
  when 'email'
47
- self.email ||= element.text
46
+ @email ||= element.text.freeze
48
47
  when 'extra_contact_info'
49
- self.extra_contact_info ||= element.text
48
+ @extra_contact_info ||= element.text.freeze
50
49
  when 'report_id'
51
- self.report_id ||= element.text
50
+ @report_id ||= element.text.freeze
52
51
  when 'error'
53
- errors << element.text
52
+ errors << element.text.freeze
54
53
  when 'date_range'
55
54
  parse_date_range(element)
56
55
  end
57
56
  end
57
+ ensure
58
+ @errors.freeze
58
59
  end
59
60
 
60
61
  private
@@ -63,9 +64,9 @@ module DMARCator
63
64
  date_range.element_children.each do |element|
64
65
  case element.name
65
66
  when 'begin'
66
- self.begin_at ||= parse_timestamp(element.text)
67
+ @begin_at ||= parse_timestamp(element.text.freeze)
67
68
  when 'end'
68
- self.end_at ||= parse_timestamp(element.text)
69
+ @end_at ||= parse_timestamp(element.text.freeze)
69
70
  end
70
71
  end
71
72
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../xml_helper'
3
+ require_relative '../internal/xml_helper'
4
4
 
5
5
  module DMARCator
6
6
  class Report
@@ -8,26 +8,26 @@ module DMARCator
8
8
  # Represents the `policy_published` element of a DMARC XML report.
9
9
  #
10
10
  class Policy
11
- include XmlHelper
11
+ include Internal::XmlHelper
12
12
 
13
13
  # Attribute names exposed by this class.
14
14
  ATTRIBUTES = [:domain, :adkim, :aspf, :p, :sp, :pct, :fo]
15
15
 
16
- # @!attribute [rw] domain
16
+ # @!attribute [r] domain
17
17
  # @return [String, nil] the domain the policy applies to.
18
- # @!attribute [rw] adkim
18
+ # @!attribute [r] adkim
19
19
  # @return [String, nil] the DKIM alignment mode (`r` or `s`).
20
- # @!attribute [rw] aspf
20
+ # @!attribute [r] aspf
21
21
  # @return [String, nil] the SPF alignment mode (`r` or `s`).
22
- # @!attribute [rw] p
22
+ # @!attribute [r] p
23
23
  # @return [String, nil] the requested policy.
24
- # @!attribute [rw] sp
24
+ # @!attribute [r] sp
25
25
  # @return [String, nil] the requested subdomain policy.
26
- # @!attribute [rw] pct
26
+ # @!attribute [r] pct
27
27
  # @return [Integer, nil] the percentage of messages the policy applied to.
28
- # @!attribute [rw] fo
28
+ # @!attribute [r] fo
29
29
  # @return [String, nil] the requested failure-reporting options.
30
- ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
30
+ ATTRIBUTES.each { |attribute| attr_reader(attribute) }
31
31
 
32
32
  def initialize(node)
33
33
  @node = node
@@ -38,19 +38,19 @@ module DMARCator
38
38
  policy.element_children.each do |element|
39
39
  case element.name
40
40
  when 'pct'
41
- self.pct ||= parse_integer(element.text)
41
+ @pct ||= parse_integer(element.text.freeze)
42
42
  when 'domain'
43
- self.domain ||= element.text
43
+ @domain ||= element.text.freeze
44
44
  when 'adkim'
45
- self.adkim ||= element.text
45
+ @adkim ||= element.text.freeze
46
46
  when 'aspf'
47
- self.aspf ||= element.text
47
+ @aspf ||= element.text.freeze
48
48
  when 'p'
49
- self.p ||= element.text
49
+ @p ||= element.text.freeze
50
50
  when 'sp'
51
- self.sp ||= element.text
51
+ @sp ||= element.text.freeze
52
52
  when 'fo'
53
- self.fo ||= element.text
53
+ @fo ||= element.text.freeze
54
54
  end
55
55
  end
56
56
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../xml_helper'
3
+ require_relative '../internal/xml_helper'
4
4
 
5
5
  module DMARCator
6
6
  class Report
@@ -8,16 +8,16 @@ module DMARCator
8
8
  # Represents a `reason` element of a DMARC XML report record.
9
9
  #
10
10
  class Reason
11
- include XmlHelper
11
+ include Internal::XmlHelper
12
12
 
13
13
  # Attribute names exposed by this class.
14
14
  ATTRIBUTES = [:type, :comment]
15
15
 
16
- # @!attribute [rw] type
16
+ # @!attribute [r] type
17
17
  # @return [String, nil] the reason type.
18
- # @!attribute [rw] comment
18
+ # @!attribute [r] comment
19
19
  # @return [String, nil] an optional explanatory comment.
20
- ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
20
+ ATTRIBUTES.each { |attribute| attr_reader(attribute) }
21
21
 
22
22
  def initialize(node)
23
23
  @node = node
@@ -25,9 +25,9 @@ module DMARCator
25
25
  node.element_children.each do |element|
26
26
  case element.name
27
27
  when 'type'
28
- self.type ||= element.text
28
+ @type ||= element.text.freeze
29
29
  when 'comment'
30
- self.comment ||= element.text
30
+ @comment ||= element.text.freeze
31
31
  end
32
32
  end
33
33
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../xml_helper'
3
+ require_relative '../internal/xml_helper'
4
4
  require_relative 'auth_result'
5
5
  require_relative 'reason'
6
6
 
@@ -13,7 +13,7 @@ module DMARCator
13
13
  # DMARC TXT record.
14
14
  #
15
15
  class Row
16
- include XmlHelper
16
+ include Internal::XmlHelper
17
17
 
18
18
  # Attribute names exposed by this class.
19
19
  ATTRIBUTES = [
@@ -21,24 +21,23 @@ module DMARCator
21
21
  :envelope_to, :envelope_from, :header_from
22
22
  ]
23
23
 
24
- # @!attribute [rw] source_ip
24
+ # @!attribute [r] source_ip
25
25
  # @return [String, nil] the sending IP address.
26
- # @!attribute [rw] count
26
+ # @!attribute [r] count
27
27
  # @return [Integer, nil] the number of messages from this source.
28
- # @!attribute [rw] disposition
28
+ # @!attribute [r] disposition
29
29
  # @return [String, nil] the action applied to the messages.
30
- # @!attribute [rw] dkim
30
+ # @!attribute [r] dkim
31
31
  # @return [String, nil] the DKIM evaluation result.
32
- # @!attribute [rw] spf
32
+ # @!attribute [r] spf
33
33
  # @return [String, nil] the SPF evaluation result.
34
- # @!attribute [rw] envelope_to
34
+ # @!attribute [r] envelope_to
35
35
  # @return [String, nil] the envelope recipient domain.
36
- # @!attribute [rw] envelope_from
36
+ # @!attribute [r] envelope_from
37
37
  # @return [String, nil] the envelope sender domain.
38
- # @!attribute [rw] header_from
38
+ # @!attribute [r] header_from
39
39
  # @return [String, nil] the `From` header domain.
40
- ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
41
- attr_writer :auth_results, :reasons
40
+ ATTRIBUTES.each { |attribute| attr_reader(attribute) }
42
41
 
43
42
  def initialize(node)
44
43
  @node = node
@@ -65,7 +64,7 @@ module DMARCator
65
64
  def reasons
66
65
  return @reasons if defined?(@reasons)
67
66
 
68
- @reasons = @reason_nodes.map { |reason_node| Reason.new(reason_node) }
67
+ @reasons = @reason_nodes.map { |reason_node| Reason.new(reason_node) }.freeze
69
68
  end
70
69
 
71
70
  #
@@ -78,7 +77,7 @@ module DMARCator
78
77
 
79
78
  @auth_results = @auth_result_nodes.map do |auth_result_node|
80
79
  AuthResult.new(auth_result_node)
81
- end
80
+ end.freeze
82
81
  end
83
82
 
84
83
  private
@@ -87,9 +86,9 @@ module DMARCator
87
86
  row.element_children.each do |element|
88
87
  case element.name
89
88
  when 'source_ip'
90
- self.source_ip ||= element.text
89
+ @source_ip ||= element.text.freeze
91
90
  when 'count'
92
- self.count ||= parse_integer(element.text)
91
+ @count ||= parse_integer(element.text.freeze)
93
92
  when 'policy_evaluated'
94
93
  parse_policy_evaluated(element)
95
94
  end
@@ -100,11 +99,11 @@ module DMARCator
100
99
  policy_evaluated.element_children.each do |element|
101
100
  case element.name
102
101
  when 'disposition'
103
- self.disposition ||= element.text
102
+ @disposition ||= element.text.freeze
104
103
  when 'dkim'
105
- self.dkim ||= element.text
104
+ @dkim ||= element.text.freeze
106
105
  when 'spf'
107
- self.spf ||= element.text
106
+ @spf ||= element.text.freeze
108
107
  when 'reason'
109
108
  @reason_nodes << element
110
109
  end
@@ -115,11 +114,11 @@ module DMARCator
115
114
  identifiers.element_children.each do |element|
116
115
  case element.name
117
116
  when 'envelope_to'
118
- self.envelope_to ||= element.text
117
+ @envelope_to ||= element.text.freeze
119
118
  when 'envelope_from'
120
- self.envelope_from ||= element.text
119
+ @envelope_from ||= element.text.freeze
121
120
  when 'header_from'
122
- self.header_from ||= element.text
121
+ @header_from ||= element.text.freeze
123
122
  end
124
123
  end
125
124
  end
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'nokogiri'
4
- require 'bigdecimal'
5
- require 'bigdecimal/util'
6
4
 
7
- require_relative 'xml_helper'
5
+ require_relative 'config'
6
+ require_relative 'internal/xml_helper'
8
7
  require_relative 'exceptions'
9
8
  require_relative 'report/metadata'
10
9
  require_relative 'report/policy'
@@ -18,25 +17,40 @@ module DMARCator
18
17
  # resolved or fetched; treat report XML as untrusted input.
19
18
  #
20
19
  class Report
21
- include XmlHelper
20
+ include Internal::XmlHelper
22
21
 
23
22
  # Default maximum accepted report size in bytes (25 MiB).
24
- MAX_REPORT_BYTES = 25 * 1024 * 1024
23
+ #
24
+ # @deprecated Use {Config::DEFAULT_MAX_REPORT_BYTES}. Removed in 2.0.
25
+ #
26
+ MAX_REPORT_BYTES = Config::DEFAULT_MAX_REPORT_BYTES
25
27
 
26
28
  class << self
27
29
  #
28
30
  # The maximum accepted report size in bytes.
29
31
  #
30
- # Defaults to {MAX_REPORT_BYTES}. Parsing a report builds a DOM that is
31
- # roughly 13x the input size, so lower this to bound memory for reports
32
- # from untrusted sources.
32
+ # @deprecated Use {DMARCator.configure} with `config.max_report_bytes`.
33
+ # Removed in 2.0.
33
34
  #
34
35
  # @return [Integer]
35
36
  #
36
- attr_writer :max_report_bytes
37
-
38
37
  def max_report_bytes
39
- @max_report_bytes || MAX_REPORT_BYTES
38
+ DMARCator.config.max_report_bytes
39
+ end
40
+
41
+ #
42
+ # Sets the maximum accepted report size in bytes.
43
+ #
44
+ # @deprecated Use {DMARCator.configure} with `config.max_report_bytes`.
45
+ # Removed in 2.0.
46
+ #
47
+ # @param [Integer, nil] value
48
+ #
49
+ # @raise [ArgumentError]
50
+ # The value is not a positive Integer or nil.
51
+ #
52
+ def max_report_bytes=(value)
53
+ DMARCator.config.max_report_bytes = value
40
54
  end
41
55
  end
42
56
 
@@ -47,8 +61,8 @@ module DMARCator
47
61
  # The raw DMARC XML report.
48
62
  #
49
63
  # @raise [InvalidReport]
50
- # The report is not a String, exceeds {.max_report_bytes}, or is not
51
- # well-formed XML.
64
+ # The report is not a String, exceeds
65
+ # {Config#max_report_bytes}, or is not well-formed XML.
52
66
  #
53
67
  def initialize(xml)
54
68
  unless xml.is_a?(String)
@@ -57,7 +71,7 @@ module DMARCator
57
71
 
58
72
  @xml = xml
59
73
 
60
- limit = self.class.max_report_bytes
74
+ limit = DMARCator.config.max_report_bytes
61
75
 
62
76
  if xml.bytesize > limit
63
77
  raise InvalidReport, "DMARC XML report exceeds maximum size of #{limit} bytes"
@@ -73,23 +87,22 @@ module DMARCator
73
87
  end
74
88
 
75
89
  #
76
- # The report version.
90
+ # The report version, as published.
91
+ #
92
+ # Returned as a String because versions are opaque identifiers, not
93
+ # numbers to compute with.
77
94
  #
78
- # @return [BigDecimal, nil]
95
+ # @return [String, nil]
96
+ # The version, or `nil` when it is absent or not numeric.
79
97
  #
80
98
  def version
81
99
  return @version if defined?(@version)
82
100
 
83
101
  @version = begin
84
102
  value = get_text('feedback/version')
103
+ value = value.strip if value
85
104
 
86
- if value && !value.empty?
87
- begin
88
- BigDecimal(value)
89
- rescue ArgumentError
90
- nil
91
- end
92
- end
105
+ value.freeze if value && value.match?(/\A\d+(?:\.\d+)*\z/)
93
106
  end
94
107
  end
95
108
 
@@ -119,7 +132,7 @@ module DMARCator
119
132
  def records
120
133
  @records ||= @node.xpath('feedback/record').map do |node|
121
134
  Row.new(node)
122
- end
135
+ end.freeze
123
136
  end
124
137
  end
125
138
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module DMARCator
4
4
  # dmarcator version
5
- VERSION = '0.11.0'
5
+ VERSION = '1.0.0'
6
6
  end
data/lib/dmarcator.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'dmarcator/exceptions'
4
4
  require 'dmarcator/query'
5
5
  require 'dmarcator/record'
6
- require 'dmarcator/parser'
6
+ require 'dmarcator/internal/parser'
7
7
  require 'dmarcator/report'
8
8
  require 'dmarcator/version'
9
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dmarcator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Berman
@@ -43,20 +43,6 @@ dependencies:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
45
  version: '1.10'
46
- - !ruby/object:Gem::Dependency
47
- name: bigdecimal
48
- requirement: !ruby/object:Gem::Requirement
49
- requirements:
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: '1.0'
53
- type: :runtime
54
- prerelease: false
55
- version_requirements: !ruby/object:Gem::Requirement
56
- requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: '1.0'
60
46
  description: A hardened DMARC record parser and query client, forked from the Trail
61
47
  of Bits 'dmarc' gem.
62
48
  email:
@@ -71,7 +57,8 @@ files:
71
57
  - lib/dmarcator.rb
72
58
  - lib/dmarcator/config.rb
73
59
  - lib/dmarcator/exceptions.rb
74
- - lib/dmarcator/parser.rb
60
+ - lib/dmarcator/internal/parser.rb
61
+ - lib/dmarcator/internal/xml_helper.rb
75
62
  - lib/dmarcator/query.rb
76
63
  - lib/dmarcator/record.rb
77
64
  - lib/dmarcator/report.rb
@@ -86,7 +73,6 @@ files:
86
73
  - lib/dmarcator/resolver/encrypted.rb
87
74
  - lib/dmarcator/uri.rb
88
75
  - lib/dmarcator/version.rb
89
- - lib/dmarcator/xml_helper.rb
90
76
  homepage: https://github.com/bermannoah/dmarcator
91
77
  licenses:
92
78
  - MIT