-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Hello team,
I would like to report a bug where a TypeError is raised when nested SPF record contains the %{d} (aka ) macro.
Spec to reproduce:
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe 'SPF::Server' do
describe '#process' do
let(:dns_resolver) do
MockDnsResolver.new(
'mail1.example.com' => {
Resolv::DNS::Resource::IN::TXT => 'v=spf1 include:mail2.example.com ~all'
},
'mail2.example.com' => {
Resolv::DNS::Resource::IN::TXT => 'v=spf1 include:mail.%{d}.abc.xyz ~all'
},
'mail.mail2.example.com.abc.xyz' => {
Resolv::DNS::Resource::IN::TXT => 'v=spf1 ip4:192.168.0.0/4'
}
)
end
let(:server) do
SPF::Server.new(dns_resolver: dns_resolver)
end
let(:request) do
SPF::Request.new(
scope: 'helo',
identity: 'mail1.example.com',
ip_address: '192.168.0.1',
helo_identity: 'mta.example.com',
)
end
subject { server.process(request) }
it 'processes request' do
expect { subject }.to raise_error TypeError
end
end
end
class MockDnsResolver
def initialize(mock_responses = {})
@mock_responses = mock_responses
end
def getresources(name, typeclass)
[Resolv::DNS::Resource::IN::TXT.new(@mock_responses[name][typeclass])]
end
end(We had a real-life scenario with our customer's SPF record, which I cannot share in a public space.)
Explanation:
The SPF record for mail1.example.com includes include:mail2.example.com. The record for mail2.example.com includes the macro include:mail.%{d}.abc.xyz which is responsible for the line where the error comes from. The error occurs only when it's called as a nested record; in other words, if you process the request for the mail2.example.com domain directly, the request was processed without error.
When the gem processes a nested record, it creates a new request with the authority_domain as an instance of SPF::MacroString (here and here). When the SPF::MacroString#expand method evaluates the record and finds the d macro, it uses the .authority_domain, which is then concatenated. In the case with nested records, the authority_domain is an instance of SPF::MacroString record, hence the TypeError.
Possible remediation:
By calling .to_s to authority_domain here should fix the problem. (It worked as far as I tested).
Thank you for taking a look at this issue. Let me know if there is anything I can help to release the fix.