-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JENKINS-46670: Escape quotes in environment variables #218
Conversation
Does this break with already escaped quotes? See https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Escaping_JavaScript_escapes. Would be nice if you could add a test for that. IMHO it is always better to use a battle tested library for stuff like that, instead of implementing it with string replace.. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use a method of escaping that takes into account already escaped values..
@@ -212,7 +212,7 @@ public void onClose(int i, String s) { | |||
environmentExpander.expand(envVars); | |||
for (Map.Entry<String, String> entry : envVars.entrySet()) { | |||
watch.getInput().write( | |||
String.format("export %s=\"%s\"%s", entry.getKey(), entry.getValue(), NEWLINE).getBytes(StandardCharsets.UTF_8)); | |||
String.format("export %s=\"%s\"%s", entry.getKey(), entry.getValue().replace("\"","\\\""), NEWLINE).getBytes(StandardCharsets.UTF_8)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked, this is vulnerable to double escaping:
podTemplate(name: "horst", label: "label", containers: [
containerTemplate(name: 'ubuntu',
image: 'ubuntu',
ttyEnabled: true,
command: 'cat',
)
]) {
withEnv(['C="C', 'D=D"', 'E=\\"E', 'F=F\\"']) {
node("label") {
container('ubuntu') {
sh '''echo "C='$C'" '''
sh '''echo "D='$D'" '''
sh '''echo "E='$E'" '''
sh '''echo "F='$F'" '''
}
}
}
}
leads to
[full] Running shell script
+ echo C='"C'
C='"C'
[Pipeline] sh
[full] Running shell script
+ echo D='D"'
D='D"'
[Pipeline] sh
[full] Running shell script
+ echo E='\E
export F=F\'
E='\E
export F=F\'
[Pipeline] sh
[full] Running shell script
+ echo F=''
F=''
Note: corrected the example, '\\' is necessary to trigger this. While not necessarily a vulnerability, users might trigger unexpected errors when using escaped quotes. |
362019c
to
6a196d3
Compare
Agreed - switched to using |
} | ||
|
||
if (environmentExpander != null) { | ||
EnvVars envVars = new EnvVars(); | ||
environmentExpander.expand(envVars); | ||
for (Map.Entry<String, String> entry : envVars.entrySet()) { | ||
watch.getInput().write( | ||
String.format("export %s=\"%s\"%s", entry.getKey(), entry.getValue(), NEWLINE).getBytes(StandardCharsets.UTF_8)); | ||
String.format("export %s=\"%s\"%s", entry.getKey(), StringEscapeUtils.escapeJavaScript(entry.getValue()), NEWLINE).getBytes(StandardCharsets.UTF_8)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This escapes single quotes as well, which
a) does not even work in bash
b) is wrong as we are already in double quotes (%s=\"%s\"%s"
)
see e.g. a password that contains a single quote (stupid idea, but happens), where it will be encoded as \'
(literally).
example pipeline:
podTemplate(name: "horst", label: "label", containers: [
containerTemplate(name: 'ubuntu',
image: 'ubuntu',
ttyEnabled: true,
command: 'cat',
)
]) {
withEnv(["C='C'", 'D=\'', 'E= ', 'F=\n']) {
node("label") {
container('ubuntu') {
sh '''#!/bin/bash -x
ENC='Jw=='; [[ $D = $(base64 -d <<<$ENC) ]]
'''
}
}
}
}
Note: 'Jw==' is a single quote base64 encoded.
The single quote part can be fixed by using escapeJava
instead of escapeJavaScript
, but I don't know if that will have more issues. It also escapes "quotes and control-chars (tab, backslash, cr, ff, etc.)", see Javadoc. It also escapes unicode characters, see source code :-).
Escaping is hard, especially for bash. Maybe it makes sense to add more tests, especially regarding control chars and unicode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Back now and able to look at this - I had originally thought of the issue of blasting these out to build console log and thought to use the javascript to escape - how wrong that was!
re: xss - I check and the console log viewer escapes already
export HACK="<script>alert('XSS);</script>"
becomes
export HACK="<script>alert('XSS');<\/script>"
so I think we should be good there unless I'm not seeing it right.
To sum up: We want to only escape double quotes if they exist in the environment variable, not anything else with it - otherwise, we're guessing at what the user REALLY meant to escape. If the user provided an escaped quote such as
environment {
//note that pipeline requires that extra \
quote = 'Testing-\\"-qoute'
}
it comes to bash as
quote = 'Testing-"-qoute'
and we should provide the same functionality as the other executors provide by putting executing export quote = "Testing-\"-qoute"
and seeing
quote = 'Testing-"-qoute'
So I'm not sure where to go from here other than making naive assumption to ONLY escape a double quote in the provided variable then let bash double quotes handle the rest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
XSS is not an issue here, I never wanted to hint at that. OWASP is just a nice resource for escaping issues of any kind 😃 .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm not sure where to go from here
I would suggest to use escapeJava
instead of escapeJavascript
, and add a few test cases to make sure that whitespace characters (tab, backslash, cr, ff, etc.) are encoded correctly. See https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#escapeJava(java.lang.String) for reference.
This is updated now - I'll have the candidate fix for "global properties" variables behind it since they'll use that common escape. |
@marvinthepa looks like your feedback has been addressed, can we merge? I'd like to cut a new release |
@MattLud looks better. Did you check what happens with whitespace/control characters?
|
Looks like this is one cause for "script returned exit code -1", see https://issues.jenkins-ci.org/browse/JENKINS-46651 |
@carlossg @MattLud whitespace is not correctly handled:
leads to
I.e., a literal newline becomes the characters '' and 'n', while it should be a literal newline. |
EscapeXSI was no better -
|
That is annoying. If only kubernetes would provide an API to set environment variables directly, we wouldn't have this problem.. |
Edited: this was wrong, sorry about that. |
I noticed there is also another problem...it seems when you throw global libraries into the mix, it creates env vars for each one that gets loaded... I see this in my logs: export: `library.ci-pipeline.version=debug-int-tests': not a valid identifier 'ci-pipeline' is my library...and the version is my branch which is 'debug-int-tests' I patched my env with something simple like:
make sense? |
After a discussion with a security-aware colleague of mine, we came up with a different approach. I will push it for you to review. It involves a self-implemented escaper, but it is a very simple one. |
Could you provide a code sample, so I can reproduce it? Thanks. I tried something like
Before the whole pipeline, but it didn't trigger any error for me. |
That way, we only need to escape single quotes, nothing else. Single quotes cannot be escaped using baslash, as that is treated literally inside single quotes. I.e. to get a literal single quotes, we have to break out of the single quotes and escape it outside. "Simple". So "it's cool" becomes 'it'\''s cool,i', Treated by the shell as the concatenation of "it", "'", and "s cool". See e.g. https://stackoverflow.com/a/1315213.
Trying to put together something for the env vars from libraries...nothing yet... but, take a look at this: https://github.com/jenkinsci/workflow-cps-global-lib-plugin/blob/master/src/main/resources/org/jenkinsci/plugins/workflow/libs/LibraryConfiguration/help-name.html |
@scoheb: I was able to reproduce your problem (just using @carlossg do you have time to review my change and merge to master, so that we can merge it into #232? |
@marvinthepa so this is ready to merge now? |
there's a findbugs error
|
@marvinthepa were you also able to reproduce the "-1" script return error? |
@carlossg: findbugs error fixed, good to merge. |
cd9ad5a
to
a27fd94
Compare
Simple escape for quotes on environment variables to avoid dangling multi-line variables during 'export' part of container.