Skip to content

Commit

Permalink
Merge pull request #3863 from car-roll/string-interpolation-issues
Browse files Browse the repository at this point in the history
add warning to string interpolation section
  • Loading branch information
MarkEWaite committed Nov 10, 2020
2 parents 134cd71 + fce5adf commit 9de18ff
Showing 1 changed file with 195 additions and 37 deletions.
232 changes: 195 additions & 37 deletions content/doc/book/pipeline/jenkinsfile.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -307,41 +307,6 @@ The following sections provide details about handling:
your application or Pipeline project.


=== String interpolation

Jenkins Pipeline uses rules identical to link:http://groovy-lang.org[Groovy] for
string interpolation. Groovy's String interpolation support can be confusing to
many newcomers to the language. While Groovy supports declaring a string with
either single quotes, or double quotes, for example:

[source,groovy]
----
def singlyQuoted = 'Hello'
def doublyQuoted = "World"
----

Only the latter string will support the dollar-sign (`$`) based string
interpolation, for example:

[source,groovy]
----
def username = 'Jenkins'
echo 'Hello Mr. ${username}'
echo "I said, Hello Mr. ${username}"
----

Would result in:

[source]
----
Hello Mr. ${username}
I said, Hello Mr. Jenkins
----

Understanding how to use string interpolation is vital for using some of
Pipeline's more advanced features.


[[using-environment-variables]]
=== Using environment variables

Expand Down Expand Up @@ -847,8 +812,7 @@ Pipeline code readability.
(the implicit parameter to `sh`) in Groovy above.
The single-quotes will cause the secret to be expanded by the shell as an environment variable.
The double-quotes are potentially less secure as the secret is interpolated by Groovy,
and so typical operating system process listings (as well as Blue Ocean,
and the pipeline steps tree in the classic UI) will accidentally disclose it :
and so typical operating system process listings will accidentally disclose it :
```
node {
withCredentials([string(credentialsId: 'mytoken', variable: 'TOKEN')]) {
Expand Down Expand Up @@ -889,6 +853,200 @@ within a single `withCredentials( ... ) { ... }` step by doing the following:
`withCredentials( ... ) { ... }` step snippet.


=== String interpolation

Jenkins Pipeline uses rules identical to link:http://groovy-lang.org[Groovy] for
string interpolation. Groovy's String interpolation support can be confusing to
many newcomers to the language. While Groovy supports declaring a string with
either single quotes, or double quotes, for example:

[source,groovy]
----
def singlyQuoted = 'Hello'
def doublyQuoted = "World"
----

Only the latter string will support the dollar-sign (`$`) based string
interpolation, for example:

[source,groovy]
----
def username = 'Jenkins'
echo 'Hello Mr. ${username}'
echo "I said, Hello Mr. ${username}"
----

Would result in:

[source]
----
Hello Mr. ${username}
I said, Hello Mr. Jenkins
----

Understanding how to use string interpolation is vital for using some of
Pipeline's more advanced features.

==== Interpolation of sensitive environment variables

[WARNING]
======
Groovy string interpolation should [red]*never* be used with credentials.
======

Groovy string interpolation can leak sensitive environment variables (i.e. credentials, see: <<Handling credentials>>).
This is because the sensitive environment variable will be interpolated during Groovy evaluation, and the environment variable's value could be made available earlier than intended, resulting in sensitive data leaking in various contexts.

For example, consider a sensitive environment variable passed to the `sh` step.

[pipeline]
----
// Declarative //
pipeline {
agent any
environment {
EXAMPLE_CREDS = credentials('example-credentials-id')
}
stages {
stage('Example') {
steps {
/* WRONG! */
sh("curl -u ${EXAMPLE_CREDS_USR}:${EXAMPLE_CREDS_PSW} https://example.com/")
}
}
}
}
// Script //
----
Should Groovy perform the interpolation, the sensitive value will be injected directly into the arguments of the `sh` step, which among other issues, means that the literal value will be visible as an argument to the `sh` process on the agent in OS process listings.
Using single-quotes instead of double-quotes when referencing these sensitive environment variables prevents this type of leaking.

[pipeline]
----
// Declarative //
pipeline {
agent any
environment {
EXAMPLE_CREDS = credentials('example-credentials-id')
}
stages {
stage('Example') {
steps {
/* CORRECT */
sh('curl -u $EXAMPLE_CREDS_USR:$EXAMPLE_CREDS_PSW https://example.com/')
}
}
}
}
// Script //
----


==== Injection via interpolation

[WARNING]
======
Groovy string interpolation can inject rogue commands into command interpreters via special characters.
======

Another note of caution. Using Groovy string interpolation for user-controlled variables with steps that pass their arguments to command interpreters such as the `sh`, `bat`, `powershell`, or `pwsh` steps can result in problems analogous to SQL injection.
This occurs when a user-controlled variable (generally an environment variable, usually a parameter passed to the build) that contains special characters (e.g. `/ \ $ & % ^ > < | ;`) is passed to the `sh`, `bat`, `powershell`, or `pwsh` steps using Groovy interpolation.
For a simple example:

[pipeline]
----
// Declarative //
pipeline {
agent any
parameters {
string(name: 'STATEMENT', defaultValue: 'hello; ls /', description: 'What should I say?')
}
stages {
stage('Example') {
steps {
/* WRONG! */
sh("echo ${STATEMENT}")
}
}
}
}
// Script //
----

In this example, the argument to the `sh` step is evaluated by Groovy, and `STATEMENT` is interpolated directly into the argument as if `sh('echo hello; ls /')` has been written in the Pipeline.
When this is processed on the agent, rather than echoing the value `hello; ls /`, it will echo `hello` then proceed to list the entire root directory of the agent.
Any user able to control a variable interpolated by such a step would be able to make the `sh` step run arbitrary code on the agent.
To avoid this problem, make sure arguments to steps such as `sh` or `bat` that reference parameters or other user-controlled environment variables use single quotes to avoid Groovy interpolation.

[pipeline]
----
// Declarative //
pipeline {
agent any
parameters {
string(name: 'STATEMENT', defaultValue: 'hello; ls /', description: 'What should I say?')
}
stages {
stage('Example') {
steps {
/* CORRECT */
sh('echo ${STATEMENT}')
}
}
}
}
// Script //
----

Credential mangling is another issue that can occur when credentials that contain special characters are passed to a step using Groovy interpolation.
When the credential value is mangled, it is no longer valid and will no longer be masked in the console log.

[pipeline]
----
// Declarative //
pipeline {
agent any
environment {
EXAMPLE_KEY = credentials('example-credentials-id') // Secret value is 'sec%ret'
}
stages {
stage('Example') {
steps {
/* WRONG! */
bat "echo ${EXAMPLE_KEY}"
}
}
}
}
// Script //
----

Here, the `bat` step receives `echo sec%ret` and the Windows batch shell will simply drop the `%` and print out the value `secret`.
Because there is a single character difference, the value `secret` will not be masked.
Though the value is not the same as the actual credential, this is still a significant exposure of sensitive information.
Again, single-quotes avoids this issue.

[pipeline]
----
// Declarative //
pipeline {
agent any
environment {
EXAMPLE_KEY = credentials('example-credentials-id') // Secret value is 'sec%ret'
}
stages {
stage('Example') {
steps {
/* CORRECT */
bat 'echo %SECRET_VALUE%'
}
}
}
}
// Script //
----


=== Handling parameters

Declarative Pipeline supports parameters out-of-the-box, allowing the Pipeline
Expand Down

0 comments on commit 9de18ff

Please sign in to comment.