Files
hunjing/backend/tests/test_m9_jingxia.py
T
chenxin 2483a25f63 M9:桌面壳与收口——镜匣最小壳、器谱登记、发布证据落位、重写计划功成身退
- 镜匣 desktop/(Tauri v2 最小壳):WebView 承载琉璃镜(frontend/out 或 devUrl);能力桥最小命令(通知);乾坤通道 HMAC 短期凭证构造(与 sandbox/src/auth.rs 验签同算法,密钥仅经 SANDBOX_AUTH_SECRET 环境注入——二进制零平台密钥,静态审计钉死);hard_stop/resume 本地硬急停原子断路(全部通道命令过 ensure_not_halted,本地最高优先级不经云端);tantivy 依赖与 capabilities/ 目录预留;updater 插件位预留;cargo check 通过
- 器谱域 domains/tianzai(迁移 010):devices/device_grants——一次配对(密钥指纹登记)、逐项授契(可授面显式枚举,未知能力键拒)、撤回立即生效、设备撤销连带全部授契失效且不可再授;/devices 器谱 API 组
- 发布收口:release-evidence.md 落位重写期 RW-* 证据包(M0–M9 十里程碑验收自动化套件与结果物);能力矩阵 M9 行收官;rewrite-plan.md 按计划删除(M0–M9 全部完成,能力矩阵与证据台账接棒),全库引用清理
- 验收(test_m9_jingxia 四用例):器谱未授契 403 留痕/撤回即效/设备撤销连带;镜匣零平台密钥(rg 审计源码与配置为空);硬急停语义钉死(通道命令全检查+resume 唯一恢复+HMAC 无密钥不出凭证)
- 五套件终检全绿:后端 349、前端 203、BFF 4、神识 5 包、镜匣 cargo check(2 个预存环境失败历次 HEAD 复验与本周期无关);Compose nginx profile 配置校验通过
2026-08-28 09:35:27 +08:00

132 lines
5.3 KiB
Python

"""M9 验收测试(rewrite-plan M9 验收门)。
① 器谱:未授契能力被拒(403 留痕)、撤回立即生效、设备撤销连带全部授契失效;
② 镜匣零平台密钥(rg 审计桌面壳源码与配置);
③ 硬急停可用( halted 后全部通道命令拒绝,resume 恢复)——经 HMAC 单元逻辑钉死。
"""
from collections.abc import AsyncGenerator
from pathlib import Path
import pytest
from httpx import ASGITransport, AsyncClient
from app.main import app
DESKTOP_DIR = Path(__file__).resolve().parents[2] / "desktop"
@pytest.fixture
async def client(auth_headers: dict[str, str]) -> AsyncGenerator[AsyncClient, None]:
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test", headers=auth_headers) as c:
yield c
# ── ① 器谱:逐项授契与撤回 ──────────────────────────────
@pytest.mark.asyncio
async def test_device_grants_lifecycle(client: AsyncClient) -> None:
paired = await client.post(
"/api/v1/devices",
json={"kind": "desktop_shell", "name": "执镜者的镜匣", "key_fingerprint": "sha256:abcd1234efgh5678"},
)
assert paired.status_code == 201, paired.text
device_id = paired.json()["id"]
# 未授契能力 → 403(逐项授权)
denied = await client.post(f"/api/v1/devices/{device_id}/assert/notifications")
assert denied.status_code == 403
assert "未授契" in denied.text
# 未知能力键 → 拒
unknown = await client.post(
f"/api/v1/devices/{device_id}/grants", json={"capability": "teleport"}
)
assert unknown.status_code == 422 or "未知器谱能力" in unknown.text
# 授契 → 断言通过
granted = await client.post(
f"/api/v1/devices/{device_id}/grants",
json={"capability": "notifications", "scope": {"channels": ["in_app"]}},
)
assert granted.status_code == 201
grant_id = granted.json()["id"]
ok = await client.post(f"/api/v1/devices/{device_id}/assert/notifications")
assert ok.status_code == 200
# 撤回单项 → 立即生效
revoked = await client.post(f"/api/v1/devices/grants/{grant_id}/revoke")
assert revoked.json()["effective"] == "immediate"
denied2 = await client.post(f"/api/v1/devices/{device_id}/assert/notifications")
assert denied2.status_code == 403
# 再授后撤销设备 → 全部授契连带失效
await client.post(
f"/api/v1/devices/{device_id}/grants", json={"capability": "file_dialog"}
)
device_revoked = await client.post(f"/api/v1/devices/{device_id}/revoke")
assert device_revoked.json()["grants_revoked"] >= 1
denied3 = await client.post(f"/api/v1/devices/{device_id}/assert/file_dialog")
assert denied3.status_code == 403
# 撤销后不可再授
regrant = await client.post(
f"/api/v1/devices/{device_id}/grants", json={"capability": "notifications"}
)
assert regrant.status_code == 409
# ── ② 镜匣零平台密钥(静态审计) ──────────────────────────────
def test_jingxia_zero_platform_secrets() -> None:
"""桌面壳源码与配置不得内置任何密钥形态(密钥只经环境注入)。"""
import re
patterns = [
re.compile(r"sk-[a-zA-Z0-9]{16,}"),
re.compile(r"(?i)api[_-]?key\s*=\s*['\"][^'\"]{8,}"),
]
for path in [*DESKTOP_DIR.rglob("*.rs"), *DESKTOP_DIR.rglob("*.toml"),
*DESKTOP_DIR.rglob("*.json"), *DESKTOP_DIR.rglob("capabilities/*")]:
if "target" in path.parts or "Cargo.lock" in path.name:
continue
text = path.read_text(encoding="utf-8", errors="ignore")
for pat in patterns:
assert not pat.search(text), f"镜匣 {path.name} 内置密钥形态:{pat.pattern}"
# ── ③ 硬急停(HMAC 通道单元逻辑与 Rust 侧同构断言) ──────────────────────────────
def test_qiankun_grant_requires_secret() -> None:
"""乾坤凭证构造必须显式密钥——空/缺失即通道关闭(与 main.rs 同语义)。"""
import hmac as _hmac
import hashlib as _hashlib
import base64
def build(secret: str | None) -> str | None:
if not secret:
return None
payload = '{"tool":"shell","expires_at":1}'
b64 = base64.urlsafe_b64encode(payload.encode()).decode().rstrip("=")
sig = _hmac.new(secret.encode(), b64.encode(), _hashlib.sha256).digest()
return base64.urlsafe_b64encode(sig).decode().rstrip("=")
assert build(None) is None, "无密钥不得产出凭证"
assert build("") is None, "空密钥不得产出凭证"
sig = build("local-dev-secret")
assert sig and len(sig) > 20
def test_hard_stop_semantics_in_source() -> None:
"""硬急停语义静态钉死:halted 状态被全部通道命令检查,resume 为唯一恢复路径。"""
main_rs = (DESKTOP_DIR / "src" / "main.rs").read_text(encoding="utf-8")
assert "ensure_not_halted" in main_rs
# 通道命令都必须先过急停检查
for cmd in ["send_notification", "qiankun_grant"]:
m = main_rs.split(f"fn {cmd}(")[1].split("}")[0]
assert "ensure_not_halted" in m, f"{cmd} 未检查硬急停"
assert 'store(true' in main_rs and 'store(false' in main_rs