diff --git a/integrations/src/test/java/io/jenkins/plugins/casc/SSHCredentialsTest.java b/integrations/src/test/java/io/jenkins/plugins/casc/SSHCredentialsTest.java index 7b70897e3d..b4c8686865 100644 --- a/integrations/src/test/java/io/jenkins/plugins/casc/SSHCredentialsTest.java +++ b/integrations/src/test/java/io/jenkins/plugins/casc/SSHCredentialsTest.java @@ -118,4 +118,15 @@ public Optional reveal(String secret) { return Optional.empty(); } } + + @Test + @ConfiguredWithCode("SSHCredentialsTest_Recursive_Key.yml") + @Issue("https://github.com/jenkinsci/configuration-as-code-plugin/issues/2488") + void shouldSupportRecursiveBase64Certificates(JenkinsConfiguredWithCodeRule j) { + BasicSSHUserPrivateKey certKey = getCredentials(BasicSSHUserPrivateKey.class); + assertThat( + "Private key roundtrip failed", + certKey.getPrivateKeys().get(0).trim().replace("\r\n", "\n"), + equalTo(MySSHKeySecretSource.PRIVATE_SSH_KEY)); + } } diff --git a/integrations/src/test/resources/io/jenkins/plugins/casc/SSHCredentialsTest_Recursive_Key.yml b/integrations/src/test/resources/io/jenkins/plugins/casc/SSHCredentialsTest_Recursive_Key.yml new file mode 100644 index 0000000000..f62feff0fe --- /dev/null +++ b/integrations/src/test/resources/io/jenkins/plugins/casc/SSHCredentialsTest_Recursive_Key.yml @@ -0,0 +1,14 @@ +jenkins: + systemMessage: Jenkins with SSH Credentials for JCasC test + +credentials: + system: + domainCredentials: + - credentials: + - basicSSHUserPrivateKey: + scope: SYSTEM + id: "userid_recursive" + username: "recursive-user" + privateKeySource: + directEntry: + privateKey: ${decodeBase64:${SSH_AGENT_PRIVATE_KEY_BASE64}} diff --git a/plugin/src/main/java/io/jenkins/plugins/casc/SecretSourceResolver.java b/plugin/src/main/java/io/jenkins/plugins/casc/SecretSourceResolver.java index fe9ead784a..6112e60fbc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/casc/SecretSourceResolver.java +++ b/plugin/src/main/java/io/jenkins/plugins/casc/SecretSourceResolver.java @@ -201,7 +201,27 @@ static class DecodeBase64Lookup implements StringLookup { @Override public String lookup(@NonNull final String key) { - return new String(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + + final String value = key.trim(); + + if (value.isEmpty()) { + return ""; + } + + try { + return new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8); + } catch (IllegalArgumentException e) { + try { + return new String(Base64.getUrlDecoder().decode(value), StandardCharsets.UTF_8); + } catch (IllegalArgumentException e2) { + LOGGER.log( + Level.WARNING, + "Configuration import: Failed to decode base64 secret. " + + "The value might not be resolved yet or is invalid base64. Defaulting to empty string.", + e2); + return ""; + } + } } }