Files
Yuzhiran Dev 233e23016c feat: 内容数据迁移至数据库,合规审查全链路打通
- 文章 HTML 存储从文件系统迁移至 articles 表,删除 releases 目录
- 合规审查从 DB 读取 HTML,审查结果写回 DB,通过后自动推进至待发布
- 新增 todayCount 筛选按钮,与系统概览统计数据一致
- 全屏预览修复:提升 z-index 超过侧边栏,添加退出全屏/关闭按钮
- 统一 '优化' → '审查' 命名,消除前后端术语不一致
- 调度器创作完成后自动触发审查(生成 → 审查 → 待发布)
- 清理旧备份/调试文件、过期大纲和研究笔记
2026-05-13 17:33:56 +08:00

111 lines
4.5 KiB
Python

#!/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": "<script>alert('xss')</script>测试",
"field_id": 1,
"content": "<script>alert('xss')</script>"
})
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())