58 lines
3.1 KiB
JavaScript
58 lines
3.1 KiB
JavaScript
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}`);
|