Files
Aq-Accounting-Spring/src/test/java/com/accounting/service/SensitiveDataMaskerTest.java
T
2026-09-23 17:16:40 +08:00

101 lines
3.6 KiB
Java
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.
package com.accounting.service;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 脱敏器的单测 —— 这是整个 AI 链路里最不能出错的一个类,
* 用例按「用户笔记里真实出现过的形态」来写。
*/
class SensitiveDataMaskerTest {
private final SensitiveDataMasker masker = new SensitiveDataMasker();
@Test
@DisplayName("IPv4 地址被脱敏")
void masksIpv4() {
String out = masker.mask("服务器 IP 是 155.103.159.109,SSH 端口 31022");
assertFalse(out.contains("155.103.159.109"));
assertTrue(out.contains("[REDACTED_IP_"));
assertTrue(out.contains("31022")); // 端口不算敏感,保留
}
@Test
@DisplayName("password 键值对:键名保留,值被替换")
void masksPasswordKv() {
String out = masker.mask("MySQL root 密码:mysql_jP65Fc,登录后记得改");
assertFalse(out.contains("mysql_jP65Fc"));
assertTrue(out.contains("密码:[REDACTED_SECRET_"));
}
@Test
@DisplayName("password= 等号形式同样生效")
void masksPasswordWithEquals() {
String out = masker.mask("jdbc:url?user=root&password=mySecret123");
assertFalse(out.contains("mySecret123"));
}
@Test
@DisplayName("PEM 私钥整块被脱敏")
void masksPrivateKeyBlock() {
String key = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ "b3BlbnNzaC1rZXktdjEAAAAA\n"
+ "b3BlbnNzaC1rZXktdjEAAAAA\n"
+ "-----END OPENSSH PRIVATE KEY-----";
String out = masker.mask("私钥如下:\n" + key + "\n记得保管好");
assertFalse(out.contains("b3BlbnNzaC1rZXktdjEAAAAA"));
assertTrue(out.contains("[REDACTED_KEY_1]"));
}
@Test
@DisplayName("无键名的 AK/SK 长随机串被脱敏")
void masksBareAccessToken() {
String out = masker.mask("access key 是 LTAI5tDCJuB9YgLx4KeJwc9C 备用");
assertFalse(out.contains("LTAI5tDCJuB9YgLx4KeJwc9C"));
}
@Test
@DisplayName("同一个值在多次出现时映射到同一个占位符")
void keepsPlaceholderConsistent() {
String out = masker.mask("密码:abc12345,重复一下密码:abc12345");
String first = out.substring(out.indexOf("[REDACTED_"));
assertEquals(1, countOccurrences(out, "[REDACTED_SECRET_1]"), "同一值应得到同一占位符: " + out);
assertFalse(out.contains(first + "["));
}
@Test
@DisplayName("普通内容不误伤:中文、短数字、文件名都保留")
void keepsNormalContent() {
String text = "笔记在 migration_add_note.sql,共 3 篇,端口 31022,金额 ¥134.00";
assertEquals(text, masker.mask(text));
}
@Test
@DisplayName("「密码:无」这种非秘密不被误伤")
void keepsShortNonSecretValues() {
String out = masker.mask("密码:无");
assertTrue(out.contains("密码:无"));
}
@Test
@DisplayName("null 和空串原样返回")
void handlesNullAndEmpty() {
assertEquals(null, masker.mask(null));
assertEquals("", masker.mask(""));
}
private int countOccurrences(String text, String target) {
int count = 0;
int idx = 0;
while ((idx = text.indexOf(target, idx)) >= 0) {
count++;
idx += target.length();
}
return count;
}
}