60 lines
3.2 KiB
Python
60 lines
3.2 KiB
Python
import configparser
|
|
import socket
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
import psutil
|
|
from scripts import local_webcam as webcam
|
|
|
|
|
|
class LocalWebcamTests(unittest.TestCase):
|
|
def test_config_disables_audio_recording_and_other_protocols(self):
|
|
config = webcam.make_config('[hook]\non_server_started=http://remote/\n[protocol]\nenable_mp4=1\n',
|
|
'test-secret', 'http://127.0.0.1:12345/publish/test')
|
|
self.assertEqual(config['general']['listen_ip'], '127.0.0.1')
|
|
for name in ('enable_audio', 'add_mute_audio', 'enable_mp4', 'enable_hls', 'enable_hls_fmp4'):
|
|
self.assertEqual(config['protocol'][name], '0')
|
|
for section in ('rtc', 'rtp_proxy', 'srt', 'shell', 'onvif'):
|
|
self.assertEqual(config[section]['port'], '0')
|
|
self.assertNotIn('on_server_started', config['hook'])
|
|
self.assertEqual(config['rtsp']['rtpTransportType'], '0')
|
|
self.assertEqual(config['ffmpeg']['bin'], '')
|
|
|
|
def test_command_is_video_only_and_fixed_loopback_destination(self):
|
|
command = webcam.capture_command('ffmpeg.exe')
|
|
self.assertIn('-an', command)
|
|
self.assertEqual(command[command.index('-i') + 1], 'video=Integrated Camera')
|
|
self.assertEqual(command[command.index('-map') + 1], '0:v:0')
|
|
self.assertEqual(command[-1], 'rtmp://127.0.0.1:10935/live/laptop_cam')
|
|
self.assertNotIn('audio=', ' '.join(command))
|
|
|
|
def test_publish_gate_rejects_other_streams_and_remote_publishers(self):
|
|
data = {'app': 'live', 'stream': 'laptop_cam', 'schema': 'rtmp', 'ip': '127.0.0.1'}
|
|
self.assertTrue(webcam.allowed_publish(data))
|
|
for key, value in [('stream', 'other'), ('ip', '192.168.1.2'), ('app', 'rtp'), ('schema', 'rtsp')]:
|
|
self.assertFalse(webcam.allowed_publish({**data, key: value}))
|
|
|
|
def test_network_guard_rejects_exposure_udp_and_outbound_connections(self):
|
|
proc = mock.Mock()
|
|
local = SimpleNamespace(ip='127.0.0.1', port=10935)
|
|
good = SimpleNamespace(type=socket.SOCK_STREAM, laddr=local, raddr=(), status=psutil.CONN_LISTEN)
|
|
proc.connections.return_value = [good]
|
|
self.assertEqual(webcam.check_connections([proc], {10935}), {10935})
|
|
proc.connections.return_value = [SimpleNamespace(
|
|
type=socket.SOCK_STREAM, laddr=SimpleNamespace(ip='::1', port=60001),
|
|
raddr=SimpleNamespace(ip='::1', port=60002), status=psutil.CONN_ESTABLISHED)]
|
|
self.assertEqual(webcam.check_connections([proc], {10935}), set())
|
|
for bad in [dict(type=socket.SOCK_DGRAM),
|
|
dict(laddr=SimpleNamespace(ip='0.0.0.0', port=10935)),
|
|
dict(raddr=SimpleNamespace(ip='8.8.8.8', port=443))]:
|
|
proc.connections.return_value = [SimpleNamespace(**{**vars(good), **bad})]
|
|
with self.assertRaises(RuntimeError):
|
|
webcam.check_connections([proc], {10935})
|
|
|
|
def test_webcam_does_not_bootstrap_full_service_manager(self):
|
|
source = webcam.SCRIPT.read_text(encoding='utf-8')
|
|
self.assertNotIn('get_service_manager().start()', source)
|
|
self.assertNotIn('shell=True', source)
|
|
self.assertIn('ServiceLeaderLock()', source)
|