"""Network status must be scoped, bounded and honest about reachability.""" import json import unittest from unittest.mock import patch,Mock import network_status as ns from test_app import app import test_households as fixture class NetworkTests(unittest.TestCase): setUp=fixture.HouseholdTests.setUp stop_server=fixture.HouseholdTests.stop_server request=fixture.HouseholdTests.request def test_scoped_checks_cannot_probe_an_arbitrary_host(self): device=app.INVENTORY.save(dict(name='网关',deviceType='路由器',managementHost='192.168.8.1'),self.owner) endpoint='/api/devices/network?source=device&id='+device['id'] with patch.object(ns,'probe',return_value=dict(host='192.168.8.1',reachable=True,ports=[80])) as probe: result=self.request(endpoint)[1] self.assertTrue(result['targets'][0]['reachable']) self.assertEqual(result['links'][0]['url'],'http://192.168.8.1/') self.assertEqual(self.request(endpoint,self.other)[0],404) self.assertEqual(self.request(endpoint,self.members[self.aid])[0],403) self.assertEqual(self.request('/api/devices/network?source=device&host=example.com')[0],404) again=self.request(endpoint)[1];self.assertTrue(again['cached']);self.assertEqual(probe.call_count,1) def test_record_changes_invalidate_cache(self): device=app.INVENTORY.save(dict(name='网关',deviceType='路由器',managementHost='192.168.8.1'),self.owner) with patch.object(ns,'probe',side_effect=lambda host:dict(host=host,ports=[],reachable=False)) as probe: query=dict(source='device',id=device['id']) app.NETWORK.read(self.owner,query) app.INVENTORY.save(dict(device,managementHost='192.168.8.2'),self.owner) result=app.NETWORK.read(self.owner,query) self.assertEqual(probe.call_count,2) self.assertFalse(result['targets'][0]['reachable']) self.assertFalse(result['links'][0]['verified']) self.assertEqual(result['links'][0]['url'],'http://192.168.8.2/') class NetworkUnitTests(unittest.TestCase): def test_rejects_local_metadata_and_unsafe_links(self): for host in ['127.0.0.1','169.254.169.254','::1','0.0.0.0','224.0.0.1']: self.assertFalse(ns.safe_ip(host)) self.assertTrue(ns.safe_ip('192.168.8.1'));self.assertTrue(ns.safe_ip('100.64.1.2')) for host in ['127.0.0.1','javascript:alert(1)','user:secret@example.com','example.com/path']: self.assertEqual(ns.management_links([dict(host=host,kind='local',ports=[80])]),[]) with patch.object(ns.socket,'getaddrinfo',return_value=[(2,1,6,'',('127.0.0.1',0))]),patch.object(ns.socket,'create_connection') as connect: self.assertFalse(ns.probe('malicious.example')['reachable']);connect.assert_not_called() def test_peer_online_does_not_prove_subnet_route(self): status={'Peer':{'abc':{'DNSName':'gateway.example.ts.net.','TailscaleIPs':['100.64.1.2'],'Online':True,'Active':False,'AllowedIPs':['100.64.1.2/32'],'PrimaryRoutes':None}}} with patch.object(ns.subprocess,'run',return_value=Mock(stdout=json.dumps(status))): peer=ns.tail_peer('100.64.1.2','192.168.8.0/24') self.assertTrue(peer['online']);self.assertFalse(peer['subnetAvailable']);self.assertNotIn('Peer',peer) status['Peer']['abc']['PrimaryRoutes']=['192.168.8.0/24'] with patch.object(ns.subprocess,'run',return_value=Mock(stdout=json.dumps(status))): self.assertTrue(ns.tail_peer('gateway.example.ts.net','192.168.8.0/24')['subnetAvailable']) with patch.object(ns.subprocess,'run',side_effect=FileNotFoundError):self.assertFalse(ns.tail_peer('100.64.1.2','')['available']) def test_uses_detected_web_port_and_ipv6_brackets(self): links=ns.management_links([dict(host='192.168.3.151',kind='local',ports=[8790]),dict(host='fd7a:115c:a1e0::1',kind='tailscale',ports=[443])]) self.assertEqual(links[0]['url'],'http://192.168.3.151:8790/') self.assertEqual(links[1]['url'],'https://[fd7a:115c:a1e0::1]/') if __name__=='__main__':unittest.main()