import assert from "node:assert/strict"; const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787"; async function request(path, options = {}) { const response = await fetch(`${api}${path}`, { ...options, headers: { "content-type": "application/json", ...(options.headers || {}) } }); const payload = await response.json().catch(() => ({})); return { response, payload }; } async function login(email, password = "Demo@123456") { const result = await request("/api/auth/login", { method: "POST", body: JSON.stringify({ email, password }) }); assert.equal(result.response.status, 200, `${email} 登录失败`); assert.ok(result.payload.session?.token, `${email} 未返回登录会话`); return { authorization: `Bearer ${result.payload.session.token}` }; } const ownerHeaders = await login("producer@local.test"); const writerHeaders = await login("writer@local.test"); const badLogin = await request("/api/auth/login", { method: "POST", body: JSON.stringify({ email: "writer@local.test", password: "security-smoke-invalid-password" }) }); assert.equal(badLogin.response.status, 401, "错误密码必须被拒绝"); const ownEvents = await request("/api/auth/security-events", { headers: writerHeaders }); assert.equal(ownEvents.response.status, 200, "普通用户无法读取自己的安全事件"); assert.ok(Array.isArray(ownEvents.payload.events), "个人安全事件接口必须返回 events 数组"); assert.ok(ownEvents.payload.events.some((event) => event.eventType === "login.success"), "个人安全事件缺少登录成功记录"); assert.ok(ownEvents.payload.events.some((event) => event.eventType === "login.failure"), "个人安全事件缺少登录失败记录"); assert.ok(!JSON.stringify(ownEvents.payload.events).includes("security-smoke-invalid-password"), "安全事件不能记录密码"); const eventKeys = new Set(); for (const event of ownEvents.payload.events) { for (const key of Object.keys(event.metadata || {})) eventKeys.add(key); } assert.ok(![...eventKeys].some((key) => /(sessionToken|challengeToken|password|otp|secret|api.?key)/i.test(key)), "安全事件不能返回敏感字段"); const ordinarySystemDenied = await request("/api/system/security-events", { headers: writerHeaders }); assert.equal(ordinarySystemDenied.response.status, 403, "普通用户不能查看全局安全事件"); assert.equal(ordinarySystemDenied.payload.error, "system_admin_required", "系统安全事件权限错误码不稳定"); const systemEvents = await request("/api/system/security-events?userId=u-writer", { headers: ownerHeaders }); assert.equal(systemEvents.response.status, 200, "系统管理员无法查看指定用户安全事件"); assert.ok(systemEvents.payload.events.some((event) => event.userId === "u-writer"), "系统安全事件未按指定用户过滤"); const detail = await request("/api/system/users/u-writer", { headers: ownerHeaders }); assert.equal(detail.response.status, 200, "系统管理员无法读取用户详情"); assert.ok(Array.isArray(detail.payload.securityEvents), "用户详情缺少账号安全事件台账"); console.log(`security events smoke passed: ${api}`);