fix(health): 穷尽审计修复 — 权限同步/编译错误/前端bug/审计日志
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / security-audit (push) Has been cancelled

审计发现并修复的问题:

HIGH:
- H1: ConsultationDetail 使用 getSession(id) 替代错误的列表搜索
- H2: SessionResp 添加 version/updated_at 字段
- H3: 移除 FollowUpRecordList 调用不存在的导出端点
- H4: 新增 articles.ts 前端 API 模块

MEDIUM:
- M1: article delete 添加乐观锁 (expected_version)
- M2: 取消预约排班释放传播错误 (log::warn -> ?)
- M3: FollowUpTaskList 日期格式 Dayjs -> string
- M4: 补充 15 个缺失审计日志

LOW:
- L1: 替换 follow_up_service 中的 .unwrap()
- L2: PatientListItem 添加 version 字段

CRITICAL (新发现):
- 权限未同步: 健康模块 14 个权限从未写入数据库,添加启动时自动同步
- migration 表名错误: patients -> patient
- 编译错误: health_trend entity 未导入, ToPrimitive trait 未导入
- HealthError 缺少 From<AppError> 实现
This commit is contained in:
iven
2026-04-25 08:58:58 +08:00
parent 9ffb938128
commit 07f4ba41ba
31 changed files with 3373 additions and 445 deletions

View File

@@ -88,3 +88,82 @@ impl HealthCrypto {
hex::encode(mac.finalize().into_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_crypto() -> HealthCrypto {
HealthCrypto::dev_default()
}
#[test]
fn encrypt_decrypt_roundtrip() {
let crypto = test_crypto();
let plaintext = "110101199001011234";
let encrypted = crypto.encrypt(plaintext).unwrap();
let decrypted = crypto.decrypt(&encrypted).unwrap();
assert_eq!(plaintext, decrypted);
}
#[test]
fn encrypt_produces_different_ciphertexts() {
let crypto = test_crypto();
let plaintext = "110101199001011234";
let e1 = crypto.encrypt(plaintext).unwrap();
let e2 = crypto.encrypt(plaintext).unwrap();
assert_ne!(e1, e2); // 不同 nonce 导致不同密文
}
#[test]
fn decrypt_wrong_key_fails() {
let crypto1 = HealthCrypto::dev_default();
let hex_key = "00".repeat(32); // 64 个 0
let crypto2 = HealthCrypto::from_keys(&hex_key, &hex_key).unwrap();
let encrypted = crypto1.encrypt("test").unwrap();
assert!(crypto2.decrypt(&encrypted).is_err());
}
#[test]
fn hmac_hash_deterministic() {
let crypto = test_crypto();
let hash1 = crypto.hmac_hash("110101199001011234");
let hash2 = crypto.hmac_hash("110101199001011234");
assert_eq!(hash1, hash2);
}
#[test]
fn hmac_hash_different_inputs() {
let crypto = test_crypto();
let h1 = crypto.hmac_hash("123456789012345678");
let h2 = crypto.hmac_hash("987654321098765432");
assert_ne!(h1, h2);
}
#[test]
fn encrypt_empty_string() {
let crypto = test_crypto();
let encrypted = crypto.encrypt("").unwrap();
let decrypted = crypto.decrypt(&encrypted).unwrap();
assert_eq!("", decrypted);
}
#[test]
fn decrypt_too_short_fails() {
let crypto = test_crypto();
let short = BASE64.encode(b"short");
assert!(crypto.decrypt(&short).is_err());
}
#[test]
fn from_keys_invalid_hex() {
let result = HealthCrypto::from_keys("not-hex", "not-hex");
assert!(result.is_err());
}
#[test]
fn from_keys_wrong_length() {
let result = HealthCrypto::from_keys("ab", "cd");
assert!(result.is_err());
}
}