"""Scope gating: in-scope kept, out-of-scope dropped, exclusions honoured.""" from __future__ import annotations from pathlib import Path from recon_triage.scope import Scope FIXTURES = Path(__file__).parent / "fixtures" def _scope() -> Scope: return Scope.load(FIXTURES / "scope.yaml") def test_in_scope_domain_and_subdomains(): sc = _scope() assert sc.is_in_scope("example.com") assert sc.is_in_scope("api.example.com") assert sc.is_in_scope("deep.api.example.com") def test_out_of_scope_domain_dropped(): sc = _scope() assert not sc.is_in_scope("external.notmine.com") assert not sc.is_in_scope("notexample.com") def test_exclusion_precedence(): sc = _scope() # internal-only.example.com is under example.com but explicitly excluded. assert not sc.is_in_scope("internal-only.example.com") def test_cidr_matching(): sc = _scope() assert sc.is_in_scope("93.184.216.34") assert not sc.is_in_scope("198.51.100.7") def test_filter_drops_out_of_scope(): sc = _scope() given = ["api.example.com", "external.notmine.com", "93.184.216.34", "8.8.8.8"] kept = sc.filter(given) assert kept == ["api.example.com", "93.184.216.34"] def test_substring_domain_not_matched(): # "fakeexample.com" must not match "example.com" sc = _scope() assert not sc.is_in_scope("fakeexample.com") def test_invalid_cidr_ignored(): sc = Scope.from_dict({"in_scope_cidrs": ["not-a-cidr", "10.0.0.0/8"]}) assert sc.cidrs == ["10.0.0.0/8"] assert sc.is_in_scope("10.1.2.3")