feat: добавление STRICT режима (более строгий MASKED)

This commit is contained in:
Максименко Никита Владимирович
2026-09-23 18:20:54 +03:00
parent 0b9371e4f2
commit c209c4a366
5 changed files with 34 additions and 5 deletions
@@ -158,10 +158,6 @@ public class Pipeline {
recordLatency("mask", policy.name(), started);
logFindings(policy.name(), payloadId, payload.length(), spans);
// Только для отладки формата входных данных на DEBUG — на INFO не активен.
// На боевом прогоне (нагрузочное тестирование) уровень DEBUG должен быть выключен:
// сюда попадают сырые ПД, что напрямую нарушает требование ТЗ не логировать значения.
LOG.debug("payload_id={} raw={}", payloadId, payload);
return masked;
}
@@ -6,6 +6,15 @@ public enum MaskMode {
/** Звёздочки с сохранением длины и разделителей: {@code 45** ****56}. */
MASK,
/**
* Звёздочки без исключений: каждый тип закрывается целиком, даже те, что в
* {@link #MASK} частично открыты (края номера) или превращаются в инициалы
* (ФИО {@code Иванов Иван Иванович} → {@code ******* **** *********}, не
* {@code И. И. И.} — инициалы всё ещё выдают число слов и первую букву
* каждого).
*/
STRICT,
/** Порядковый токен: {@code [FIO_1]}. Компактно и однозначно обратимо. */
TOKEN,
@@ -75,6 +75,9 @@ public class Masker {
public String mask(String type, String value, MaskMode mode, MaskContext context) {
return switch (mode) {
case MASK -> BY_TYPE.getOrDefault(type, Strategies::stars).apply(value);
// STRICT игнорирует BY_TYPE целиком — ни один тип не открывает края
// и ФИО не превращается в инициалы, только сплошные звёздочки.
case STRICT -> Strategies.stars(value);
case TOKEN -> context.resolve(type, value, (t, n) -> "[" + t + "_" + n + "]");
case SYNTHETIC -> context.resolve(type, value, (t, n) -> Synthetic.forType(t, value, n));
};
@@ -33,6 +33,16 @@ class MaskModeTest {
return pipeline.process(text, UUID.randomUUID().toString(), policy(mode));
}
@Test
void strictModeHidesEverythingIncludingFio() {
String masked = mask(MaskMode.STRICT, "Клиент Иванов Иван Иванович, паспорт 4509 123456");
assertFalse(masked.contains("Иванов"), masked);
assertFalse(masked.contains("И. И. И."), "STRICT не должен превращать ФИО в инициалы: " + masked);
assertTrue(masked.contains("****** **** ********"), "ожидались звёздочки по длине каждого слова: " + masked);
assertTrue(masked.contains("**** ******"), "край паспорта не должен открываться: " + masked);
}
@Test
void tokenModeNumbersEachType() {
String masked = mask(MaskMode.TOKEN, "Клиент Иванов Иван Иванович, почта ivan@mail.ru");