Propagation, global resolver testing (UDP/TCP/DoH), security scan with history tracker, infrastructure detection, DNS graph, Pi-hole/AdGuard blocklist checks and DNSBL blacklist checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
"""Offline tests: parsing and validation only, no network."""
|
|
|
|
import pytest
|
|
|
|
from zmtools import blocklists, dnsutil
|
|
|
|
|
|
@pytest.mark.parametrize('raw,want', [
|
|
('Example.COM', 'example.com'),
|
|
('https://www.example.com/path?q=1', 'www.example.com'),
|
|
('example.com.', 'example.com'),
|
|
('bücher.de', 'xn--bcher-kva.de'),
|
|
('example.com:8080', 'example.com'),
|
|
])
|
|
def test_normalize_ok(raw, want):
|
|
assert dnsutil.normalize_domain(raw) == want
|
|
|
|
|
|
@pytest.mark.parametrize('raw', ['', 'localhost', '1.2.3.4', 'a..b', '-bad.com', 'x' * 64 + '.com',
|
|
'exa mple.com', '<script>.com'])
|
|
def test_normalize_rejects(raw):
|
|
with pytest.raises(dnsutil.BadInput):
|
|
dnsutil.normalize_domain(raw)
|
|
|
|
|
|
@pytest.mark.parametrize('ip,ok', [
|
|
('8.8.8.8', True), ('2606:4700:4700::1111', True),
|
|
('10.20.0.11', False), ('127.0.0.1', False), ('192.168.1.1', False),
|
|
('100.95.46.79', False), ('169.254.1.1', False), ('0.0.0.0', False),
|
|
('224.0.0.1', False), ('::1', False), ('fd00::1', False), ('nonsense', False),
|
|
])
|
|
def test_public_ip(ip, ok):
|
|
assert dnsutil.is_public_ip(ip) is ok
|
|
|
|
|
|
def test_adblock_parse():
|
|
text = '\n'.join([
|
|
'! comment', '[Adblock Plus]', '||ads.example.com^', '@@||good.example.com^',
|
|
'||tracker.net^$important', '||x.com^$denyallow=y.com', '/regex/', '||*.wild.com^',
|
|
'example.org##.banner',
|
|
])
|
|
got = list(blocklists.parse('adblock', text))
|
|
assert ('ads.example.com', '||ads.example.com^', False) in got
|
|
assert ('good.example.com', '@@||good.example.com^', True) in got
|
|
assert any(d == 'tracker.net' for d, _, _ in got)
|
|
assert not any(d in ('x.com', 'wild.com') for d, _, _ in got)
|
|
assert len(got) == 3
|
|
|
|
|
|
def test_hosts_parse():
|
|
text = '127.0.0.1 localhost\n0.0.0.0 0.0.0.0\n0.0.0.0 ads.example.com # c\n# 0.0.0.0 off.com\n::1 ip6-localhost'
|
|
assert [d for d, _, _ in blocklists.parse('hosts', text)] == ['ads.example.com']
|
|
|
|
|
|
def test_parents():
|
|
assert blocklists.parents('a.b.example.com') == ['a.b.example.com', 'b.example.com', 'example.com']
|
|
|
|
|
|
def test_lookup_semantics(tmp_path, monkeypatch):
|
|
import sqlite3
|
|
db = tmp_path / 'bl.sqlite'
|
|
con = sqlite3.connect(db)
|
|
con.execute('CREATE TABLE rules (list TEXT, domain TEXT, rule TEXT, exc INTEGER)')
|
|
con.execute('CREATE TABLE lists (id TEXT PRIMARY KEY, entries INTEGER, fetched REAL, error TEXT)')
|
|
con.executemany('INSERT INTO rules VALUES (?,?,?,?)', [
|
|
('adguard_dns', 'example.com', '||example.com^', 0),
|
|
('adguard_dns', 'ok.example.com', '@@||ok.example.com^', 1),
|
|
('stevenblack', 'example.com', '0.0.0.0 example.com', 0),
|
|
])
|
|
con.commit()
|
|
con.close()
|
|
monkeypatch.setattr(blocklists, 'DB_PATH', str(db))
|
|
by = lambda d: {l['id']: l['blocked'] for l in blocklists.check_lists(d)['lists']}
|
|
assert by('example.com')['adguard_dns'] and by('example.com')['stevenblack']
|
|
sub = by('ads.example.com')
|
|
assert sub['adguard_dns'] is True # adblock: parent rule covers subdomains
|
|
assert sub['stevenblack'] is False # hosts: exact match only
|
|
assert by('ok.example.com')['adguard_dns'] is False # @@ exception wins
|
|
|
|
|
|
def test_spf_counting(monkeypatch):
|
|
from zmtools import security
|
|
zone = {'a.com': ['v=spf1 include:b.com mx -all'], 'b.com': ['v=spf1 a include:c.com ~all'],
|
|
'c.com': ['v=spf1 ip4:192.0.2.0/24 -all']}
|
|
monkeypatch.setattr(dnsutil, 'txt_values', lambda n: zone.get(n, []))
|
|
count, broken = security.spf_lookups(zone['a.com'][0])
|
|
assert count == 4 and broken == []
|
|
count, broken = security.spf_lookups('v=spf1 include:missing.com -all')
|
|
assert count == 1 and broken == ['missing.com']
|