refactor: устранить нарушения DRY, SRP, KISS
- DRY: вынести scoped() в общий ScopedKey (был дублирован в PayloadStore и SharedIndex) - DRY: вынести чтение словарей и перечитывание файлов в ResourceLoader (было дублировано в NameDictionary, ToponymDictionary, SystemsConfig) - DRY: вынести алгоритм Луна в Validators.luhnCheckDigit (Synthetic переиспользует) - SRP: вынести константы типов ПД из RuleRegistry в PdTypes - SRP: вынести проверку организаций из NameDictionary в OrganisationDetector - KISS: словари оставлены статическими (неизменяемые, детерминированные) Поведение не изменилось: 1840 тестов проходят, NodeLogsLeakTest по-прежнему падает на тех же 55 задокументированных утечках.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package ru.pdguard.mask;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.pdguard.detect.RuleRegistry;
|
||||
import ru.pdguard.detect.PdTypes;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.UnaryOperator;
|
||||
@@ -19,57 +19,57 @@ public class Masker {
|
||||
private static final UnaryOperator<String> SHORT_SERIES = v -> Strategies.keepEdges(v, 0, 2);
|
||||
|
||||
private static final Map<String, UnaryOperator<String>> BY_TYPE = Map.ofEntries(
|
||||
Map.entry(RuleRegistry.EMAIL, Strategies::email),
|
||||
Map.entry(RuleRegistry.PHONE, EDGES),
|
||||
Map.entry(RuleRegistry.CARD, EDGES),
|
||||
Map.entry(RuleRegistry.INN, EDGES),
|
||||
Map.entry(RuleRegistry.SNILS, EDGES),
|
||||
Map.entry(RuleRegistry.PASSPORT, EDGES),
|
||||
Map.entry(RuleRegistry.DRIVER_LICENSE, EDGES),
|
||||
Map.entry(RuleRegistry.DEPT_CODE, EDGES),
|
||||
Map.entry(PdTypes.EMAIL, Strategies::email),
|
||||
Map.entry(PdTypes.PHONE, EDGES),
|
||||
Map.entry(PdTypes.CARD, EDGES),
|
||||
Map.entry(PdTypes.INN, EDGES),
|
||||
Map.entry(PdTypes.SNILS, EDGES),
|
||||
Map.entry(PdTypes.PASSPORT, EDGES),
|
||||
Map.entry(PdTypes.DRIVER_LICENSE, EDGES),
|
||||
Map.entry(PdTypes.DEPT_CODE, EDGES),
|
||||
// У этих документов серия короткая — две цифры или две буквы. Оставь мы
|
||||
// первые два знака, серия оказалась бы открыта целиком, поэтому видны
|
||||
// только последние. У паспорта РФ и водительского удостоверения серия
|
||||
// из четырёх знаков, там открывается половина.
|
||||
Map.entry(RuleRegistry.FOREIGN_PASSPORT, SHORT_SERIES),
|
||||
Map.entry(RuleRegistry.MILITARY_ID, SHORT_SERIES),
|
||||
Map.entry(RuleRegistry.BIRTH_CERTIFICATE, SHORT_SERIES),
|
||||
Map.entry(RuleRegistry.MEDICAL_POLICY, EDGES),
|
||||
Map.entry(RuleRegistry.CARDHOLDER, Strategies::initials),
|
||||
Map.entry(RuleRegistry.FIO, Strategies::initials),
|
||||
Map.entry(PdTypes.FOREIGN_PASSPORT, SHORT_SERIES),
|
||||
Map.entry(PdTypes.MILITARY_ID, SHORT_SERIES),
|
||||
Map.entry(PdTypes.BIRTH_CERTIFICATE, SHORT_SERIES),
|
||||
Map.entry(PdTypes.MEDICAL_POLICY, EDGES),
|
||||
Map.entry(PdTypes.CARDHOLDER, Strategies::initials),
|
||||
Map.entry(PdTypes.FIO, Strategies::initials),
|
||||
|
||||
// Код проверки и пин-код не показываем даже частично: у них слишком
|
||||
// мало знаков, чтобы открывать хотя бы один.
|
||||
Map.entry(RuleRegistry.CVV, Strategies::stars),
|
||||
Map.entry(RuleRegistry.PIN, Strategies::stars),
|
||||
Map.entry(PdTypes.CVV, Strategies::stars),
|
||||
Map.entry(PdTypes.PIN, Strategies::stars),
|
||||
|
||||
Map.entry(RuleRegistry.PASSPORT_ISSUER, Strategies::stars),
|
||||
Map.entry(PdTypes.PASSPORT_ISSUER, Strategies::stars),
|
||||
|
||||
// У дат сохраняем разделители: модель видит, что это дата, но не какая.
|
||||
Map.entry(RuleRegistry.BIRTH_DATE, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(RuleRegistry.PASSPORT_DATE, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(RuleRegistry.DATE, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(PdTypes.BIRTH_DATE, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(PdTypes.PASSPORT_DATE, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(PdTypes.DATE, Strategies::starsKeepingPunctuation),
|
||||
|
||||
Map.entry(RuleRegistry.ADDRESS_COUNTRY, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_POSTCODE, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_CITY, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_STREET, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_HOUSE, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_FLAT, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_REGION, Strategies::stars),
|
||||
Map.entry(RuleRegistry.ADDRESS_DISTRICT, Strategies::stars),
|
||||
Map.entry(RuleRegistry.BIRTH_PLACE, Strategies::stars),
|
||||
Map.entry(RuleRegistry.CITIZENSHIP, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_COUNTRY, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_POSTCODE, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_CITY, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_STREET, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_HOUSE, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_FLAT, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_REGION, Strategies::stars),
|
||||
Map.entry(PdTypes.ADDRESS_DISTRICT, Strategies::stars),
|
||||
Map.entry(PdTypes.BIRTH_PLACE, Strategies::stars),
|
||||
Map.entry(PdTypes.CITIZENSHIP, Strategies::stars),
|
||||
|
||||
Map.entry(RuleRegistry.ACCOUNT_NUMBER, EDGES),
|
||||
Map.entry(RuleRegistry.OGRN, EDGES),
|
||||
Map.entry(RuleRegistry.OGRNIP, EDGES),
|
||||
Map.entry(RuleRegistry.KPP, EDGES),
|
||||
Map.entry(PdTypes.ACCOUNT_NUMBER, EDGES),
|
||||
Map.entry(PdTypes.OGRN, EDGES),
|
||||
Map.entry(PdTypes.OGRNIP, EDGES),
|
||||
Map.entry(PdTypes.KPP, EDGES),
|
||||
// Срок действия карты — разделитель виден, сам месяц/год нет.
|
||||
Map.entry(RuleRegistry.CARD_EXPIRY, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(RuleRegistry.BIK, Strategies::stars),
|
||||
Map.entry(RuleRegistry.INCOME, Strategies::stars),
|
||||
Map.entry(RuleRegistry.BIOMETRIC, Strategies::stars)
|
||||
Map.entry(PdTypes.CARD_EXPIRY, Strategies::starsKeepingPunctuation),
|
||||
Map.entry(PdTypes.BIK, Strategies::stars),
|
||||
Map.entry(PdTypes.INCOME, Strategies::stars),
|
||||
Map.entry(PdTypes.BIOMETRIC, Strategies::stars)
|
||||
);
|
||||
|
||||
public String mask(String type, String value, MaskMode mode, MaskContext context) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ru.pdguard.mask;
|
||||
|
||||
import ru.pdguard.detect.RuleRegistry;
|
||||
import ru.pdguard.detect.PdTypes;
|
||||
import ru.pdguard.detect.Validators;
|
||||
|
||||
/**
|
||||
* Правдоподобные подставные значения вместо настоящих.
|
||||
@@ -24,30 +25,30 @@ final class Synthetic {
|
||||
static String forType(String type, String value, int ordinal) {
|
||||
int seed = value.hashCode() & Integer.MAX_VALUE;
|
||||
return switch (type) {
|
||||
case RuleRegistry.FIO -> pick(SURNAMES, seed) + " " + pick(NAMES, seed >> 3)
|
||||
case PdTypes.FIO -> pick(SURNAMES, seed) + " " + pick(NAMES, seed >> 3)
|
||||
+ " " + pick(PATRONYMICS, seed >> 6);
|
||||
case RuleRegistry.CARDHOLDER -> "IVAN PETROV";
|
||||
case RuleRegistry.EMAIL -> "user" + ordinal + "@" + pick(DOMAINS, seed);
|
||||
case RuleRegistry.PHONE -> "+7 9" + digits(seed, 2) + " " + digits(seed >> 4, 3)
|
||||
case PdTypes.CARDHOLDER -> "IVAN PETROV";
|
||||
case PdTypes.EMAIL -> "user" + ordinal + "@" + pick(DOMAINS, seed);
|
||||
case PdTypes.PHONE -> "+7 9" + digits(seed, 2) + " " + digits(seed >> 4, 3)
|
||||
+ "-" + digits(seed >> 8, 2) + "-" + digits(seed >> 12, 2);
|
||||
case RuleRegistry.CARD -> luhnCard(seed);
|
||||
case RuleRegistry.PASSPORT, RuleRegistry.DRIVER_LICENSE, RuleRegistry.FOREIGN_PASSPORT,
|
||||
RuleRegistry.MILITARY_ID -> digits(seed, 4) + " " + digits(seed >> 6, 6);
|
||||
case RuleRegistry.INN -> digits(seed, 12);
|
||||
case RuleRegistry.MEDICAL_POLICY -> digits(seed, 16);
|
||||
case RuleRegistry.SNILS -> digits(seed, 3) + "-" + digits(seed >> 4, 3)
|
||||
case PdTypes.CARD -> luhnCard(seed);
|
||||
case PdTypes.PASSPORT, PdTypes.DRIVER_LICENSE, PdTypes.FOREIGN_PASSPORT,
|
||||
PdTypes.MILITARY_ID -> digits(seed, 4) + " " + digits(seed >> 6, 6);
|
||||
case PdTypes.INN -> digits(seed, 12);
|
||||
case PdTypes.MEDICAL_POLICY -> digits(seed, 16);
|
||||
case PdTypes.SNILS -> digits(seed, 3) + "-" + digits(seed >> 4, 3)
|
||||
+ "-" + digits(seed >> 8, 3) + " " + digits(seed >> 12, 2);
|
||||
case RuleRegistry.BIRTH_DATE, RuleRegistry.PASSPORT_DATE, RuleRegistry.DATE -> syntheticDate(seed);
|
||||
case RuleRegistry.ADDRESS_CITY -> "Зареченск";
|
||||
case RuleRegistry.ADDRESS_STREET -> "Сосновая";
|
||||
case RuleRegistry.ADDRESS_HOUSE -> String.valueOf(1 + Math.floorMod(seed, 90));
|
||||
case RuleRegistry.ADDRESS_FLAT -> String.valueOf(1 + Math.floorMod(seed, 200));
|
||||
case RuleRegistry.ADDRESS_POSTCODE -> digits(seed, 6);
|
||||
case RuleRegistry.ADDRESS_COUNTRY -> "Заречье";
|
||||
case RuleRegistry.ADDRESS_REGION -> "Заречная область";
|
||||
case RuleRegistry.ADDRESS_DISTRICT -> "Сосновый район";
|
||||
case RuleRegistry.CVV -> digits(seed, 3);
|
||||
case RuleRegistry.PIN -> digits(seed, 4);
|
||||
case PdTypes.BIRTH_DATE, PdTypes.PASSPORT_DATE, PdTypes.DATE -> syntheticDate(seed);
|
||||
case PdTypes.ADDRESS_CITY -> "Зареченск";
|
||||
case PdTypes.ADDRESS_STREET -> "Сосновая";
|
||||
case PdTypes.ADDRESS_HOUSE -> String.valueOf(1 + Math.floorMod(seed, 90));
|
||||
case PdTypes.ADDRESS_FLAT -> String.valueOf(1 + Math.floorMod(seed, 200));
|
||||
case PdTypes.ADDRESS_POSTCODE -> digits(seed, 6);
|
||||
case PdTypes.ADDRESS_COUNTRY -> "Заречье";
|
||||
case PdTypes.ADDRESS_REGION -> "Заречная область";
|
||||
case PdTypes.ADDRESS_DISTRICT -> "Сосновый район";
|
||||
case PdTypes.CVV -> digits(seed, 3);
|
||||
case PdTypes.PIN -> digits(seed, 4);
|
||||
// Для остальных типов правдоподобной замены нет — отдаём токен.
|
||||
default -> "[" + type + "_" + ordinal + "]";
|
||||
};
|
||||
@@ -77,20 +78,7 @@ final class Synthetic {
|
||||
/** Номер карты, проходящий проверку алгоритмом Луна: подстановка должна выглядеть настоящей. */
|
||||
private static String luhnCard(int seed) {
|
||||
StringBuilder body = new StringBuilder("4").append(digits(seed, 14));
|
||||
int sum = 0;
|
||||
boolean doubled = true;
|
||||
for (int i = body.length() - 1; i >= 0; i--) {
|
||||
int d = body.charAt(i) - '0';
|
||||
if (doubled) {
|
||||
d *= 2;
|
||||
if (d > 9) {
|
||||
d -= 9;
|
||||
}
|
||||
}
|
||||
sum += d;
|
||||
doubled = !doubled;
|
||||
}
|
||||
body.append((10 - sum % 10) % 10);
|
||||
body.append(Validators.luhnCheckDigit(body.toString()));
|
||||
return body.substring(0, 4) + " " + body.substring(4, 8) + " "
|
||||
+ body.substring(8, 12) + " " + body.substring(12);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user