Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeError with nested SPF record with the domain macro #36

Open
Hirosvk opened this issue Jan 15, 2025 · 0 comments
Open

TypeError with nested SPF record with the domain macro #36

Hirosvk opened this issue Jan 15, 2025 · 0 comments

Comments

@Hirosvk
Copy link

Hirosvk commented Jan 15, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant