package ru.pdguard.mask; import jakarta.enterprise.context.ApplicationScoped; import ru.pdguard.detect.RuleRegistry; import java.util.Map; import java.util.function.UnaryOperator; /** * Превращает найденное значение в замену согласно настройкам системы. * *

Тип, для которого вид маски не задан, скрывается звёздочками целиком — * безопасное поведение по умолчанию для вновь добавленных правил. */ @ApplicationScoped public class Masker { private static final UnaryOperator EDGES = v -> Strategies.keepEdges(v, 2, 2); private static final UnaryOperator SHORT_SERIES = v -> Strategies.keepEdges(v, 0, 2); private static final Map> 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(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(RuleRegistry.CVV, Strategies::stars), Map.entry(RuleRegistry.PIN, Strategies::stars), Map.entry(RuleRegistry.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(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.BIRTH_PLACE, Strategies::stars), Map.entry(RuleRegistry.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(RuleRegistry.CARD_EXPIRY, Strategies::starsKeepingPunctuation), Map.entry(RuleRegistry.BIK, Strategies::stars), Map.entry(RuleRegistry.INCOME, Strategies::stars), Map.entry(RuleRegistry.BIOMETRIC, Strategies::stars) ); public String mask(String type, String value, MaskMode mode, MaskContext context) { return switch (mode) { case MASK -> BY_TYPE.getOrDefault(type, Strategies::stars).apply(value); case TOKEN -> context.resolve(type, value, (t, n) -> "[" + t + "_" + n + "]"); case SYNTHETIC -> context.resolve(type, value, (t, n) -> Synthetic.forType(t, value, n)); }; } }