diff --git a/src/main/java/org/jenkinsci/plugins/credentialsbinding/masking/Base64SecretPatternFactory.java b/src/main/java/org/jenkinsci/plugins/credentialsbinding/masking/Base64SecretPatternFactory.java index 96063519..8d207d4b 100644 --- a/src/main/java/org/jenkinsci/plugins/credentialsbinding/masking/Base64SecretPatternFactory.java +++ b/src/main/java/org/jenkinsci/plugins/credentialsbinding/masking/Base64SecretPatternFactory.java @@ -46,7 +46,54 @@ public Collection getBase64Forms(@NonNull String secret) { } private String removeTrailingEquals(String base64Value) { - if (base64Value.endsWith("==")) { + if (base64Value.endsWith("==")) {package org.jenkinsci.plugins.credentialsbinding.masking; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Extension; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +@Extension +@Restricted(NoExternalUse.class) +public class Base64SecretPatternFactory implements SecretPatternFactory { + + private static final Base64.Encoder STANDARD_ENCODER = Base64.getEncoder(); + private static final Base64.Encoder URL_ENCODER = Base64.getUrlEncoder(); + private static final String[] SHIFTS = {"", "a", "aa"}; + + @NonNull + @Override + public Collection getEncodedForms(@NonNull String input) { + return input.isEmpty() ? Collections.emptyList() : getBase64Forms(input); + } + + @NonNull + private Collection getBase64Forms(@NonNull String secret) { + return Arrays.stream(SHIFTS) + .flatMap(shift -> encodeSecret(shift + secret).stream()) + .collect(Collectors.toSet()); // Avoid duplicate values + } + + private List encodeSecret(@NonNull String secret) { + return Arrays.asList(STANDARD_ENCODER, URL_ENCODER).stream() + .map(encoder -> encoder.encodeToString(secret.getBytes(StandardCharsets.UTF_8))) + .flatMap(encoded -> Arrays.stream(new String[]{encoded, removeTrailingEquals(encoded)})) + .distinct() // Ensure no duplicates + .collect(Collectors.toList()); + } + + private String removeTrailingEquals(@NonNull String base64Value) { + int length = base64Value.length(); + while (length > 0 && base64Value.charAt(length - 1) == '=') { + length--; + } + return base64Value.substring(0, length); + } +} + // removing the last 3 characters, the character before the == being incomplete return base64Value.substring(0, base64Value.length() - 3); }