diff --git a/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedException.java b/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedException.java index 09c3b949..a09d5c20 100644 --- a/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedException.java +++ b/src/main/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedException.java @@ -12,6 +12,9 @@ final class MaskedException extends Exception { private static final long serialVersionUID = 1L; static Throwable of(@NonNull Throwable unmasked, Pattern pattern) { + if (pattern.toString().length() < 3) { + return unmasked; + } return of(unmasked, new HashSet<>(), pattern); } diff --git a/src/test/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedExceptionTest.java b/src/test/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedExceptionTest.java new file mode 100644 index 00000000..cf4b2898 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/credentialsbinding/impl/MaskedExceptionTest.java @@ -0,0 +1,67 @@ +/* + * The MIT License + * + * Copyright 2025 CloudBees, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.credentialsbinding.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +import java.util.regex.Pattern; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; + +public class MaskedExceptionTest { + @Issue("JENKINS-75914") + @Test + public void maskedExceptionIgnoresShortSecretsEmpty() throws Exception { + Exception unmasked = new Exception("An empty pattern for exception mask"); + var masked = MaskedException.of(unmasked, Pattern.compile("")); + assertThat(masked.getMessage(), not(containsString("****"))); + } + + @Issue("JENKINS-75914") + @Test + public void maskedExceptionIgnoresShortSecrets1Char() throws Exception { + Exception unmasked = new Exception("1 character pattern for exception mask"); + var masked = MaskedException.of(unmasked, Pattern.compile("e")); + assertThat(masked.getMessage(), not(containsString("****"))); + } + + @Issue("JENKINS-75914") + @Test + public void maskedExceptionIgnoresShortSecrets2Char() throws Exception { + Exception unmasked = new Exception("2 character pattern for exception mask"); + var masked = MaskedException.of(unmasked, Pattern.compile("n ")); + assertThat(masked.getMessage(), not(containsString("****"))); + } + + @Issue("JENKINS-75914") + @Test + public void maskedExceptionIgnoresShortSecrets4Char() throws Exception { + Exception unmasked = new Exception("4 character pattern for exception mask"); + var masked = MaskedException.of(unmasked, Pattern.compile("patt")); + assertThat(masked.getMessage(), containsString("****")); + } +}