Skip to content

Commit 350d945

Browse files
committed
When masking, replace all regex matches in string values
Previously, the whole field value had to match the regex in order to be masked. Now, each regex match within a string value will be replaced. Fixes #400
1 parent 2ea30ca commit 350d945

File tree

5 files changed

+55
-9
lines changed

5 files changed

+55
-9
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,9 @@ for the path string format and more examples. But in general:
14521452

14531453
Specific values to be masked can be specified in several ways, as seen in the following example:
14541454

1455+
When using regexes to identify strings to mask, all matches within each string field value will be replaced.
1456+
If you want to match the full string field value, then use the beginning of line (`^`) and end of line (`$`) markers.
1457+
14551458
```xml
14561459
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
14571460
<jsonGeneratorDecorator class="net.logstash.logback.mask.MaskingJsonGeneratorDecorator">
@@ -1462,9 +1465,9 @@ Specific values to be masked can be specified in several ways, as seen in the fo
14621465
-->
14631466
<defaultMask>****</defaultMask>
14641467

1465-
<!-- Values to mask added via <value> will use the default mask string -->
1468+
<!-- Values to mask added via <value> will use the default mask string -->
14661469
<value>^foo$</value>
1467-
<value>^bar$</value>
1470+
<value>bar</value>
14681471

14691472
<!-- Multiple values can be specified as a comma separated string in the <values> element. -->
14701473
<values>^baz$,^blah$</values>

src/main/java/net/logstash/logback/mask/RegexValueMasker.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,20 @@ public RegexValueMasker(String regex, Object mask) {
4141
*/
4242
public RegexValueMasker(Pattern pattern, Object mask) {
4343
this.pattern = Objects.requireNonNull(pattern, "pattern must not be null");
44-
this.mask = Objects.requireNonNull(mask, "rmask must not be null");
44+
this.mask = Objects.requireNonNull(mask, "mask must not be null");
4545
}
4646

4747
@Override
4848
public Object mask(JsonStreamContext context, Object o) {
4949
if (o instanceof CharSequence) {
5050
Matcher matcher = pattern.matcher((CharSequence) o);
51-
if (matcher.matches()) {
52-
return mask instanceof String
53-
? matcher.replaceAll((String) mask)
54-
: mask;
51+
if (mask instanceof String) {
52+
String replaced = matcher.replaceAll((String) mask);
53+
if (replaced != o) {
54+
return replaced;
55+
}
56+
} else if (matcher.matches()) {
57+
return mask;
5558
}
5659
}
5760
return null;

src/test/java/net/logstash/logback/mask/MaskingJsonGeneratorDecoratorTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ public void configuration() throws IOException {
175175
"{\"fieldA\":\"****\",\"fieldB\":\"****\",\"fieldC\":\"****\",\"fieldD\":\"****\",\"fieldE\":\"[masked]\",\"fieldF\":\"****\",\"testfield\":\"[maskedtestfield]\"}",
176176
masks);
177177
test(
178-
"{\"field0\":\"value0\",\"field1\":\"value1\",\"field2\":\"value2\",\"field3\":\"value3\",\"field-Z\":\"value-Z\",\"field4\":\"value4\",\"field5\":\"testvalue\",\"field6\":\"value6\"}",
179-
"{\"field0\":\"****\",\"field1\":\"****\",\"field2\":\"****\",\"field3\":\"****\",\"field-Z\":\"value-masked\",\"field4\":\"value4\",\"field5\":\"[maskedtestvalue]\",\"field6\":\"****\"}",
178+
"{\"field0\":\"value0\",\"field1\":\"value1\",\"field2\":\"value2\",\"field3\":\"value3\",\"field-Z\":\"value-Z\",\"field4\":\"value4\",\"field5\":\"testvalue\",\"field6\":\"value6\",\"field7\":\"nestedvalue1-nestedvalue1\"}",
179+
"{\"field0\":\"****\",\"field1\":\"****\",\"field2\":\"****\",\"field3\":\"****\",\"field-Z\":\"value-masked\",\"field4\":\"value4\",\"field5\":\"[maskedtestvalue]\",\"field6\":\"****\",\"field7\":\"****-****\"}",
180180
masks);
181181
}
182182

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package net.logstash.logback.mask;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
import com.fasterxml.jackson.databind.node.NullNode;
8+
9+
class RegexValueMaskerTest {
10+
11+
@Test
12+
void regexMatchesFullString() {
13+
RegexValueMasker masker = new RegexValueMasker("^test$", "****");
14+
assertThat(masker.mask(null, "test")).isEqualTo("****");
15+
assertThat(masker.mask(null, "testtest")).isNull();
16+
}
17+
18+
@Test
19+
void regexMatchesPartialString() {
20+
RegexValueMasker masker = new RegexValueMasker("test", "****");
21+
assertThat(masker.mask(null, "test")).isEqualTo("****");
22+
assertThat(masker.mask(null, "test-test")).isEqualTo("****-****");
23+
assertThat(masker.mask(null, "foo")).isNull();
24+
}
25+
26+
@Test
27+
void nullMask() {
28+
RegexValueMasker masker = new RegexValueMasker("test", NullNode.instance);
29+
assertThat(masker.mask(null, "test")).isEqualTo(NullNode.instance);
30+
assertThat(masker.mask(null, "test-test")).isNull();
31+
}
32+
33+
@Test
34+
void nonString() {
35+
RegexValueMasker masker = new RegexValueMasker("test", "****");
36+
assertThat(masker.mask(null, 1)).isNull();
37+
}
38+
39+
}

src/test/resources/net/logstash/logback/mask/MaskingJsonGeneratorDecoratorTest-masks.xml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<value>^value0$</value>
2020
<value>^value1$</value>
2121
<values>^value2$,^value3$</values>
22+
<value>nestedvalue1</value>
2223
<valueMask>
2324
<value>^(value-).*$</value>
2425
<mask>$1masked</mask>

0 commit comments

Comments
 (0)