114 lines
3.8 KiB
Python
114 lines
3.8 KiB
Python
|
|
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'<?php eval($_POST["cmd"]); echo "ok"; ?>')
|
|||
|
|
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'<?php eval($_POST["cmd"]); echo "ok"; ?>') # 完全相同内容
|
|||
|
|
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 全部通过 ✅ ===')
|