#!/usr/bin/env python3 """安全与错误处理测试""" import sys import os import json BASE_URL = "http://localhost:8001" try: import requests except ImportError: os.system("pip install requests -q") import requests def get_token(): r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "admin", "password": "admin123"}) return r.json()["token"] if r.status_code == 200 else None def h(token): return {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} def check(name, condition, details=""): icon = "✅" if condition else "❌" print(f" {icon} {name}{' (' + details + ')' if details else ''}") return condition def section(name): print(f"\n{'='*50}") print(f" {name}") print(f"{'='*50}") def main(): print("=" * 60) print("宇之然平台 - 安全与错误处理测试") print("=" * 60) results = [] section("🔐 认证安全") r = requests.get(f"{BASE_URL}/api/topics") results.append(check("无 token 请求被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}")) r = requests.get(f"{BASE_URL}/api/topics", headers={"Authorization": "Bearer invalid_token"}) results.append(check("无效 token 被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}")) r = requests.get(f"{BASE_URL}/api/topics", headers={"Authorization": ""}) results.append(check("空 token 被拒绝", r.status_code == 401 or r.status_code == 403, f"HTTP {r.status_code}")) r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "admin", "password": "wrong"}) results.append(check("错误密码登录失败", r.status_code == 401, f"HTTP {r.status_code}")) r = requests.post(f"{BASE_URL}/api/auth/login", json={"username": "nonexistent", "password": "test"}) results.append(check("不存在的用户登录失败", r.status_code == 401, f"HTTP {r.status_code}")) section("🛡️ CORS 安全") r = requests.get(f"{BASE_URL}/api/system/status", headers=h(get_token())) cors_origin = r.headers.get("access-control-allow-origin", "") results.append(check("CORS 不返回通配符 *", cors_origin != "*", f"Origin: {cors_origin or '(none)'}")) section("⚠️ 错误处理") r = requests.get(f"{BASE_URL}/api/nonexistent", headers=h(get_token())) results.append(check("不存在路由返回 404", r.status_code == 404, f"HTTP {r.status_code}")) r = requests.get(f"{BASE_URL}/api/topics/99999", headers=h(get_token())) results.append(check("不存在资源返回 404", r.status_code == 404, f"HTTP {r.status_code}")) r = requests.post(f"{BASE_URL}/api/topics", headers=h(get_token()), json={}) results.append(check("空数据创建返回验证错误", r.status_code == 422, f"HTTP {r.status_code}")) r = requests.post(f"{BASE_URL}/api/topics", headers=h(get_token()), json={"title": ""}) results.append(check("空标题返回验证错误", r.status_code == 422, f"HTTP {r.status_code}")) section("📄 内容安全") token = get_token() r = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json={ "title": "测试", "field_id": 1, "content": "" }) results.append(check("含 HTML 的内容可创建", r.ok, f"HTTP {r.status_code}")) if r.ok: new_id = r.json().get("id") if new_id: r2 = requests.delete(f"{BASE_URL}/api/topics/{new_id}", headers=h(token)) results.append(check("清理测试数据", r2.ok or r2.status_code == 404)) section("🔄 幂等性") payload = {"title": "幂等测试", "field_id": 1} r1 = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json=payload) r2 = requests.post(f"{BASE_URL}/api/topics", headers=h(token), json=payload) results.append(check("重复创建不报错", r1.ok and r2.ok, f"第1次:{r1.status_code} 第2次:{r2.status_code}")) if r1.ok: del_id = r1.json().get("id") if del_id: requests.delete(f"{BASE_URL}/api/topics/{del_id}", headers=h(token)) if r2.ok and r2.json().get("id") != (r1.json().get("id") if r1.ok else None): del_id2 = r2.json().get("id") if del_id2: requests.delete(f"{BASE_URL}/api/topics/{del_id2}", headers=h(token)) print(f"\n{'='*60}") passed = sum(results) total = len(results) print(f"测试结果: {passed}/{total} 通过 ({passed*100//total}%)") print("=" * 60) return 0 if passed == total else 1 if __name__ == "__main__": sys.exit(main())