"""对齐 Muse P4 测试:浏览器完全对齐。 只测本轨交付物(chv-guest/chv_browser_guard.py、 chv-cdp-broker.py、chv-browser.py、browser-blocklist.txt、 build.sh 干跑、buildinfo.py 内容项)。全部用假 socket/假进程/ 假 playwright,绝不连真实网络、不起真实 VM、不改生产行为。 """ import ast import importlib.util import json import os import shutil import socket import subprocess import sys import tempfile import threading import unittest from pathlib import Path from unittest import mock ROOT = Path(__file__).resolve().parents[2] def _load_script(name): """把 chv-guest 下的独立脚本当模块加载(无包结构)。""" path = ROOT / "chv-guest" / name spec = importlib.util.spec_from_file_location( "chv_guest_p4_" + name[:-3].replace("-", "_"), str(path)) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module GUARD = _load_script("chv_browser_guard.py") CDP = _load_script("chv-cdp-broker.py") BROWSER = _load_script("chv-browser.py") BUILDINFO = _load_script("buildinfo.py") class GuardTest(unittest.TestCase): def test_validate_url(self): self.assertTrue(GUARD.validate_url("https://example.com")[0]) self.assertTrue(GUARD.validate_url("http://example.com/x")[0]) for bad in ("ftp://example.com", "file:///etc/passwd", "https://localhost:8080/", "http://127.0.0.1/", "https://192.168.1.1/", "https://10.0.0.5/", "https://172.17.0.2/", "", None): ok, _ = GUARD.validate_url(bad) self.assertFalse(ok, bad) def test_blocklist_load_and_match(self): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "blocklist.txt") with open(path, "w") as fh: fh.write("# comment\n\nmalware.test\n.phishing.test\n") entries = GUARD.load_blocklist(path) self.assertEqual(entries, {"malware.test", ".phishing.test"}) self.assertTrue(GUARD.check_blocklist("malware.test", entries)) self.assertTrue(GUARD.check_blocklist("a.malware.test", entries)) self.assertTrue(GUARD.check_blocklist("x.phishing.test", entries)) self.assertFalse(GUARD.check_blocklist("example.com", entries)) self.assertFalse(GUARD.check_blocklist("notmalware.test", entries)) self.assertEqual(GUARD.load_blocklist("/nonexistent/x"), set()) def test_truncate_a11y_small_tree_untouched(self): tree = {"role": "WebArea", "name": "Example", "children": [{"role": "heading", "name": "Hi"}]} out, truncated = GUARD.truncate_a11y(tree) self.assertFalse(truncated) self.assertEqual(out, tree) def test_truncate_a11y_char_cap(self): tree = {"role": "WebArea", "name": "x" * 70000, "children": []} out, truncated = GUARD.truncate_a11y(tree) self.assertTrue(truncated) self.assertTrue(out.get("truncated_chars")) def test_sanitize_error_strips_html_and_secrets(self): err = RuntimeError( "timeout token=sk-secret-abc") clean = GUARD.sanitize_error(err) self.assertIn("RuntimeError", clean) self.assertNotIn(" sk-secret-abc") resp = broker.open("https://example.com") self.assertFalse(resp["ok"]) self.assertNotIn("