-2

I'm trying to mask all the occurences of AccountNumber in my XML response code. The AccountNumber has 16 digits and I want to mask first 12 digits and retain last four.

XML response:

<ns2:PaymentMethod>
  <CCInfo xmlns="">
           <AccountType>sdaj</AccountType>
           <AccountNumber>1234567890123456</AccountNumber>
           <AccountName>sdfsad</AccountName>
           <ExpirationMonth>sdaf</ExpirationMonth>
           <ExpirationYear>afgds</ExpirationYear>
    </CCInfo>
  </ns2:PaymentMethod>
  <ns2:PaymentMethod>
  <CCInfo xmlns="">
           <AccountType>kyfkuk</AccountType>
           <AccountNumber>098765432123987</AccountNumber>
           <AccountName>hjvkv</AccountName>
           <ExpirationMonth>gfdgh</ExpirationMonth>
           <ExpirationYear>tdjk</ExpirationYear>
    </CCInfo>
  </ns2:PaymentMethod>

Below is my java code:

String accountNumberPatternString ="<AccountNumber>(^.{12})";
Pattern accountNumberPattern = Pattern.compile(accountNumberPatternString);
Matcher matcher = accountNumberPattern.matcher(data);
String maskedResult = matcher.replaceAll("<AccountNumber>*******");

I'm expecting the result as:

<AccountNumber>************3456</AccountNumber>

but I'm getting the result as:

<AccountNumber>1234567890123456</AccountNumber>
Patrick Parker
  • 4,863
  • 4
  • 19
  • 51
eureka19
  • 2,731
  • 5
  • 26
  • 34
  • 1
    why is this `^` in your pattern? – Patrick Parker Feb 15 '17 at 09:03
  • one solution would be to extract the AN from the xml. once you have it : you can use : String.format("%1$" + AN.length() + "s", AN.subString(13,15).replace(" ","*"); – Tom Feb 15 '17 at 09:09
  • hopefully you are using a valid method to convert the xml stream to a String so as to not corrupt the data in the process. – jtahlborn Feb 15 '17 at 17:51

2 Answers2

2

The ^ is wrong. And if you only have numbers, you should also only match numbers with \d. And the () parantheses are unnecessary.

https://regex101.com/r/Uu6qTR/1

<AccountNumber>\d{12}
F.P
  • 17,421
  • 34
  • 123
  • 189
  • When I use String accountNumberPatternString ="\d{12}"; I'm getting the error: Invalid escape sequence (valid ones are \b \t \n \f \r \" \' \\ ) – eureka19 Feb 15 '17 at 09:12
  • 2
    Well... of course... you need to double escape the backslash because it's inside a string. – F.P Feb 15 '17 at 09:20
  • 1
    @FlorianPeschka is right. eureka19, try to use : \\d{12} – Sebastien Feb 15 '17 at 10:39
  • @FlorianPeschka It works fine thanks. What will be the change if the length of the account number varies. Is there a way to say that I want to mask the entire account number except for the last four digits? That will fix my problem of variable length account number. – eureka19 Feb 15 '17 at 10:46
  • @eureka19 For that case I would advise you to go the route that Tom mentioned in a comment to your question. It's easier and less confusing in the code. – F.P Feb 15 '17 at 10:51
1

I'm sure that you are aware of the dangers of parsing XML with regex, but let's ignore that for now.

Based on your updated requirements, that the length of the account number can vary, I would suggest the use of Matcher.appendReplacement, like so:

    Pattern p = Pattern.compile("(?<=<AccountNumber>)\\d*(?=\\d{4})");
    Matcher m = p.matcher(data);
    StringBuffer maskedResult = new StringBuffer();
    while (m.find()) {
        String thisMask = m.group(0).replaceAll(".", "*");
        m.appendReplacement(maskedResult, thisMask);
    }
    m.appendTail(maskedResult);
    System.out.println(maskedResult.toString());

Note in the above example I have used positive lookbehind (?<=) to identify the start of Account Number, then positive lookahead (?=) to ensure that four digits remain.

Finally, here's a more robust version that handles account numbers with letters, spaces, and/or dashes, such as "<AccountNumber> 123 456-78901FFA-56C </AccountNumber>":

    Pattern p = Pattern.compile("(?<=<AccountNumber>)([-\\s\\w]*)((?:[a-zA-Z0-9][-_\\s]*){4})");
    Matcher m = p.matcher(data);
    StringBuffer maskedResult = new StringBuffer();
    while (m.find()) {
        String thisMask = m.group(1).replaceAll("[^-_\\s]", "*");
        m.appendReplacement(maskedResult, thisMask + "$2");
    }
    m.appendTail(maskedResult);
    System.out.println(maskedResult.toString());
Community
  • 1
  • 1
Patrick Parker
  • 4,863
  • 4
  • 19
  • 51
  • I'm getting the following error in the line m.appendReplacement(maskedResult, thisMask); The method appendReplacement(StringBuffer, String) in the type Matcher is not applicable for the arguments (StringBuilder, String) – eureka19 Feb 16 '17 at 15:09
  • 1
    @eureka19 Oh right sorry. I guess you have to use StringBuffer not StringBuilder. I have updated the answer. – Patrick Parker Feb 16 '17 at 15:26