Using safely Credentials in shell commands?

So after receiving a string interpolation warning and getting redirected here:
String interpolation (and also reading it).

They say that interpolating with double quotes is not safe and to use single quotes instead. But that does not switch the variable out of for the credential or am i missunderstanding something? Theres a wrong and a correct example but they dont do the same thing as far as i understand.

So since in a declaritive Pipeline there is no feature to just just git push without shell i dont know how i can pass my credentials safely.

Currently:

withCredentials([usernamePassword(credentialsId: 'GitCred', usernameVariable:   'USERNAME', passwordVariable: 'PASSWORD')]) {
  def username = URLEncoder.encode(USERNAME, "UTF-8")
  bat "git push https://$username:$PASSWORD@URL.git HEAD:master"
}

Suggested by CloudBees:

withCredentials([usernamePassword(credentialsId: 'GitCred', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
    def username = URLEncoder.encode(USERNAME, "UTF-8")
    bat 'git push https://$username:$PASSWORD@URL.git HEAD:master'
 }

but this doenst substitute the credentials so its useless unless these are variables defined in the env of the agent which i have little to no access to most of the time.

Do I have a wrong understanding? What are my options here to not receive the interpolation warning?

If possible without plugins that are not installed because these instances are updated rarely and it could break things

Jenkins setup:
Jenkins: 2.426.1
OS: Mac OS X - 13.2.1
Java: 21.0.1 - Homebrew (OpenJDK 64-Bit Server VM)

ant:497.v94e7d9fffa_b_9
antisamy-markup-formatter:162.v0e6ec0fcfcf6
apache-httpcomponents-client-4-api:4.5.14-208.v438351942757
authentication-tokens:1.53.v1c90fd9191a_b_
azure-ad:433.v1982e2b_b_4a_fe
azure-cli:0.9
azure-credentials:293.vb_d506148f506
azure-sdk:157.v855da_0b_eb_dc2
blueocean:1.27.9
blueocean-bitbucket-pipeline:1.27.9
blueocean-commons:1.27.9
blueocean-config:1.27.9
blueocean-core-js:1.27.9
blueocean-dashboard:1.27.9
blueocean-display-url:2.4.2
blueocean-events:1.27.9
blueocean-git-pipeline:1.27.9
blueocean-github-pipeline:1.27.9
blueocean-i18n:1.27.9
blueocean-jwt:1.27.9
blueocean-personalization:1.27.9
blueocean-pipeline-api-impl:1.27.9
blueocean-pipeline-editor:1.27.9
blueocean-pipeline-scm-api:1.27.9
blueocean-rest:1.27.9
blueocean-rest-impl:1.27.9
blueocean-web:1.27.9
bootstrap5-api:5.3.2-2
bouncycastle-api:2.29
branch-api:2.1135.v8de8e7899051
build-timeout:1.31
caffeine-api:3.1.8-133.v17b_1ff2e0599
checks-api:2.0.2
cloudbees-bitbucket-branch-source:856.v04c46c86f911
cloudbees-folder:6.858.v898218f3609d
command-launcher:107.v773860566e2e
commons-lang3-api:3.13.0-62.v7d18e55f51e2
commons-text-api:1.11.0-94.v3e1f4a_926e49
credentials:1309.v8835d63eb_d8a_
credentials-binding:642.v737c34dea_6c2
display-url-api:2.200.vb_9327d658781
durable-task:523.va_a_22cf15d5e0
echarts-api:5.4.3-1
email-ext:2.102
favorite:2.4.3
font-awesome-api:6.4.2-1
git:5.2.1
git-client:4.5.0
git-parameter:0.9.19
github:1.37.3.1
github-api:1.318-461.v7a_c09c9fa_d63
github-branch-source:1752.vc201a_0235d80
gradle:2.9
handy-uri-templates-2-api:2.1.8-22.v77d5b_75e6953
htmlpublisher:1.32
instance-identity:185.v303dc7c645f9
ionicons-api:56.v1b_1c8c49374e
jackson2-api:2.15.3-372.v309620682326
jakarta-activation-api:2.0.1-3
jakarta-mail-api:2.0.1-3
javax-activation-api:1.2.0-6
javax-mail-api:1.6.2-9
jaxb:2.3.9-1
jdk-tool:73.vddf737284550
jenkins-design-language:1.27.9
jjwt-api:0.11.5-77.v646c772fddb_0
jquery3-api:3.7.1-1
junit:1240.vf9529b_881428
ldap:711.vb_d1a_491714dc
mailer:463.vedf8358e006b_
matrix-auth:3.2.1
matrix-project:818.v7eb_e657db_924
mina-sshd-api-common:2.11.0-86.v836f585d47fa_
mina-sshd-api-core:2.11.0-86.v836f585d47fa_
okhttp-api:4.11.0-157.v6852a_a_fa_ec11
pam-auth:1.10
pipeline-build-step:516.v8ee60a_81c5b_9
pipeline-github-lib:42.v0739460cda_c4
pipeline-graph-analysis:202.va_d268e64deb_3
pipeline-groovy-lib:689.veec561a_dee13
pipeline-input-step:477.v339683a_8d55e
pipeline-milestone-step:111.v449306f708b_7
pipeline-model-api:2.2151.ve32c9d209a_3f
pipeline-model-definition:2.2151.ve32c9d209a_3f
pipeline-model-extensions:2.2151.ve32c9d209a_3f
pipeline-rest-api:2.34
pipeline-stage-step:305.ve96d0205c1c6
pipeline-stage-tags-metadata:2.2151.ve32c9d209a_3f
pipeline-stage-view:2.34
plain-credentials:143.v1b_df8b_d3b_e48
plugin-util-api:3.6.0
powershell:2.1
pubsub-light:1.18
resource-disposer:0.23
scm-api:683.vb_16722fb_b_80b_
script-security:1281.v22fb_899df1a_e
snakeyaml-api:2.2-111.vc6598e30cc65
sse-gateway:1.26
ssh-credentials:308.ve4497b_ccd8f4
ssh-slaves:2.916.vd17b_43357ce4
sshd:3.312.v1c601b_c83b_0e
structs:325.vcb_307d2a_2782
timestamper:1.26
token-macro:384.vf35b_f26814ec
trilead-api:2.84.v72119de229b_7
variant:60.v7290fc0eb_b_cd
workflow-aggregator:596.v8c21c963d92d
workflow-api:1283.v99c10937efcb_
workflow-basic-steps:1042.ve7b_140c4a_e0c
workflow-cps:3806.va_3a_6988277b_2
workflow-durable-task-step:1289.v4d3e7b_01546b_
workflow-job:1360.vc6700e3136f5
workflow-multibranch:756.v891d88f2cd46
workflow-scm-step:415.v434365564324
workflow-step-api:639.v6eca_cd8c04a_a_
workflow-support:865.v43e78cc44e0d
ws-cleanup:0.45

You’re using a bat step, so you’re on windows and on windows env variables are reference with %var%
so you need to write

withCredentials([usernamePassword(credentialsId: 'GitCred', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
    def username = URLEncoder.encode(USERNAME, "UTF-8")
    bat 'git push https://%USERNAME%:%PASSWORD%@URL.git HEAD:master'
 }

Thought the problem is that you urlencode the username which you would need to wrap in a withEnv step probably

Ok this interpolates “correctly” without a warning I guess but my push fails even though the console logs show the exact same URL. This i do not understand. Could it be some encoding issue?

Edit: Actually its not the exact same URL the @ in the username doesnt get encoded properly. Apparently it just doenst recognize the variable i setup inside of the withCredentials wrapper.

So i wrapped it in withEnv() but then i get the same warning again:
Warning: A secret was passed to “withEnv” using Groovy String interpolation, which is insecure.
Affected argument(s) used the following variable(s): [PASSWORD]

I don’t understand how to do this properly

For the moment i changed it to only pass the encoded username in withEnv() but should the password ever need to be encoded I would not know how to handle it

withCredentials([usernamePassword(credentialsId: 'GitCred', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
    def username = URLEncoder.encode(USERNAME, "UTF-8")
    def password = URLEncoder.encode(PASSWORD, "UTF-8")
    withEnv(["USERNAME=${username}"]) { // <- Would like to be able to pass the encoded password here too
       bat 'git push https://%USERNAME%:%PASSWORD%@URL.git HEAD:master'
    }
}

Best solution would be to setup git with ssh.
Second best solution would be to create a credential with the already url-encoded values.

And if you did checkout the repo with credentials via jenkins ( userRemoteConfig in the scm options), a simple git push should work as well.