import sys, os, time, hashlib, tempfile sys.path.insert(0, '.') # DB_URL 由 .env 或 compose environment 提供 os.environ.setdefault('MESHSAFE_MASTER_KEY', 'Dt0ktRs8SjEH8VxWZJDX125NV1qiII88HoiXZPhWers=') os.environ.setdefault('MESHSAFE_POLICY_FILE', 'config/policy.yaml') os.environ.setdefault('MESHSAFE_ADMIN_PASSWORD', 'admin123') # 等 uvicorn 起来 for _ in range(15): try: import requests r = requests.get('http://127.0.0.1:8443/health', timeout=2) if r.status_code == 200: break except Exception: pass time.sleep(0.5) else: print('❌ uvicorn 没起来') sys.exit(1) print('✅ uvicorn 已就绪') import requests from app.services.scanner import Scanner from app.services.policy import PolicyStore from app.db import SessionLocal from app.models import ScanTask, ScanFinding, IdempotencyRecord ps = PolicyStore(policy_file='config/policy.yaml') scanner = Scanner(ps) # ---- 1. 控制台登录 + 页面 ---- s = requests.Session() r = s.post('http://127.0.0.1:8443/console/login', data={'username': 'admin', 'password': 'admin123'}) print(f'\n1. login: {r.status_code}') r = s.get('http://127.0.0.1:8443/console/') print(f'2. dashboard: {r.status_code}') r = s.get('http://127.0.0.1:8443/console/policy') print(f'3. policy: {r.status_code} yara_tab={"panel-yara" in r.text}') r = s.get('http://127.0.0.1:8443/console/tasks') print(f'4. tasks: {r.status_code}') r = requests.get('http://127.0.0.1:8443/health') d = r.json() print(f'5. /health: status={d["status"]} yara_ok={d["engines"]["yara"]["ok"]} clamav_stale={d["engines"]["clamav"].get("stale_warning")}') # ---- 2. 正常扫描(首次)---- tmp1 = tempfile.NamedTemporaryFile(delete=False, suffix='.php') tmp1.write(b'') tmp1.close() t1 = time.perf_counter() outcome1 = scanner.scan_file( file_path=tmp1.name, original_filename='shell.php', task_id='e2e-idem-001', channel='e2e', client_id='test', ) first_ms = int((time.perf_counter() - t1) * 1000) os.unlink(tmp1.name) print(f'\n6. 首次扫描: verdict={outcome1.verdict} score={outcome1.score} findings={len(outcome1.findings)} took={first_ms}ms idem={outcome1.meta.get("idempotent", False)}') # 验证 idempotency_records 表写入 db = SessionLocal() idem_count = db.query(IdempotencyRecord).count() print(f' idempotency_records 表行数: {idem_count}') db.close() # ---- 3. 幂等二次扫描(同内容 + 同策略)---- tmp2 = tempfile.NamedTemporaryFile(delete=False, suffix='.php') tmp2.write(b'') # 完全相同内容 tmp2.close() t2 = time.perf_counter() outcome2 = scanner.scan_file( file_path=tmp2.name, original_filename='shell.php', task_id='e2e-idem-002', channel='e2e', client_id='test', ) idem_ms = int((time.perf_counter() - t2) * 1000) os.unlink(tmp2.name) print(f'\n7. 幂等扫描: verdict={outcome2.verdict} findings={len(outcome2.findings)} took={idem_ms}ms idem={outcome2.meta.get("idempotent", False)}') speedup = first_ms / max(idem_ms, 1) print(f' 提速: {speedup:.1f}x(幂等跳过了引擎扫描)') assert outcome2.meta.get('idempotent') is True, '❌ 幂等未命中' assert idem_ms < first_ms, '❌ 幂等应该更快' print(' ✅ 幂等生效') # ---- 4. 限流(内存模式,client_id=test)---- # 扫描接口需要签名,这里用 memory 限流直接测底层 from app.security.ratelimit import _memory_check # 填满窗口 for _ in range(60): _memory_check('test:ratelimit_test', 60) ok = _memory_check('test:ratelimit_test', 60) print(f'\n8. 限流测试(61/60): {"通过" if ok else "被拒绝 ✅"}') assert ok is False, '❌ 限流应拒绝第 61 次' print(' ✅ 限流生效') # 清内存窗口,给后续 run 不受影响 import app.security.ratelimit as rl with rl._MEM_LOCK: rl._MEM_BUCKET.clear() print('\n=== E2E 全部通过 ✅ ===')