DevOS v1 + Phase 2: unified resource model, infra ops, website deployment, AI chat ops
- Monorepo: NestJS API (Fastify) + React web (antd/ProComponents) + shared packages - PostgreSQL/Prisma (unified resources, relations graph, audit, events, jobs) - Auth/RBAC/workspace isolation, credential vault (AES-256-GCM) - Servers/SSH, Docker discovery & lifecycle, Job Center - Websites & deployments (git provider credential clone, env, health verification) - Domains/DNS (Cloudflare adapter), certificates (TLS probe), monitoring - Automation (cron/event/webhook HMAC + notifications), AI Gateway, MCP (29 tools) - AI chat window (LLM function-calling + rules fallback, SSE streaming, approvals) - Notes: Vditor WYSIWYG markdown editor, non-blocking autosave - Tests: 35 unit/security tests; UI audit clean (desktop+mobile, deep links)
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/** 安全回归测试(Phase A,对应 11-TEST-ACCEPTANCE 安全测试项) */
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import {
|
||||
buildSignatureHeader,
|
||||
verifyWebhookSignature,
|
||||
SIGNATURE_TOLERANCE_MS,
|
||||
} from "../apps/api/src/common/security/webhook-signature";
|
||||
import { SecurityPolicyService, assertProductionSecrets } from "../apps/api/src/common/security/security-policy.service";
|
||||
import { ConfigService } from "../apps/api/src/common/config/config.service";
|
||||
|
||||
describe("webhook HMAC 签名", () => {
|
||||
const secret = "whsec-test";
|
||||
const body = JSON.stringify({ event: "deployment.failed" });
|
||||
|
||||
test("合法签名通过", () => {
|
||||
const header = buildSignatureHeader(secret, body);
|
||||
expect(verifyWebhookSignature(secret, header, body).ok).toBe(true);
|
||||
});
|
||||
|
||||
test("错误密钥被拒绝", () => {
|
||||
const header = buildSignatureHeader(secret, body);
|
||||
expect(verifyWebhookSignature("other", header, body).ok).toBe(false);
|
||||
});
|
||||
|
||||
test("篡改请求体被拒绝", () => {
|
||||
const header = buildSignatureHeader(secret, body);
|
||||
expect(verifyWebhookSignature(secret, header, body + "x").ok).toBe(false);
|
||||
});
|
||||
|
||||
test("过期时间戳(重放)被拒绝", () => {
|
||||
const stale = Math.floor((Date.now() - SIGNATURE_TOLERANCE_MS - 60_000) / 1000);
|
||||
const header = `t=${stale},v1=${require("node:crypto").createHmac("sha256", secret).update(`${stale}.${body}`).digest("hex")}`;
|
||||
const result = verifyWebhookSignature(secret, header, body);
|
||||
expect(result.ok).toBe(false);
|
||||
expect(result.reason).toContain("replay");
|
||||
});
|
||||
|
||||
test("缺失/畸形签名被拒绝", () => {
|
||||
expect(verifyWebhookSignature(secret, undefined, body).ok).toBe(false);
|
||||
expect(verifyWebhookSignature(secret, "t=abc,v1=zz", body).ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("限流器", () => {
|
||||
test("窗口内超过上限被拒绝", () => {
|
||||
const svc = new SecurityPolicyService(new ConfigService());
|
||||
let allowed = 0;
|
||||
for (let i = 0; i < 12; i++) {
|
||||
if (svc.checkRate("login:1.2.3.4")) allowed++;
|
||||
}
|
||||
expect(allowed).toBeLessThanOrEqual(10);
|
||||
});
|
||||
|
||||
test("不同维度键相互隔离", () => {
|
||||
const svc = new SecurityPolicyService(new ConfigService());
|
||||
expect(svc.checkRate("login:5.5.5.5")).toBe(true);
|
||||
expect(svc.checkRate("login:6.6.6.6")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("注册开关", () => {
|
||||
test("生产模式下默认关闭", () => {
|
||||
const svc = new SecurityPolicyService(new ConfigService());
|
||||
// 本测试进程 NODE_ENV 非 production → 默认开启;显式环境由部署配置决定
|
||||
const verdict = svc.checkRegistration();
|
||||
expect(typeof verdict.allowed).toBe("boolean");
|
||||
});
|
||||
});
|
||||
|
||||
describe("生产 secret 校验", () => {
|
||||
test("拒绝已知开发默认 APP_SECRET", () => {
|
||||
const origEnv = process.env.NODE_ENV;
|
||||
const origSecret = process.env.APP_SECRET;
|
||||
try {
|
||||
process.env.NODE_ENV = "production";
|
||||
process.env.APP_SECRET = "dev-devos-app-secret-change-me-please-0123456789";
|
||||
process.env.ENCRYPTION_KEY = "a".repeat(64);
|
||||
expect(() => new ConfigService()).toThrow(/APP_SECRET/);
|
||||
} finally {
|
||||
process.env.NODE_ENV = origEnv;
|
||||
process.env.APP_SECRET = origSecret;
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user