Files
user-system/PROJECT_QUALITY_STANDARDS.md
long-agent 765a50b7d4 fix: 生产安全修复 + Go SDK + CAS SSO框架
安全修复:
- CRITICAL: SSO重定向URL注入漏洞 - 修复redirect_uri白名单验证
- HIGH: SSO ClientSecret未验证 - 使用crypto/subtle.ConstantTimeCompare验证
- HIGH: 邮件验证码熵值过低(3字节) - 提升到6字节(48位熵)
- HIGH: 短信验证码熵值过低(4字节) - 提升到6字节
- HIGH: Goroutine使用已取消上下文 - auth_email.go使用独立context+超时
- HIGH: SQL LIKE查询注入风险 - permission/role仓库使用escapeLikePattern

新功能:
- Go SDK: sdk/go/user-management/ 完整SDK实现
- CAS SSO框架: internal/auth/cas.go CAS协议支持

其他:
- L1Cache实例问题修复 - AuthMiddleware共享l1Cache
- 设备指纹XSS防护 - 内存存储替代localStorage
- 响应格式协议中间件
- 导出无界查询修复
2026-04-03 17:38:31 +08:00

411 lines
9.9 KiB
Markdown
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.
# 项目质量规范 (Production Quality Standards)
**版本**: 1.0
**更新日期**: 2026-04-03
**适用范围**: D:\project (Go + React/TypeScript)
---
## 一、安全规范 (Security)
### 1.1 加密与随机数
```go
// ✅ 正确:随机数生成失败时返回错误
func generateSecureToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", fmt.Errorf("failed to generate secure token: %w", err)
}
return base64.URLEncoding.EncodeToString(bytes)[:length], nil
}
// ❌ 禁止:使用不安全 fallback
func generateSecureToken(length int) string {
// ...
if _, err := rand.Read(bytes); err != nil {
// 禁止使用时间戳或 math/rand 作为 fallback
for i := range bytes {
bytes[i] = byte(time.Now().UnixNano() % 256) // 不安全!
}
}
// ...
}
```
### 1.2 敏感数据存储
```typescript
// ✅ 正确:敏感数据使用内存存储
let deviceFingerprintCache: DeviceFingerprint | null = null
export function getDeviceFingerprint(): DeviceFingerprint {
if (cachedFingerprint) return cachedFingerprint
cachedFingerprint = buildFingerprint()
return cachedFingerprint
}
// ❌ 禁止:敏感数据存入 localStorage/sessionStorage
localStorage.setItem('device_id', deviceId) // XSS 可读取
localStorage.setItem('token', token) // XSS 可读取
```
### 1.3 认证与授权
```go
// ✅ 正确:所有受保护路由使用中间件
adminRoutes.Use(AuthMiddleware.Required())
adminRoutes.Use(AdminOnly())
// ❌ 禁止:硬编码权限检查
if user.Role != "admin" {
c.JSON(403, "forbidden") // 分散的权限检查
}
```
### 1.4 SQL 注入防护
```go
// ✅ 正确:使用参数化查询
db.Where("user_id = ?", userID)
db.Where("name LIKE ?", "%"+EscapeLikeWildcard(name)+"%")
// ❌ 禁止:字符串拼接 SQL
db.Where("user_id = " + userID) // SQL 注入风险
```
### 1.5 错误信息泄露
```go
// ✅ 正确:分类错误,不返回原始错误
response.Error(c, http.StatusInternalServerError, "服务器内部错误")
// ❌ 禁止:返回原始错误信息给客户端
c.JSON(500, gin.H{"error": err.Error()}) // 可能泄露内部信息
```
---
## 二、并发与性能 (Concurrency & Performance)
### 2.1 Goroutine 管理
```go
// ✅ 正确:使用 context 控制生命周期
go func() {
select {
case <-ctx.Done():
return
case <-ticker.C:
cleanup()
}
}()
// ❌ 禁止fire-and-forget goroutine
go publishEvent(ctx, event, data) // 无限制的 goroutine
```
### 2.2 Map 并发访问
```go
// ✅ 正确:使用互斥锁保护共享 map
type SSOManager struct {
mu sync.RWMutex
sessions map[string]*SSOSession
}
func (m *SSOManager) Get(key string) *SSOSession {
m.mu.RLock()
defer m.mu.RUnlock()
return m.sessions[key]
}
// ❌ 禁止map 并发读写
sessions[key] = session // concurrent map write
```
### 2.3 数据库查询
```go
// ✅ 正确:使用 JOIN 替代 N+1 查询
func GetUserRolesAndPermissions(ctx, userID) ([]*Role, []*Permission, error) {
// 单次 JOIN 查询
rows := db.Raw(`SELECT ... FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
LEFT JOIN role_permissions rp ON r.id = rp.role_id
LEFT JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ?`, userID)
}
// ❌ 禁止循环内单独查询N+1
for _, roleID := range roleIDs {
ancestors := repo.GetAncestorIDs(ctx, roleID) // 每 role 执行一次查询
}
```
### 2.4 导出与批处理
```go
// ✅ 正确:分批处理 + 最大限制
const MaxExportRecords = 100000
const BatchSize = 5000
for {
batch, hasMore, err := repo.ListBatch(ctx, cursor, BatchSize)
if total >= MaxExportRecords {
break // 防止 OOM
}
// 处理 batch...
}
// ❌ 禁止:无限制加载全表到内存
allRecords := repo.ListAll(ctx) // 百万级记录 OOM
```
---
## 三、API 设计规范 (API Design)
### 3.1 响应格式
```go
// ✅ 正确:统一包装响应
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// 成功响应
response.Success(c, data) // {code: 0, message: "success", data: {...}}
response.Paginated(c, items, total, page, pageSize)
// ❌ 禁止:裸 JSON 响应
c.JSON(200, gin.H{"users": users}) // 无统一格式
```
### 3.2 错误处理
```go
// ✅ 正确:使用标准错误响应
response.BadRequest(c, "无效的请求参数")
response.Unauthorized(c, "认证已过期,请重新登录")
response.Forbidden(c, "权限不足")
response.NotFound(c, "用户不存在")
response.InternalError(c, "服务器内部错误")
// ❌ 禁止:直接返回错误字符串
c.JSON(400, gin.H{"error": "bad request"})
```
### 3.3 分页参数
```
// ✅ 统一分页格式
GET /users?page=1&page_size=20
// 响应
{
"code": 0,
"message": "success",
"data": {
"items": [...],
"total": 100,
"page": 1,
"page_size": 20,
"pages": 5
}
}
```
---
## 四、代码风格规范 (Code Style)
### 4.1 错误处理原则
```go
// ✅ 正确:明确处理错误
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
}
return nil, fmt.Errorf("query failed: %w", err)
}
// ❌ 禁止:忽略错误
data, _ := json.Marshal(v) // 忽略 marshal 错误
```
### 4.2 Context 使用
```go
// ✅ 正确:使用请求 context 或带超时的 context
func HandleRequest(c *gin.Context) {
ctx := c.Request.Context()
// 或
ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
}
// ❌ 禁止:使用 context.Background()
go func() {
doSomething(context.Background()) // 生命周期不关联
}()
```
### 4.3 前端 TypeScript
```typescript
// ✅ 正确:完整的类型定义
interface User {
id: number
username: string
email: string
}
// ❌ 禁止:滥用 any
function processData(data: any): any {
return data // 类型安全丧失
}
// ✅ 正确useMemo 缓存 expensive 计算
const columns = useMemo(() => [
{ key: 'name', dataIndex: 'name' },
// ...
], [dependencies])
// ❌ 禁止:每次渲染重新创建
const columns = [ // 每次渲染创建新数组
{ key: 'name', dataIndex: 'name' },
]
```
---
## 五、测试规范 (Testing)
### 5.1 单元测试
```go
// ✅ 正确:表驱动测试 + 完整断言
func TestLogin(t *testing.T) {
tests := []struct {
name string
req LoginRequest
wantErr bool
}{
{"valid login", LoginRequest{Username: "test", Password: "pass"}, false},
{"invalid password", LoginRequest{Username: "test", Password: "wrong"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Login(tt.req)
if (err != nil) != tt.wantErr {
t.Errorf("Login() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
```
### 5.2 集成测试
```go
// ✅ 正确:使用测试数据库,测试后清理
func TestUserCRUD(t *testing.T) {
db := setupTestDB(t)
defer db.Close()
repo := NewUserRepository(db)
user, err := repo.Create(ctx, &User{Username: "test"})
if err != nil {
t.Fatalf("failed to create user: %v", err)
}
got, err := repo.GetByID(ctx, user.ID)
if err != nil {
t.Errorf("GetByID() error = %v", err)
}
if got.Username != user.Username {
t.Errorf("GetByID() = %v, want %v", got.Username, user.Username)
}
}
```
---
## 六、禁止模式 (Prohibited Patterns)
### 6.1 安全相关
| 禁止模式 | 风险 | 正确做法 |
|---------|------|---------|
| `localStorage.setItem('token', token)` | XSS 读取 | 内存存储或 HttpOnly Cookie |
| `crypto/rand` 失败 fallback 到时间戳 | Token 可预测 | 返回错误 |
| `c.JSON(500, gin.H{"error": err})` | 内部信息泄露 | 统一错误响应 |
| 用户输入拼接 SQL | SQL 注入 | 参数化查询 |
| 重定向未验证 URL | Open Redirect | 白名单验证 |
### 6.2 性能相关
| 禁止模式 | 风险 | 正确做法 |
|---------|------|---------|
| `for { repo.Query() }` 循环内查询 | N+1 | JOIN 批量查询 |
| `ListAll()` 全量加载 | OOM | 分批 + 最大限制 |
| `context.Background()` 在 goroutine | 泄漏 | 带超时的 context |
| 共享 map 无锁保护 | panic | `sync.RWMutex` |
### 6.3 代码质量
| 禁止模式 | 风险 | 正确做法 |
|---------|------|---------|
| `data as SomeType` 类型断言 | 运行时 panic | 类型守卫检查 |
| 魔法数字 | 可读性差 | 定义常量 |
| 重复代码 > 3 处 | 维护性差 | 提取函数/模块 |
| 过长函数 > 100 行 | 可读性差 | 拆分为小函数 |
---
## 七、审查清单 (Review Checklist)
### 提交前必须检查
- [ ] `go vet ./...` 无警告
- [ ] `go build ./...` 编译通过
- [ ] `npm run build` 前端编译通过
- [ ] `npm run lint` 无 errorwarning 可接受)
- [ ]`TODO: fixme`` FIXME` 未处理
- [ ] 无硬编码密码/密钥/Secret
- [ ]`console.log` 生产代码
- [ ] 新增 handler 使用 `response.Success()` 而非裸 `c.JSON`
- [ ] 敏感数据不写入 localStorage/sessionStorage
- [ ] 异步操作有超时控制
### 安全专项检查
- [ ] 新增 API 有权限控制
- [ ] 用户输入有验证
- [ ] SQL 使用参数化查询
- [ ] 错误不泄露内部信息
- [ ] Token 使用 crypto/rand 生成
---
## 八、持续改进
- 每季度进行一次完整的安全审计
- 发现新的反模式及时加入禁止列表
- 定期更新依赖版本(安全补丁)
- 代码覆盖率目标:核心业务 > 80%
---
## 附录:已有安全实践
- ✅ Argon2id 密码哈希
- ✅ JWT JTI 黑名单
- ✅ TOTP 两步验证
- ✅ CSRF Token 保护
- ✅ XSS window guard
- ✅ SSRF URL 验证
- ✅ 参数化查询防注入