Masking local strings in Groovy

Using Jenkins 2.440.2.

I am using credentials but for some reason I have to urlencode one of the credentials (since it’s used together with sh 'curl ...') but it is also used in a “plain” version.

Unfortunately, Jenkins is leaking the encoded password when it differs from the credential.

withCredentials([usernamePassword(credentialsId: 'xyz', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) {
    script {
        String duplicate_password = env.PASSWORD
        String encoded_password = java.net.URLEncoder.encode(env.PASSWORD, "UTF-8")

        echo "credential password is ${env.PASSWORD}"
        echo "duplicate_password is ${duplicate_password}"
        echo "encoded_password is ${encoded_password}"
    }
}

The output of this is:

14:45:20  Warning: A secret was passed to "echo" using Groovy String interpolation, which is insecure.
14:45:20  		 Affected argument(s) used the following variable(s): [PASSWORD]
14:45:20  		 See https://****.io/redirect/groovy-string-interpolation for details.
14:45:20  credential password is ****
14:45:20  [Pipeline] echo
14:45:20  Warning: A secret was passed to "echo" using Groovy String interpolation, which is insecure.
14:45:20  		 Affected argument(s) used the following variable(s): [PASSWORD]
14:45:20  		 See https://****.io/redirect/groovy-string-interpolation for details.
14:45:20  duplicate_password is ****
14:45:20  [Pipeline] echo
14:45:20  encoded_password is REDACTED

Is there a way or plugin to be able to create a local variable which is masked in echo and sh (here: output and blue ocean label, not the actual command executed)?

At the moment I can only see two options, both of which have their disadvantages:

  1. Have two credentials: one urlencoded, the other one as plaintext
  2. Ensure the credential requires no urlencoding

Both options have the disadvantage that they can easily be forgotten when changing passwords and the like. It would therefore be best if there were a way to do this programmatically.

Does anybody know of such an option?

Thank you!

The plugin Mask Passwords allows to do this

maskPasswords(varPasswordPairs: [[password: encoded_password , var: 'USERNAME']]) {
  echo "encoded_password is ${encoded_password}"
}
1 Like

Oh thank you! :slight_smile: I stumbled across this plugin before, but hadn’t given it any further consideration because in the README it sounded like I needed to know my variables to mask (and their values) beforehand and define them in per job in the job configuration. So very cumbersome and parallel to the credentials.

But from your snippet it sounds much more useful and exactly what I’m looking for. Am I right to assume, I would have to create a nested withCredentials/maskPasswords setup? Something like this (pseudo code):

...
withCredentials(...) {
    ...
    maskPasswords(...) {
      ...
    } 
   ...
}
...

In freestyle jobs you would need to know them in advance but in pipeline you can do it dynamically.

And yes you’re right need to nest the things.

Thank you so much, it now works.

Was some more work to do to avoid leaking through Blue Ocean output of the sh command as well but it now works.

My solution now (no pseudo code but only the bare minimum):

withCredentials([usernamePassword(credentialsId: 'xyz', passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
script {
    String encoded_password = java.net.URLEncoder.encode(env.GIT_PASSWORD, "UTF-8")
    maskPasswords(varPasswordPairs: [[password: encoded_password]]) {
        String git_url = env.GIT_URL.replace("https://", "https://${GIT_USERNAME}:${encoded_password}@")
         // we have to add the local variable to the environment, otherwise the secure `sh '...'` will not interpolate the variable
        env.CREDENTIAL_GIT_URL = git_url
        // use single quotes to avoid leaking trough blue ocean output of the command
        sh 'git push -f --tags ${CREDENTIAL_GIT_URL}'
   }
}

There might be an easier way but this works.

1 Like