feat: добавление STRICT режима (более строгий MASKED)
This commit is contained in:
+12
-1
@@ -2,7 +2,7 @@
|
|||||||
"default": {
|
"default": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"demask": true,
|
"demask": true,
|
||||||
"maskMode": "MASK",
|
"maskMode": "TOKEN",
|
||||||
"types": ["*"],
|
"types": ["*"],
|
||||||
"requireCompanion": [
|
"requireCompanion": [
|
||||||
"CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY",
|
"CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY",
|
||||||
@@ -27,5 +27,16 @@
|
|||||||
"demask": false,
|
"demask": false,
|
||||||
"maskMode": "MASK",
|
"maskMode": "MASK",
|
||||||
"types": ["*"]
|
"types": ["*"]
|
||||||
|
},
|
||||||
|
"strict": {
|
||||||
|
"enabled": true,
|
||||||
|
"demask": true,
|
||||||
|
"maskMode": "STRICT",
|
||||||
|
"types": ["*"],
|
||||||
|
"requireCompanion": [
|
||||||
|
"CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY",
|
||||||
|
"ACCOUNT_NUMBER", "BIK", "OGRN", "OGRNIP", "KPP",
|
||||||
|
"INCOME", "BIOMETRIC"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,10 +158,6 @@ public class Pipeline {
|
|||||||
|
|
||||||
recordLatency("mask", policy.name(), started);
|
recordLatency("mask", policy.name(), started);
|
||||||
logFindings(policy.name(), payloadId, payload.length(), spans);
|
logFindings(policy.name(), payloadId, payload.length(), spans);
|
||||||
// Только для отладки формата входных данных на DEBUG — на INFO не активен.
|
|
||||||
// На боевом прогоне (нагрузочное тестирование) уровень DEBUG должен быть выключен:
|
|
||||||
// сюда попадают сырые ПД, что напрямую нарушает требование ТЗ не логировать значения.
|
|
||||||
LOG.debug("payload_id={} raw={}", payloadId, payload);
|
|
||||||
return masked;
|
return masked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,15 @@ public enum MaskMode {
|
|||||||
/** Звёздочки с сохранением длины и разделителей: {@code 45** ****56}. */
|
/** Звёздочки с сохранением длины и разделителей: {@code 45** ****56}. */
|
||||||
MASK,
|
MASK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Звёздочки без исключений: каждый тип закрывается целиком, даже те, что в
|
||||||
|
* {@link #MASK} частично открыты (края номера) или превращаются в инициалы
|
||||||
|
* (ФИО {@code Иванов Иван Иванович} → {@code ******* **** *********}, не
|
||||||
|
* {@code И. И. И.} — инициалы всё ещё выдают число слов и первую букву
|
||||||
|
* каждого).
|
||||||
|
*/
|
||||||
|
STRICT,
|
||||||
|
|
||||||
/** Порядковый токен: {@code [FIO_1]}. Компактно и однозначно обратимо. */
|
/** Порядковый токен: {@code [FIO_1]}. Компактно и однозначно обратимо. */
|
||||||
TOKEN,
|
TOKEN,
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ public class Masker {
|
|||||||
public String mask(String type, String value, MaskMode mode, MaskContext context) {
|
public String mask(String type, String value, MaskMode mode, MaskContext context) {
|
||||||
return switch (mode) {
|
return switch (mode) {
|
||||||
case MASK -> BY_TYPE.getOrDefault(type, Strategies::stars).apply(value);
|
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 TOKEN -> context.resolve(type, value, (t, n) -> "[" + t + "_" + n + "]");
|
||||||
case SYNTHETIC -> context.resolve(type, value, (t, n) -> Synthetic.forType(t, value, 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));
|
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
|
@Test
|
||||||
void tokenModeNumbersEachType() {
|
void tokenModeNumbersEachType() {
|
||||||
String masked = mask(MaskMode.TOKEN, "Клиент Иванов Иван Иванович, почта ivan@mail.ru");
|
String masked = mask(MaskMode.TOKEN, "Клиент Иванов Иван Иванович, почта ivan@mail.ru");
|
||||||
|
|||||||
Reference in New Issue
Block a user