dmarcator 0.6.0 → 0.7.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 +4 -4
- data/ChangeLog.md +18 -0
- data/README.md +16 -3
- data/lib/dmarcator/exceptions.rb +5 -0
- data/lib/dmarcator/parser.rb +42 -0
- data/lib/dmarcator/query.rb +9 -6
- data/lib/dmarcator/record.rb +6 -13
- data/lib/dmarcator/report/auth_result.rb +12 -1
- data/lib/dmarcator/report/metadata.rb +16 -1
- data/lib/dmarcator/report/policy.rb +16 -1
- data/lib/dmarcator/report/reason.rb +6 -1
- data/lib/dmarcator/report/record.rb +18 -1
- data/lib/dmarcator/report.rb +3 -1
- data/lib/dmarcator/resolver/doh.rb +22 -3
- data/lib/dmarcator/uri.rb +9 -4
- data/lib/dmarcator/version.rb +1 -1
- data/lib/dmarcator/xml_helper.rb +0 -24
- data/lib/dmarcator.rb +12 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dfd0c6f8b9f84d311a49c5ad8a6b0e43618bd151ebd9cb7532a76486f4ea600a
|
|
4
|
+
data.tar.gz: 1b0eb0947c60844e369594d4c28e68510a91246faceb4a099f2c08dd7391a3fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a151f9aabc45f12dc8107a0d4fb8c61bcb4f66880698045ba6df8dc97664f4840db0dc98af62fce3f2c77243f9250ee0df6d886908816a71b853d55f94d4ca8a
|
|
7
|
+
data.tar.gz: 7d7d50f10d95e4d762e8a8f3f1deb9fab301f690b942e65f77129f0ee633afa108281a86947ac6fe81a59e8062fe62f51e9b492ab0f2e08940b6483ad27f8248
|
data/ChangeLog.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
### 0.7.0 / 2026-10-29
|
|
2
|
+
|
|
3
|
+
* Plaintext DNS lookups now fail closed: a lookup failure raises `DMARCator::ResolutionError` instead of being reported as a domain without a DMARC record. Previously a timeout or resolver failure looked like no policy, which failed open. This is a behavior change and applies to every transport.
|
|
4
|
+
* DoH responses must use the `application/dns-message` content type (RFC 8484) and are capped at 65,535 bytes.
|
|
5
|
+
* `DMARCator::Uri` no longer forwards calls to private methods of the wrapped `URI::MailTo`.
|
|
6
|
+
* Remove the unused `XmlHelper#get_int`/`#get_timestamp` helpers and the deprecated `Record.from_txt`.
|
|
7
|
+
* Memoize a missing report version instead of recomputing it, and scope spec constants under `DMARCator`.
|
|
8
|
+
* Add `changelog_uri` and `documentation_uri` gem metadata, and document that domains must be passed as A-labels.
|
|
9
|
+
* Add a CI workflow that runs the specs on the minimum supported Ruby and the current release, loads the gem with warnings enabled, fails on YARD warnings, and builds the gem.
|
|
10
|
+
|
|
11
|
+
### 0.6.1 / 2026-10-29
|
|
12
|
+
|
|
13
|
+
* Remove a redundant `attr_reader :sp` that `Record#sp` already overrode, fixing a `method redefined; discarding old sp` warning emitted under `-w` (as CI enables).
|
|
14
|
+
* Add tests covering `Record#sp` falling back to `p` when `sp` is omitted.
|
|
15
|
+
* Document every report accessor, constant, and public class that YARD previously omitted; documentation coverage is now 100% with no warnings.
|
|
16
|
+
* Note the encrypted-transport guarantees in the README security section.
|
|
17
|
+
* No functional changes.
|
|
18
|
+
|
|
1
19
|
### 0.6.0 / 2026-10-28
|
|
2
20
|
|
|
3
21
|
* Add DNS over HTTPS (RFC 8484) and DNS over TLS (RFC 7858) transports, selected with `DMARCATOR_DNS` (`doh` or `dot`). No provider is bundled and plaintext remains the default.
|
data/README.md
CHANGED
|
@@ -93,6 +93,11 @@ record = DMARCator::Record.query("twitter.com")
|
|
|
93
93
|
# => #<DMARCator::Record ... @p=:reject, @rua=[...], ...>
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
+
Domains must be ASCII (A-labels): pass punycode such as
|
|
97
|
+
`xn--mnchen-3ya.de` rather than `münchen.de`. A lookup failure raises
|
|
98
|
+
`DMARCator::ResolutionError`; `nil` only means the domain genuinely has no
|
|
99
|
+
DMARC record.
|
|
100
|
+
|
|
96
101
|
## Strict parsing
|
|
97
102
|
|
|
98
103
|
By default, unknown tags and tags with invalid values are ignored. Pass
|
|
@@ -149,9 +154,11 @@ hostname.
|
|
|
149
154
|
|
|
150
155
|
Encrypted transports fail closed: a transport failure raises
|
|
151
156
|
`DMARCator::ResolutionError` instead of being reported as a domain without a
|
|
152
|
-
DMARC record.
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
DMARC record. This applies to every transport, including plaintext —
|
|
158
|
+
`Resolv` returns an empty result for NXDOMAIN and raises for timeouts and
|
|
159
|
+
other failures, so a failed lookup is never mistaken for an absent record.
|
|
160
|
+
Plaintext remains the default because forcing a public resolver breaks private
|
|
161
|
+
and split-horizon zones, and some networks block port 853 or public resolvers.
|
|
155
162
|
|
|
156
163
|
A resolver can also be built explicitly:
|
|
157
164
|
|
|
@@ -177,6 +184,12 @@ DMARC records and XML reports are treated as untrusted input:
|
|
|
177
184
|
CVE-2026-80212/CVE-2026-80213.
|
|
178
185
|
* Domain names are validated before resolution (labels ≤ 63 octets, total ≤
|
|
179
186
|
248 octets, no leading/trailing hyphens).
|
|
187
|
+
* Optional [DNS over HTTPS and DNS over TLS](#dns-transport) transports verify
|
|
188
|
+
TLS certificates and fail closed: a transport failure raises
|
|
189
|
+
`DMARCator::ResolutionError` rather than being reported as a domain without a
|
|
190
|
+
DMARC record, so a blocked or downgraded lookup can never silently enforce no
|
|
191
|
+
policy. Plaintext lookups fail closed the same way. DoH responses must be
|
|
192
|
+
`application/dns-message` and are capped at 65,535 bytes.
|
|
180
193
|
|
|
181
194
|
## Requirements
|
|
182
195
|
|
data/lib/dmarcator/exceptions.rb
CHANGED
data/lib/dmarcator/parser.rb
CHANGED
|
@@ -50,6 +50,21 @@ module DMARCator
|
|
|
50
50
|
unknown_tag
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
#
|
|
54
|
+
# Defines a rule for a `tag=value` DMARC tag, capturing values that do not
|
|
55
|
+
# match the rule as `:invalid_tag` so strict mode can reject them.
|
|
56
|
+
#
|
|
57
|
+
# @param [Symbol] name
|
|
58
|
+
# The rule name, used as `dmarc_<name>`.
|
|
59
|
+
#
|
|
60
|
+
# @param [String] tag
|
|
61
|
+
# The DMARC tag name.
|
|
62
|
+
#
|
|
63
|
+
# @yieldreturn [Parslet::Atoms::Base]
|
|
64
|
+
# The parslet matching the tag's value.
|
|
65
|
+
#
|
|
66
|
+
# @api private
|
|
67
|
+
#
|
|
53
68
|
def self.tag_rule(name,tag,&block)
|
|
54
69
|
rule(:"dmarc_#{name}") do
|
|
55
70
|
(
|
|
@@ -123,6 +138,11 @@ module DMARCator
|
|
|
123
138
|
rule(:wsp) { str(' ') | str("\t") }
|
|
124
139
|
rule(:wsp?) { wsp.repeat }
|
|
125
140
|
|
|
141
|
+
#
|
|
142
|
+
# Converts the raw parse tree into tag attributes.
|
|
143
|
+
#
|
|
144
|
+
# @api private
|
|
145
|
+
#
|
|
126
146
|
class Transform < Parslet::Transform
|
|
127
147
|
|
|
128
148
|
rule(fo_opt: simple(:fo_opt)) { fo_opt }
|
|
@@ -155,8 +175,21 @@ module DMARCator
|
|
|
155
175
|
rule(rua: subtree(:uris)) { {rua: Array(uris)} }
|
|
156
176
|
rule(ruf: subtree(:uris)) { {ruf: Array(uris)} }
|
|
157
177
|
|
|
178
|
+
# Maximum length of error messages echoed from untrusted input.
|
|
158
179
|
MAX_ERROR_MESSAGE_LENGTH = 300
|
|
159
180
|
|
|
181
|
+
#
|
|
182
|
+
# Parses a `mailto:` URI from a parse tree slice.
|
|
183
|
+
#
|
|
184
|
+
# @param [Parslet::Slice] value
|
|
185
|
+
#
|
|
186
|
+
# @return [URI::MailTo]
|
|
187
|
+
#
|
|
188
|
+
# @raise [InvalidRecord]
|
|
189
|
+
# The URI is not a valid `mailto:` URI.
|
|
190
|
+
#
|
|
191
|
+
# @api private
|
|
192
|
+
#
|
|
160
193
|
def self.parse_uri(value)
|
|
161
194
|
uri = value.to_s
|
|
162
195
|
|
|
@@ -173,6 +206,15 @@ module DMARCator
|
|
|
173
206
|
parsed
|
|
174
207
|
end
|
|
175
208
|
|
|
209
|
+
#
|
|
210
|
+
# Truncates an error message to a bounded length.
|
|
211
|
+
#
|
|
212
|
+
# @param [String] message
|
|
213
|
+
#
|
|
214
|
+
# @return [String]
|
|
215
|
+
#
|
|
216
|
+
# @api private
|
|
217
|
+
#
|
|
176
218
|
def self.truncate(message)
|
|
177
219
|
message = message.to_s
|
|
178
220
|
|
data/lib/dmarcator/query.rb
CHANGED
|
@@ -104,11 +104,11 @@ module DMARCator
|
|
|
104
104
|
# plaintext resolvers receive {DNS_TIMEOUTS}.
|
|
105
105
|
#
|
|
106
106
|
# @return [String, nil]
|
|
107
|
-
# The domain's DMARC record or `nil` if none
|
|
107
|
+
# The domain's DMARC record, or `nil` if the domain simply has none.
|
|
108
108
|
#
|
|
109
|
-
# @raise [
|
|
110
|
-
#
|
|
111
|
-
#
|
|
109
|
+
# @raise [ResolutionError]
|
|
110
|
+
# The lookup failed. A failure is never reported as a missing record, so a
|
|
111
|
+
# blocked or degraded resolver cannot silently enforce no policy.
|
|
112
112
|
#
|
|
113
113
|
# @api semipublic
|
|
114
114
|
#
|
|
@@ -121,10 +121,13 @@ module DMARCator
|
|
|
121
121
|
|
|
122
122
|
host = "_dmarc.#{domain}"
|
|
123
123
|
|
|
124
|
+
# Resolv returns an empty result for NXDOMAIN and raises for timeouts and
|
|
125
|
+
# other failures, so any exception here is a transport failure, not an
|
|
126
|
+
# absent record.
|
|
124
127
|
begin
|
|
125
128
|
records = resolver.getresources(host, Resolv::DNS::Resource::IN::TXT)
|
|
126
|
-
rescue Resolv::ResolvError
|
|
127
|
-
|
|
129
|
+
rescue Resolv::ResolvError => error
|
|
130
|
+
raise ResolutionError, "DNS lookup failed for #{host}: #{error.message}"
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
# Per RFC 7489 §6.6.3, use the TXT record beginning with "v=DMARC1".
|
data/lib/dmarcator/record.rb
CHANGED
|
@@ -7,10 +7,15 @@ require 'dmarcator/exceptions'
|
|
|
7
7
|
require 'resolv'
|
|
8
8
|
|
|
9
9
|
module DMARCator
|
|
10
|
+
#
|
|
11
|
+
# A parsed DMARC record, as published in a `_dmarc.<domain>` TXT record.
|
|
12
|
+
#
|
|
13
|
+
# @see https://www.rfc-editor.org/rfc/rfc7489
|
|
14
|
+
#
|
|
10
15
|
class Record
|
|
11
16
|
|
|
12
17
|
# `p` field.
|
|
13
|
-
#
|
|
18
|
+
#
|
|
14
19
|
# @return [:none, :quarantine, :reject]
|
|
15
20
|
attr_reader :p
|
|
16
21
|
|
|
@@ -24,11 +29,6 @@ module DMARCator
|
|
|
24
29
|
# @return [Array<Uri>]
|
|
25
30
|
attr_reader :ruf
|
|
26
31
|
|
|
27
|
-
# `sp` field.
|
|
28
|
-
#
|
|
29
|
-
# @return [:none, :quarantine, :reject]
|
|
30
|
-
attr_reader :sp
|
|
31
|
-
|
|
32
32
|
# `v` field.
|
|
33
33
|
#
|
|
34
34
|
# @return [:DMARC1]
|
|
@@ -288,13 +288,6 @@ module DMARCator
|
|
|
288
288
|
raise InvalidRecord.new(truncate_error_message(error.message), error.cause)
|
|
289
289
|
end
|
|
290
290
|
|
|
291
|
-
#
|
|
292
|
-
# @deprecated use {parse} instead.
|
|
293
|
-
#
|
|
294
|
-
def self.from_txt(rec)
|
|
295
|
-
parse(rec)
|
|
296
|
-
end
|
|
297
|
-
|
|
298
291
|
# Maximum length of error messages echoed from parsing untrusted input.
|
|
299
292
|
MAX_ERROR_MESSAGE_LENGTH = 500
|
|
300
293
|
|
|
@@ -10,9 +10,20 @@ module DMARCator
|
|
|
10
10
|
class AuthResult
|
|
11
11
|
include XmlHelper
|
|
12
12
|
|
|
13
|
+
# Attribute names exposed by this class.
|
|
13
14
|
ATTRIBUTES = [:type, :domain, :result, :selector, :scope]
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
# @!attribute [rw] type
|
|
17
|
+
# @return [String] the entry name (`dkim` or `spf`).
|
|
18
|
+
# @!attribute [rw] domain
|
|
19
|
+
# @return [String, nil] the authenticated domain.
|
|
20
|
+
# @!attribute [rw] result
|
|
21
|
+
# @return [String, nil] the authentication result.
|
|
22
|
+
# @!attribute [rw] selector
|
|
23
|
+
# @return [String, nil] the DKIM selector.
|
|
24
|
+
# @!attribute [rw] scope
|
|
25
|
+
# @return [String, nil] the SPF scope.
|
|
26
|
+
ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
|
|
16
27
|
|
|
17
28
|
def initialize(node)
|
|
18
29
|
@node = node
|
|
@@ -10,12 +10,27 @@ module DMARCator
|
|
|
10
10
|
class Metadata
|
|
11
11
|
include XmlHelper
|
|
12
12
|
|
|
13
|
+
# Attribute names exposed by this class.
|
|
13
14
|
ATTRIBUTES = [
|
|
14
15
|
:org_name, :email, :extra_contact_info, :report_id,
|
|
15
16
|
:begin_at, :end_at, :errors
|
|
16
17
|
]
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# @!attribute [rw] org_name
|
|
20
|
+
# @return [String, nil] the reporting organization.
|
|
21
|
+
# @!attribute [rw] email
|
|
22
|
+
# @return [String, nil] the reporting organization's contact address.
|
|
23
|
+
# @!attribute [rw] extra_contact_info
|
|
24
|
+
# @return [String, nil] additional contact information.
|
|
25
|
+
# @!attribute [rw] report_id
|
|
26
|
+
# @return [String, nil] the unique report identifier.
|
|
27
|
+
# @!attribute [rw] begin_at
|
|
28
|
+
# @return [Time, nil] the start of the reporting interval.
|
|
29
|
+
# @!attribute [rw] end_at
|
|
30
|
+
# @return [Time, nil] the end of the reporting interval.
|
|
31
|
+
# @!attribute [rw] errors
|
|
32
|
+
# @return [Array<String>] errors reported by the reporting organization.
|
|
33
|
+
ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
|
|
19
34
|
|
|
20
35
|
def initialize(node)
|
|
21
36
|
@node = node
|
|
@@ -10,9 +10,24 @@ module DMARCator
|
|
|
10
10
|
class Policy
|
|
11
11
|
include XmlHelper
|
|
12
12
|
|
|
13
|
+
# Attribute names exposed by this class.
|
|
13
14
|
ATTRIBUTES = [:domain, :adkim, :aspf, :p, :sp, :pct, :fo]
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
# @!attribute [rw] domain
|
|
17
|
+
# @return [String, nil] the domain the policy applies to.
|
|
18
|
+
# @!attribute [rw] adkim
|
|
19
|
+
# @return [String, nil] the DKIM alignment mode (`r` or `s`).
|
|
20
|
+
# @!attribute [rw] aspf
|
|
21
|
+
# @return [String, nil] the SPF alignment mode (`r` or `s`).
|
|
22
|
+
# @!attribute [rw] p
|
|
23
|
+
# @return [String, nil] the requested policy.
|
|
24
|
+
# @!attribute [rw] sp
|
|
25
|
+
# @return [String, nil] the requested subdomain policy.
|
|
26
|
+
# @!attribute [rw] pct
|
|
27
|
+
# @return [Integer, nil] the percentage of messages the policy applied to.
|
|
28
|
+
# @!attribute [rw] fo
|
|
29
|
+
# @return [String, nil] the requested failure-reporting options.
|
|
30
|
+
ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
|
|
16
31
|
|
|
17
32
|
def initialize(node)
|
|
18
33
|
@node = node
|
|
@@ -10,9 +10,14 @@ module DMARCator
|
|
|
10
10
|
class Reason
|
|
11
11
|
include XmlHelper
|
|
12
12
|
|
|
13
|
+
# Attribute names exposed by this class.
|
|
13
14
|
ATTRIBUTES = [:type, :comment]
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
# @!attribute [rw] type
|
|
17
|
+
# @return [String, nil] the reason type.
|
|
18
|
+
# @!attribute [rw] comment
|
|
19
|
+
# @return [String, nil] an optional explanatory comment.
|
|
20
|
+
ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
|
|
16
21
|
|
|
17
22
|
def initialize(node)
|
|
18
23
|
@node = node
|
|
@@ -12,12 +12,29 @@ module DMARCator
|
|
|
12
12
|
class Record
|
|
13
13
|
include XmlHelper
|
|
14
14
|
|
|
15
|
+
# Attribute names exposed by this class.
|
|
15
16
|
ATTRIBUTES = [
|
|
16
17
|
:source_ip, :count, :disposition, :dkim, :spf,
|
|
17
18
|
:envelope_to, :envelope_from, :header_from
|
|
18
19
|
]
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
# @!attribute [rw] source_ip
|
|
22
|
+
# @return [String, nil] the sending IP address.
|
|
23
|
+
# @!attribute [rw] count
|
|
24
|
+
# @return [Integer, nil] the number of messages from this source.
|
|
25
|
+
# @!attribute [rw] disposition
|
|
26
|
+
# @return [String, nil] the action applied to the messages.
|
|
27
|
+
# @!attribute [rw] dkim
|
|
28
|
+
# @return [String, nil] the DKIM evaluation result.
|
|
29
|
+
# @!attribute [rw] spf
|
|
30
|
+
# @return [String, nil] the SPF evaluation result.
|
|
31
|
+
# @!attribute [rw] envelope_to
|
|
32
|
+
# @return [String, nil] the envelope recipient domain.
|
|
33
|
+
# @!attribute [rw] envelope_from
|
|
34
|
+
# @return [String, nil] the envelope sender domain.
|
|
35
|
+
# @!attribute [rw] header_from
|
|
36
|
+
# @return [String, nil] the `From` header domain.
|
|
37
|
+
ATTRIBUTES.each { |attribute| attr_accessor(attribute) }
|
|
21
38
|
attr_writer :auth_results, :reasons
|
|
22
39
|
|
|
23
40
|
def initialize(node)
|
data/lib/dmarcator/report.rb
CHANGED
|
@@ -25,6 +25,12 @@ module DMARCator
|
|
|
25
25
|
# Default connect/read timeout in seconds.
|
|
26
26
|
DEFAULT_TIMEOUT = 5
|
|
27
27
|
|
|
28
|
+
# Maximum accepted response body in bytes (the DNS message limit).
|
|
29
|
+
MAX_RESPONSE_BYTES = 65_535
|
|
30
|
+
|
|
31
|
+
# Media type RFC 8484 requires for DNS messages.
|
|
32
|
+
CONTENT_TYPE = 'application/dns-message'
|
|
33
|
+
|
|
28
34
|
# @return [URI::HTTPS] the DoH endpoint.
|
|
29
35
|
attr_reader :url
|
|
30
36
|
|
|
@@ -92,8 +98,8 @@ module DMARCator
|
|
|
92
98
|
http.ipaddr = @ipaddr if @ipaddr
|
|
93
99
|
|
|
94
100
|
request = Net::HTTP::Post.new(@url.request_uri)
|
|
95
|
-
request['content-type'] =
|
|
96
|
-
request['accept'] =
|
|
101
|
+
request['content-type'] = CONTENT_TYPE
|
|
102
|
+
request['accept'] = CONTENT_TYPE
|
|
97
103
|
request.body = query.encode
|
|
98
104
|
|
|
99
105
|
response = http.start { |connection| connection.request(request) }
|
|
@@ -104,7 +110,20 @@ module DMARCator
|
|
|
104
110
|
raise ResolutionError, "DoH endpoint returned HTTP #{status}"
|
|
105
111
|
end
|
|
106
112
|
|
|
107
|
-
response.
|
|
113
|
+
content_type = response['content-type'].to_s
|
|
114
|
+
|
|
115
|
+
unless content_type.start_with?(CONTENT_TYPE)
|
|
116
|
+
raise ResolutionError, "DoH endpoint returned unexpected content-type: #{content_type.inspect}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
body = response.body.to_s.b
|
|
120
|
+
|
|
121
|
+
if body.bytesize > MAX_RESPONSE_BYTES
|
|
122
|
+
raise ResolutionError,
|
|
123
|
+
"DoH response exceeds maximum size of #{MAX_RESPONSE_BYTES} bytes"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
body
|
|
108
127
|
rescue ResolutionError
|
|
109
128
|
raise
|
|
110
129
|
rescue StandardError => error
|
data/lib/dmarcator/uri.rb
CHANGED
|
@@ -118,19 +118,24 @@ module DMARCator
|
|
|
118
118
|
private
|
|
119
119
|
|
|
120
120
|
#
|
|
121
|
-
# Pass
|
|
121
|
+
# Pass public methods through to {#uri}.
|
|
122
|
+
#
|
|
123
|
+
# Private methods are not forwarded: delegation is limited to the wrapped
|
|
124
|
+
# URI's public interface.
|
|
122
125
|
#
|
|
123
126
|
def method_missing(name,*arguments,&block)
|
|
124
|
-
@uri.
|
|
127
|
+
return super unless @uri.respond_to?(name)
|
|
128
|
+
|
|
129
|
+
@uri.public_send(name,*arguments,&block)
|
|
125
130
|
end
|
|
126
131
|
|
|
127
132
|
#
|
|
128
|
-
# Indicates whether a missing method can be handled by {#uri}.
|
|
133
|
+
# Indicates whether a missing public method can be handled by {#uri}.
|
|
129
134
|
#
|
|
130
135
|
# @return [Boolean]
|
|
131
136
|
#
|
|
132
137
|
def respond_to_missing?(name, include_private = false)
|
|
133
|
-
@uri.respond_to?(name
|
|
138
|
+
@uri.respond_to?(name) || super
|
|
134
139
|
end
|
|
135
140
|
|
|
136
141
|
end
|
data/lib/dmarcator/version.rb
CHANGED
data/lib/dmarcator/xml_helper.rb
CHANGED
|
@@ -20,30 +20,6 @@ module DMARCator
|
|
|
20
20
|
@node.at_xpath(xpath)&.text
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
# Returns a Unix timestamp as a `Time`.
|
|
25
|
-
#
|
|
26
|
-
# @param [String] xpath
|
|
27
|
-
# The XPath expression to match.
|
|
28
|
-
#
|
|
29
|
-
# @return [Time, nil]
|
|
30
|
-
#
|
|
31
|
-
def get_timestamp(xpath)
|
|
32
|
-
parse_timestamp(get_text(xpath))
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
# Returns an integer value.
|
|
37
|
-
#
|
|
38
|
-
# @param [String] xpath
|
|
39
|
-
# The XPath expression to match.
|
|
40
|
-
#
|
|
41
|
-
# @return [Integer, nil]
|
|
42
|
-
#
|
|
43
|
-
def get_int(xpath)
|
|
44
|
-
parse_integer(get_text(xpath))
|
|
45
|
-
end
|
|
46
|
-
|
|
47
23
|
#
|
|
48
24
|
# Parses a Unix timestamp from raw element text.
|
|
49
25
|
#
|
data/lib/dmarcator.rb
CHANGED
|
@@ -6,3 +6,15 @@ require 'dmarcator/record'
|
|
|
6
6
|
require 'dmarcator/parser'
|
|
7
7
|
require 'dmarcator/report'
|
|
8
8
|
require 'dmarcator/version'
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Parses, queries, and validates DMARC records and aggregates.
|
|
12
|
+
#
|
|
13
|
+
# Use {DMARCator::Record} for `_dmarc.<domain>` TXT records, {DMARCator::Report}
|
|
14
|
+
# for aggregate/forensic XML reports, and {DMARCator::Resolver} to choose the
|
|
15
|
+
# DNS transport (plaintext, DNS over HTTPS, or DNS over TLS).
|
|
16
|
+
#
|
|
17
|
+
# @see https://www.rfc-editor.org/rfc/rfc7489
|
|
18
|
+
#
|
|
19
|
+
module DMARCator
|
|
20
|
+
end
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Noah Berman
|
|
@@ -92,6 +92,8 @@ licenses:
|
|
|
92
92
|
metadata:
|
|
93
93
|
source_code_uri: https://github.com/bermannoah/dmarcator
|
|
94
94
|
bug_tracker_uri: https://github.com/bermannoah/dmarcator/issues
|
|
95
|
+
changelog_uri: https://github.com/bermannoah/dmarcator/blob/main/ChangeLog.md
|
|
96
|
+
documentation_uri: https://rubydoc.info/gems/dmarcator
|
|
95
97
|
rubygems_mfa_required: 'true'
|
|
96
98
|
rdoc_options: []
|
|
97
99
|
require_paths:
|