fix: P0/P1 安全与质量缺陷修复 — 10 项 QA 审查问题解决
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled

P0 安全修复:
- tenant_rls: SQL 拼接改为参数化查询防止注入
- follow_up_service: UUID SQL 拼接改为参数化原生查询
- RLS 策略: 新迁移移除空字符串绕过条件
- SSE 消息推送: token 键名 'token' → 'access_token' 修复
- rate_limit: 登录端点 Redis 不可达时 fail-close

P1 质量修复:
- 小程序缓存清理: preservedKeys 补全认证键名
- 小程序 token 刷新: 失败时清除所有认证数据
- 小程序 401: redirectTo → reLaunch 兼容 tabBar
- 集成测试: 信号量限制并行数据库创建(4个)
- change_password: 乐观锁 version 硬编码 → 动态递增

测试: 516 全部通过 (含 153 集成测试)
This commit is contained in:
iven
2026-04-28 00:57:41 +08:00
parent 3d34e021a9
commit 9dd6095e77
11 changed files with 391 additions and 21 deletions

View File

@@ -87,6 +87,7 @@ mod m20260427_000084_domain_events_cleanup;
mod m20260427_000085_processed_events;
mod m20260427_000086_enable_rls_all_tables;
mod m20260427_000087_audit_logs_hash_chain;
mod m20260428_000088_rls_policy_strict;
pub struct Migrator;
@@ -181,6 +182,7 @@ impl MigratorTrait for Migrator {
Box::new(m20260427_000085_processed_events::Migration),
Box::new(m20260427_000086_enable_rls_all_tables::Migration),
Box::new(m20260427_000087_audit_logs_hash_chain::Migration),
Box::new(m20260428_000088_rls_policy_strict::Migration),
]
}
}

View File

@@ -0,0 +1,80 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
// 替换所有表的 RLS 策略:移除空字符串绕过条件
// 原策略允许 current_setting(...) = '' 时通过(绕过 RLS现在要求变量已设置且匹配
conn.execute_unprepared(
r#"
DO $$
DECLARE
tbl TEXT;
BEGIN
FOR tbl IN
SELECT c.table_name FROM information_schema.columns c
JOIN information_schema.tables t
ON c.table_name = t.table_name AND c.table_schema = t.table_schema
WHERE c.column_name = 'tenant_id'
AND c.table_schema = 'public'
AND t.table_type = 'BASE TABLE'
ORDER BY c.table_name
LOOP
EXECUTE format('DROP POLICY IF EXISTS tenant_isolation ON %I', tbl);
EXECUTE format(
'CREATE POLICY tenant_isolation ON %I USING (
current_setting(''app.current_tenant_id'', true) != ''''
AND tenant_id = current_setting(''app.current_tenant_id'', true)::uuid
)',
tbl
);
END LOOP;
END;
$$;
"#,
).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let conn = manager.get_connection();
// 回滚:恢复允许空字符串绕过的原策略
conn.execute_unprepared(
r#"
DO $$
DECLARE
tbl TEXT;
BEGIN
FOR tbl IN
SELECT c.table_name FROM information_schema.columns c
JOIN information_schema.tables t
ON c.table_name = t.table_name AND c.table_schema = t.table_schema
WHERE c.column_name = 'tenant_id'
AND c.table_schema = 'public'
AND t.table_type = 'BASE TABLE'
ORDER BY c.table_name
LOOP
EXECUTE format('DROP POLICY IF EXISTS tenant_isolation ON %I', tbl);
EXECUTE format(
'CREATE POLICY tenant_isolation ON %I USING (
current_setting(''app.current_tenant_id'', true) = ''''
OR tenant_id = current_setting(''app.current_tenant_id'', true)::uuid
)',
tbl
);
END LOOP;
END;
$$;
"#,
).await?;
Ok(())
}
}