From 4bdb03341b96a0850acf476530274c019c4c7de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D0=BA=D1=81=D0=B8=D0=BC=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=92=D0=BB?= =?UTF-8?q?=D0=B0=D0=B4=D0=B8=D0=BC=D0=B8=D1=80=D0=BE=D0=B2=D0=B8=D1=87?= Date: Wed, 23 Sep 2026 18:20:54 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20STRICT=20=D1=80=D0=B5=D0=B6=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=20(=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B3=D0=B8=D0=B9=20MASKED)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/systems.json | 13 ++++++++++++- src/main/java/ru/pdguard/core/Pipeline.java | 4 ---- src/main/java/ru/pdguard/mask/MaskMode.java | 9 +++++++++ src/main/java/ru/pdguard/mask/Masker.java | 3 +++ src/test/java/ru/pdguard/MaskModeTest.java | 10 ++++++++++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/config/systems.json b/config/systems.json index 5f825c4..ace4733 100644 --- a/config/systems.json +++ b/config/systems.json @@ -2,7 +2,7 @@ "default": { "enabled": true, "demask": true, - "maskMode": "MASK", + "maskMode": "TOKEN", "types": ["*"], "requireCompanion": [ "CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY", @@ -27,5 +27,16 @@ "demask": false, "maskMode": "MASK", "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" + ] } } diff --git a/src/main/java/ru/pdguard/core/Pipeline.java b/src/main/java/ru/pdguard/core/Pipeline.java index 30fb1ac..a3562da 100644 --- a/src/main/java/ru/pdguard/core/Pipeline.java +++ b/src/main/java/ru/pdguard/core/Pipeline.java @@ -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; } diff --git a/src/main/java/ru/pdguard/mask/MaskMode.java b/src/main/java/ru/pdguard/mask/MaskMode.java index a7345bc..c05a603 100644 --- a/src/main/java/ru/pdguard/mask/MaskMode.java +++ b/src/main/java/ru/pdguard/mask/MaskMode.java @@ -6,6 +6,15 @@ public enum MaskMode { /** Звёздочки с сохранением длины и разделителей: {@code 45** ****56}. */ MASK, + /** + * Звёздочки без исключений: каждый тип закрывается целиком, даже те, что в + * {@link #MASK} частично открыты (края номера) или превращаются в инициалы + * (ФИО {@code Иванов Иван Иванович} → {@code ******* **** *********}, не + * {@code И. И. И.} — инициалы всё ещё выдают число слов и первую букву + * каждого). + */ + STRICT, + /** Порядковый токен: {@code [FIO_1]}. Компактно и однозначно обратимо. */ TOKEN, diff --git a/src/main/java/ru/pdguard/mask/Masker.java b/src/main/java/ru/pdguard/mask/Masker.java index 27d21d3..83b93d2 100644 --- a/src/main/java/ru/pdguard/mask/Masker.java +++ b/src/main/java/ru/pdguard/mask/Masker.java @@ -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)); }; diff --git a/src/test/java/ru/pdguard/MaskModeTest.java b/src/test/java/ru/pdguard/MaskModeTest.java index 6afabb2..c7c0dad 100644 --- a/src/test/java/ru/pdguard/MaskModeTest.java +++ b/src/test/java/ru/pdguard/MaskModeTest.java @@ -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");