Files
i/server/test/middleware.csrf.test.js
T
wsh5485 65b0bb04f8 feat: import CarLog v2.8 code + dev plan
把 CarLog v2.8 全套源码 + 配置导入到 i 仓库作为 baseline:
- server/src/ (13 个路由 + middleware + services + config)
- server/migrations/ (0001~0018 共 18 个迁移 + mysql)
- server/test/ (12 文件 101 测试)
- client/src/ (20 个 view + components + stores + api + composables)
- client/public/ + client/scripts/
- 全部配置文件 (.editorconfig, .eslintrc.json, .prettierrc.json, vitest.config.js, lighthouserc.json, .pa11yci.json, package.json, carlog-init.sql)
- .husky/pre-commit (git hooks)
- docs/install/ (宝塔部署文档)

不含:
- node_modules/ (本地 npm install)
- .env (敏感, 走 .env.example)
- *.zip / *.log / *.sqlite / .DS_Store

新增文档 docs/DEV-PLAN.md:
- Phase 1: 平台基座 (019 migration + 3 个 platform 路由 + 3 个 view)
- Phase 2: CarLog 子系统化 (后端 routes/ → subsystems/carlog/ + 前端 views/ → views/subsystems/carlog/ + 元数据驱动菜单)
- Phase 3: 验证 (测试 + E2E + DB 完整性)
- 交付清单 + commit 模板 + 给 Mavis review 的材料

后续 Trae 实施, 提交后我 code review + 跑测试。
2026-06-20 22:30:19 +08:00

123 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// server/test/middleware.csrf.test.js
// 测试 server/src/middleware/csrf.js 的所有分支
import { describe, it, expect, vi } from 'vitest';
import { requireCsrf } from '../src/middleware/csrf.js';
function mockRes() {
return {
statusCode: 200,
body: null,
status(c) { this.statusCode = c; return this; },
json(b) { this.body = b; return this; },
};
}
describe('middleware/requireCsrf', () => {
it('GET 请求直接放行', () => {
const req = { method: 'GET', session: { csrfToken: 'abc' } };
const res = mockRes();
const next = vi.fn();
requireCsrf(req, res, next);
expect(next).toHaveBeenCalledOnce();
expect(res.statusCode).toBe(200);
});
it('HEAD 请求直接放行', () => {
const req = { method: 'HEAD', session: { csrfToken: 'abc' } };
const next = vi.fn();
requireCsrf(req, mockRes(), next);
expect(next).toHaveBeenCalledOnce();
});
it('OPTIONS 请求直接放行', () => {
const req = { method: 'OPTIONS', session: { csrfToken: 'abc' } };
const next = vi.fn();
requireCsrf(req, mockRes(), next);
expect(next).toHaveBeenCalledOnce();
});
it('POST 没有 session.csrfToken → 403', () => {
const req = { method: 'POST', body: { csrf_token: 'abc' } };
const res = mockRes();
const next = vi.fn();
requireCsrf(req, res, next);
expect(next).not.toHaveBeenCalled();
expect(res.statusCode).toBe(403);
expect(res.body.error.code).toBe('CSRF');
});
it('POST session 没 csrfToken 但用户提交了 → 403', () => {
const req = { method: 'POST', body: { csrf_token: 'abc' }, session: {} };
const res = mockRes();
requireCsrf(req, res, vi.fn());
expect(res.statusCode).toBe(403);
});
it('POST 错误 token → 403', () => {
const req = {
method: 'POST',
body: { csrf_token: 'wrong' },
session: { csrfToken: 'right' },
};
const res = mockRes();
requireCsrf(req, res, vi.fn());
expect(res.statusCode).toBe(403);
expect(res.body.error.message).toMatch(/校验失败/);
});
it('POST 正确 tokenbody)→ 放行', () => {
const req = {
method: 'POST',
body: { csrf_token: 'good' },
session: { csrfToken: 'good' },
};
const next = vi.fn();
requireCsrf(req, mockRes(), next);
expect(next).toHaveBeenCalledOnce();
});
it('POST 正确 tokenheader)→ 放行', () => {
const req = {
method: 'POST',
body: {},
headers: { 'x-csrf-token': 'good' },
session: { csrfToken: 'good' },
};
const next = vi.fn();
requireCsrf(req, mockRes(), next);
expect(next).toHaveBeenCalledOnce();
});
it('PUT 也需校验', () => {
const req = { method: 'PUT', body: {}, headers: {}, session: { csrfToken: 'x' } };
const res = mockRes();
requireCsrf(req, res, vi.fn());
expect(res.statusCode).toBe(403);
});
it('DELETE 也需校验', () => {
const req = { method: 'DELETE', body: {}, headers: {}, session: { csrfToken: 'x' } };
const res = mockRes();
requireCsrf(req, res, vi.fn());
expect(res.statusCode).toBe(403);
});
it('长度为 0 的 token(防御性)→ 抛错或 403', () => {
const req = {
method: 'POST',
body: { csrf_token: '' },
session: { csrfToken: 'good' },
};
const res = mockRes();
// 防御:空 token 走 timingSafeEqual 会抛错,被 require() 内部 try/catch 吞掉
// 期望:不调用 next
try {
requireCsrf(req, res, vi.fn());
expect(res.statusCode).toBe(403);
} catch {
// 也接受抛错(更安全的行为)
expect(true).toBe(true);
}
});
});