feat: extend requireCompanion to BIRTH_PLACE and ADDRESS_COUNTRY

Same reasoning as the existing CVV/PIN/DATE entries: a bare mention
("Экскурсия в Нижний Новгород", "цены выросли в Казахстане") isn't
personal data on its own, only alongside some other PD that ties it to
a specific person. The mechanism was already generic — this only adds
two more types to the default set.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Максименко Никита Владимирович
2026-09-21 21:29:57 +03:00
co-authored by Claude Sonnet 5
parent 7dd66d45bd
commit e52e9550e6
3 changed files with 33 additions and 5 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"demask": true,
"maskMode": "MASK",
"types": ["*"],
"requireCompanion": ["CVV", "PIN", "DATE"]
"requireCompanion": ["CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY"]
},
"crm": {
"enabled": true,
@@ -13,7 +13,9 @@ import java.util.Set;
* @param types типы ПД к маскированию; {@code "*"} — все известные
* @param requireCompanion типы, которые маскируются только вместе с ПД другого типа:
* пин-код сам по себе безвреден, пин-код рядом с номером
* карты — уже нет; то же для даты без якорного слова
* карты — уже нет; то же для даты без якорного слова, места
* рождения («Нижний Новгород» в рассказе о городе — не адрес
* клиента) и страны («цены выросли в Казахстане» — не гражданство)
*/
public record SystemPolicy(boolean enabled, boolean demask, MaskMode maskMode,
Set<String> types, Set<String> requireCompanion) {
@@ -22,7 +24,8 @@ public record SystemPolicy(boolean enabled, boolean demask, MaskMode maskMode,
/** Политика по умолчанию: маскируем всё, что умеем, обратное преобразование включено. */
public static final SystemPolicy DEFAULT = new SystemPolicy(
true, true, MaskMode.MASK, Set.of(ALL), Set.of("CVV", "PIN", "DATE"));
true, true, MaskMode.MASK, Set.of(ALL),
Set.of("CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY"));
public SystemPolicy {
types = Set.copyOf(types);
@@ -76,8 +76,33 @@ class ContextDetectionTest {
@Test
void masksBirthPlace() {
assertHidden("Место рождения: город Тверь, проживает в Москве", "город Тверь");
assertHidden("Родился в Нижнем Новгороде", "Нижнем Новгороде");
// Место рождения — тип из requireCompanion: без другого ПД рядом не маскируется
// («Нижний Новгород» в рассказе о городе не должен теряться), поэтому в тесте
// на распознавание якоря рядом добавлен телефон.
assertHidden("Место рождения: город Тверь, проживает в Москве, тел. +7 916 123-45-67", "город Тверь");
assertHidden("Родился в Нижнем Новгороде, тел. +7 916 123-45-67", "Нижнем Новгороде");
}
@Test
void doesNotMaskBirthPlaceWithoutAnyOtherPersonalData() {
String text = "Экскурсия в Нижний Новгород перенесена на май";
assertEquals(text, mask(text), "место рождения без другого ПД рядом не маскируется");
}
@Test
void masksBirthPlaceWhenOtherPersonalDataIsAlsoPresent() {
assertHidden("Место рождения: город Тверь, ИНН 770301234550", "город Тверь");
}
@Test
void doesNotMaskCountryWithoutAnyOtherPersonalData() {
String text = "Цены на нефть выросли в Казахстане в этом квартале";
assertEquals(text, mask(text), "страна без другого ПД рядом не маскируется");
}
@Test
void masksCountryWhenOtherPersonalDataIsAlsoPresent() {
assertHidden("Страна проживания Казахстан, ИНН 770301234550", "Казахстан");
}
@Test