提交 09e1f8cd 编写于 作者: D Daniel Beck

Merge branch 'master' of github.com:jenkinsci-cert/jenkins

......@@ -5,7 +5,7 @@
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.107-SNAPSHOT</version>
<version>2.108-SNAPSHOT</version>
</parent>
<artifactId>cli</artifactId>
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.107-SNAPSHOT</version>
<version>2.108-SNAPSHOT</version>
</parent>
<artifactId>jenkins-core</artifactId>
......
......@@ -35,6 +35,7 @@ import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.File;
......@@ -42,10 +43,10 @@ import net.sf.json.JSONObject;
import com.thoughtworks.xstream.XStream;
import hudson.init.Initializer;
import hudson.init.Terminator;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Locale;
import java.util.logging.Logger;
import jenkins.model.GlobalConfiguration;
import org.kohsuke.stapler.HttpResponses;
/**
* Base class of Hudson plugin.
......@@ -81,6 +82,8 @@ import org.kohsuke.stapler.HttpResponses;
*/
public abstract class Plugin implements Saveable {
private static final Logger LOGGER = Logger.getLogger(Plugin.class.getName());
/**
* You do not need to create custom subtypes:
* <ul>
......@@ -224,13 +227,13 @@ public abstract class Plugin implements Saveable {
public void doDynamic(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
String path = req.getRestOfPath();
if (path.startsWith("/META-INF/") || path.startsWith("/WEB-INF/")) {
throw HttpResponses.notFound();
String pathUC = path.toUpperCase(Locale.ENGLISH);
if (path.isEmpty() || path.contains("..") || path.startsWith(".") || path.contains("%") || pathUC.contains("META-INF") || pathUC.contains("WEB-INF")) {
LOGGER.warning("rejecting possibly malicious " + req.getRequestURIWithQueryString());
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST);
return;
}
if(path.length()==0)
path = "/";
// Stapler routes requests like the "/static/.../foo/bar/zot" to be treated like "/foo/bar/zot"
// and this is used to serve long expiration header, by using Jenkins.VERSION_HASH as "..."
// to create unique URLs. Recognize that and set a long expiration header.
......@@ -240,11 +243,7 @@ public abstract class Plugin implements Saveable {
long expires = staticLink ? TimeUnit.DAYS.toMillis(365) : -1;
// use serveLocalizedFile to support automatic locale selection
try {
rsp.serveLocalizedFile(req, wrapper.baseResourceURL.toURI().resolve(new URI(null, '.' + path, null)).toURL(), expires);
} catch (URISyntaxException x) {
throw new IOException(x);
}
rsp.serveLocalizedFile(req, new URL(wrapper.baseResourceURL, '.' + path), expires);
}
//
......
......@@ -341,6 +341,8 @@ public final class ProxyConfiguration extends AbstractDescribableImpl<ProxyConfi
@QueryParameter("userName") String userName, @QueryParameter("password") String password,
@QueryParameter("noProxyHost") String noProxyHost) {
Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER);
if (Util.fixEmptyAndTrim(testUrl) == null) {
return FormValidation.error(Messages.ProxyConfiguration_TestUrlRequired());
}
......
......@@ -39,5 +39,5 @@ THE SOFTWARE.
<!-- No escape/encode to avoid double-encoding if used in value attribute below -->
<j:set var="optionBody" encode="false"><d:invokeBody escapeText="false"/></j:set>
<option value="${attrs.value!=null?attrs.value:h.xmlUnescape(optionBody)}"
selected="${attrs.selected?'true':null}"><j:out value="${optionBody}"/></option>
selected="${attrs.selected?'true':null}">${h.xmlUnescape(optionBody)}</option>
</j:jelly>
......@@ -33,7 +33,7 @@ THE SOFTWARE.
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.107-SNAPSHOT</version>
<version>2.108-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Jenkins main module</name>
......
......@@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.107-SNAPSHOT</version>
<version>2.108-SNAPSHOT</version>
</parent>
<artifactId>test</artifactId>
......
......@@ -35,19 +35,26 @@ public class PluginTest {
@Rule public JenkinsRule r = new JenkinsRule();
@Issue({"SECURITY-131", "SECURITY-155"})
@Issue({"SECURITY-131", "SECURITY-155", "SECURITY-705"})
@Test public void doDynamic() throws Exception {
((TestPluginManager) r.jenkins.pluginManager).installDetachedPlugin("credentials");
r.createWebClient().goTo("plugin/credentials/images/24x24/credentials.png", "image/png");
/* Collapsed somewhere before it winds up in restOfPath:
r.createWebClient().assertFails("plugin/credentials/images/../images/24x24/credentials.png", HttpServletResponse.SC_BAD_REQUEST);
*/
r.createWebClient().goTo("plugin/credentials/images/../images/24x24/credentials.png", "image/png"); // collapsed somewhere before it winds up in restOfPath
r.createWebClient().assertFails("plugin/credentials/images/%2E%2E/images/24x24/credentials.png", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // IAE from TokenList.<init>
r.createWebClient().assertFails("plugin/credentials/images/%252E%252E/images/24x24/credentials.png", HttpServletResponse.SC_NOT_FOUND); // SECURITY-131
r.createWebClient().assertFails("plugin/credentials/images/%25252E%25252E/images/24x24/credentials.png", HttpServletResponse.SC_NOT_FOUND); // just checking
r.createWebClient().assertFails("plugin/credentials/images/%252E%252E/images/24x24/credentials.png", HttpServletResponse.SC_BAD_REQUEST); // SECURITY-131
r.createWebClient().assertFails("plugin/credentials/images/%25252E%25252E/images/24x24/credentials.png", HttpServletResponse.SC_BAD_REQUEST); // just checking
// SECURITY-705:
r.createWebClient().assertFails("plugin/credentials/images/..%2fWEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/./credentials.jpi", /* Path collapsed to simply `credentials.jpi` before entering */ HttpServletResponse.SC_NOT_FOUND);
r.createWebClient().assertFails("plugin/credentials/images/%2e%2e%2fWEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/images/%2e.%2fWEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/images/..%2f..%2f..%2f" + r.jenkins.getRootDir().getName() + "%2fsecrets%2fmaster.key", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/" + r.jenkins.getRootDir() + "/secrets/master.key", /* ./ prepended anyway */ HttpServletResponse.SC_NOT_FOUND);
// SECURITY-155:
r.createWebClient().assertFails("plugin/credentials/WEB-INF/licenses.xml", HttpServletResponse.SC_NOT_FOUND);
r.createWebClient().assertFails("plugin/credentials/META-INF/MANIFEST.MF", HttpServletResponse.SC_NOT_FOUND);
r.createWebClient().assertFails("plugin/credentials/WEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/META-INF/MANIFEST.MF", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/web-inf/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/credentials/meta-inf/manifest.mf", HttpServletResponse.SC_BAD_REQUEST);
}
}
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lib.form;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlOption;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.model.RootAction;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestExtension;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
/**
* Tests for lib/form/option.jelly
*/
public class OptionTest {
private static final int MODE_JELLY_REGULAR = 0;
private static final int MODE_JELLY_FORCE_RAW = 1;
private static final int MODE_GROOVY_REGULAR = 0;
private static final int MODE_GROOVY_TEXT = 1;
private static final int MODE_XML_ESCAPE = 2;
private static final int MODE_NATIVE_OPTION = 3;
@Rule
public JenkinsRule j = new JenkinsRule();
@Test
@Issue("SECURITY-624")
public void optionsAreCorrectlyEscaped() throws Exception {
checkNonDangerousOutputCorrect_simple();
checkNonDangerousOutputCorrect_advanced();
checkDangerousOutputNotActive();
}
private void checkNonDangerousOutputCorrect_simple() throws Exception {
String simpleText = "Simple text";
benchOfTest_acceptEscapedCharacters(simpleText, simpleText);
}
private void checkNonDangerousOutputCorrect_advanced() throws Exception {
String advancedText = "Markdown -> HTML & XHTML even with \"'_/$\\< characters";
// all those variants are displayed normally to the user since the escaping is un-escaped by the browser
// for display purpose (and only for display, not executing)
String escapeForValue = escapeForValue(advancedText);
String escapeForBody = escapeForBody(advancedText);
String escapeForBody_alternate = escapeForBody_alternate(advancedText);
// those variants are ugly for the user since they have some escaped visible characters
// they are produced by too much escaping done manually
// those tests are provided to ensure the security,
// normally they are not used since they are displaying the value in a ugly way
// => Markdown -&amp;gt; HTML &amp;amp; XHTML even with &quot;'_/$\&amp;lt; characters
String escapeForBody_uglyButSafe = escapeForBody_uglyButSafe(advancedText);
// => Markdown -&amp;gt; HTML &amp;amp; XHTML even with "'_/$\&amp;lt; characters
String escapeForValue_uglyButSafe = escapeForValue_uglyButSafe(advancedText);
{ // lenient mode
checkJelly(MODE_JELLY_REGULAR, advancedText, advancedText, false);
checkGroovy(MODE_GROOVY_TEXT, advancedText, advancedText, false);
checkGroovy(MODE_XML_ESCAPE, advancedText, advancedText, false);
checkJelly(MODE_NATIVE_OPTION, advancedText, advancedText, advancedText, false, true, false);
checkGroovy(MODE_NATIVE_OPTION, advancedText, advancedText, advancedText, false, true, false);
// those ones were vulnerable before the patch, you can test that by undoing the changes
checkJelly(MODE_JELLY_FORCE_RAW, advancedText, advancedText, false);
checkGroovy(MODE_GROOVY_REGULAR, advancedText, advancedText, false);
// those ones are ugly and the display value is a string with escape characters.
checkJelly(MODE_XML_ESCAPE, advancedText, escapeForBody_alternate, advancedText, false, true, false);
checkJelly(MODE_XML_ESCAPE, advancedText, escapeForBody_alternate, escapeForBody_alternate, false, false, true);
}
{ // in strict mode, we need to provide the exact characters that are expected
checkJelly(MODE_JELLY_REGULAR, advancedText, escapeForBody, escapeForValue, true);
checkGroovy(MODE_GROOVY_TEXT, advancedText, escapeForBody, escapeForValue, true);
checkJelly(MODE_NATIVE_OPTION, advancedText, escapeForBody, escapeForValue, true, true, false);
checkGroovy(MODE_XML_ESCAPE, advancedText, escapeForBody, escapeForValue, true);
checkGroovy(MODE_NATIVE_OPTION, advancedText, escapeForBody_alternate, escapeForValue, true, true, false);
// those ones were vulnerable before the patch, you can test that by undoing the changes
checkJelly(MODE_JELLY_FORCE_RAW, advancedText, escapeForBody, escapeForValue, true);
checkGroovy(MODE_GROOVY_REGULAR, advancedText, escapeForBody, escapeForValue, true);
// ugly display, was already the case, just shown here to be sure of the safety
checkJelly(MODE_XML_ESCAPE, advancedText, escapeForBody_uglyButSafe, escapeForValue, true, true, false);
checkJelly(MODE_XML_ESCAPE, advancedText, escapeForBody_uglyButSafe, escapeForValue_uglyButSafe, true, false, true);
}
}
private String escapeForBody(String str){
return str
.replace("&", "&amp;")
.replace("<", "&lt;")
;
}
private String escapeForBody_alternate(String str){
return str
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
;
}
private String escapeForValue(String str){
return str
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\"", "&quot;")
;
}
private String escapeForBody_uglyButSafe(String str){
return str
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
// double escaping, make the text ugly but it was exactly the same case before
// when we are using too much escaping (manual h.xmlEscape on the body)
.replace("&", "&amp;")
;
}
private String escapeForValue_uglyButSafe(String str){
return str
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
// double escaping, make the text ugly but it was exactly the same case before
// when we are using too much escaping (manual h.xmlEscape on the body)
.replace("&", "&amp;")
// html attribute escaping done after the double escaping
.replace("\"", "&quot;")
;
}
private void checkDangerousOutputNotActive() throws Exception {
// this javascript simply replace the whole document by "hacked" and so the "FLAG" will not be present
// but it will not be executed (thanks to the escaping)
benchOfTest_strictContains(
"<script>function hack(){document.writeln('hacked');};document.addEventListener('DOMContentLoaded', hack, false);</script>_FLAG",
"FLAG"
);
}
private void benchOfTest_strictContains(String msg, String containsExpected) throws Exception {
_benchOfTest(msg, containsExpected, true);
}
private void benchOfTest_acceptEscapedCharacters(String msg, String containsExpected) throws Exception {
_benchOfTest(msg, containsExpected, false);
}
private void _benchOfTest(String msg, String containsExpected, boolean checkExactCharacters) throws Exception {
checkJelly(MODE_JELLY_REGULAR, msg, containsExpected, checkExactCharacters);
checkGroovy(MODE_GROOVY_TEXT, msg, containsExpected, checkExactCharacters);
checkJelly(MODE_XML_ESCAPE, msg, containsExpected, checkExactCharacters);
checkJelly(MODE_NATIVE_OPTION, msg, containsExpected, containsExpected, checkExactCharacters, true, false);
checkGroovy(MODE_XML_ESCAPE, msg, containsExpected, checkExactCharacters);
checkGroovy(MODE_NATIVE_OPTION, msg, containsExpected, containsExpected, checkExactCharacters, true, false);
// those ones were vulnerable before the patch, you can test that by undoing the changes
checkJelly(MODE_JELLY_FORCE_RAW, msg, containsExpected, checkExactCharacters);
checkGroovy(MODE_GROOVY_REGULAR, msg, containsExpected, checkExactCharacters);
}
private void checkJelly(int mode, String msgToInject, String bothContainsExpected, boolean checkExactCharacters) throws Exception {
checkJelly(mode, msgToInject, bothContainsExpected, bothContainsExpected, checkExactCharacters);
}
private void checkJelly(int mode, String msgToInject,
String bodyContainsExpected, String valueContainsExpected,
boolean checkExactCharacters) throws Exception {
checkJelly(mode, msgToInject, bodyContainsExpected, valueContainsExpected, checkExactCharacters, true, true);
}
private void checkJelly(int mode, String msgToInject,
String bodyContainsExpected, String valueContainsExpected,
boolean checkExactCharacters,
boolean withValueTrue, boolean withValueFalse) throws Exception {
UsingJellyView view = j.jenkins.getExtensionList(UsingJellyView.class).get(0);
view.setMode(mode);
view.setInjection(msgToInject);
if(withValueTrue){
view.setWithValue(true);
callPageAndCheckIfResultContainsExpected("usingJelly", bodyContainsExpected, valueContainsExpected, checkExactCharacters);
}
if(withValueFalse){
view.setWithValue(false);
callPageAndCheckIfResultContainsExpected("usingJelly", bodyContainsExpected, valueContainsExpected, checkExactCharacters);
}
}
private void checkGroovy(int mode, String msgToInject, String bothContainsExpected, boolean checkExactCharacters) throws Exception {
checkGroovy(mode, msgToInject, bothContainsExpected, bothContainsExpected, checkExactCharacters);
}
private void checkGroovy(int mode, String msgToInject, String bodyContainsExpected, String valueContainsExpected, boolean checkExactCharacters) throws Exception {
checkGroovy(mode, msgToInject, bodyContainsExpected, valueContainsExpected, checkExactCharacters, true, true);
}
private void checkGroovy(int mode, String msgToInject,
String bodyContainsExpected, String valueContainsExpected,
boolean checkExactCharacters,
boolean withValueTrue, boolean withValueFalse) throws Exception {
UsingGroovyView view = j.jenkins.getExtensionList(UsingGroovyView.class).get(0);
view.setMode(mode);
view.setInjection(msgToInject);
if(withValueTrue){
view.setWithValue(true);
callPageAndCheckIfResultContainsExpected("usingGroovy", bodyContainsExpected, valueContainsExpected, checkExactCharacters);
}
if(withValueFalse){
view.setWithValue(false);
callPageAndCheckIfResultContainsExpected("usingGroovy", bodyContainsExpected, valueContainsExpected, checkExactCharacters);
}
}
private void callPageAndCheckIfResultContainsExpected(String url, String bodyContainsExpected, String valueContainsExpected, boolean checkExactCharacters) throws Exception {
HtmlPage page = (HtmlPage) j.createWebClient().goTo(url, null);
String responseContent = page.getWebResponse().getContentAsString();
if(checkExactCharacters){
// in this mode, we check the data directly received by the response,
// without any un-escaping done by HtmlElement
// first value shown as value
int indexOfValue = responseContent.indexOf(valueContainsExpected);
assertTrue(indexOfValue != -1);
// second as body
int indexOfBody = responseContent.indexOf(bodyContainsExpected, indexOfValue + 1);
assertTrue(indexOfBody != -1);
// also check there is no "<script>" present in the answer
int indexOfScript = responseContent.indexOf("<script>");
assertEquals(-1, indexOfScript);
}else{
// in this mode, we check the content as displayed to the user, converting all the escaped characters to
// their un-escaped equivalent, done by com.gargoylesoftware.htmlunit.html.HtmlSerializer#cleanUp(String)
HtmlElement document = page.getDocumentElement();
DomNodeList<HtmlElement> elements = document.getElementsByTagName("option");
assertEquals(1, elements.size());
HtmlOption option = (HtmlOption) elements.get(0);
// without that check, the getValueAttribute could return getText if the value is not present
assertNotEquals(DomElement.ATTRIBUTE_NOT_DEFINED, option.getAttribute("value"));
assertTrue(
"Value attribute does not contain the expected value",
option.getValueAttribute().contains(valueContainsExpected)
);
assertTrue(
"Body content of the option does not contain the expected value",
option.getText().contains(bodyContainsExpected)
);
}
}
@TestExtension("optionsAreCorrectlyEscaped")
public static class UsingJellyView implements RootAction {
private String injection;
private int mode;
private boolean withValue;
public String getInjection() {
return injection;
}
public void setInjection(String injection) {
this.injection = injection;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public boolean isWithValue() {
return withValue;
}
public void setWithValue(boolean withValue) {
this.withValue = withValue;
}
@Override
public String getIconFileName() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getUrlName() {
return "usingJelly";
}
}
@TestExtension("optionsAreCorrectlyEscaped")
public static class UsingGroovyView implements RootAction {
private String injection;
private int mode;
private boolean withValue;
public String getInjection() {
return injection;
}
public void setInjection(String injection) {
this.injection = injection;
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
public boolean isWithValue() {
return withValue;
}
public void setWithValue(boolean withValue) {
this.withValue = withValue;
}
@Override
public String getIconFileName() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public String getUrlName() {
return "usingGroovy";
}
}
}
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lib.form
import hudson.Functions
f=namespace(lib.FormTagLib)
h=Functions
html{ ->
body{ ->
select { ->
if (it.withValue) {
if (it.mode == 0) {
f.option(value: it.injection, it.injection)
}
if (it.mode == 1) {
f.option(value: it.injection) { ->
text(it.injection)
}
}
if (it.mode == 2) {
f.option(value: it.injection, h.xmlEscape(it.injection))
}
if (it.mode == 3) {
option(value: it.injection, it.injection)
}
} else {
if (it.mode == 0) {
f.option(it.injection)
}
if (it.mode == 1) {
f.option { ->
text(it.injection)
}
}
if (it.mode == 2) {
f.option(h.xmlEscape(it.injection))
}
}
}
}
}
<!--
The MIT License
Copyright (c) 2017, CloudBees, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<j:new var="h" className="hudson.Functions" />
<html>
<body>
<select>
<j:if test="${it.withValue}">
<j:if test="${it.mode == 0}">
<f:option value="${it.injection}">${it.injection}</f:option>
</j:if>
<j:if test="${it.mode == 1}">
<f:option value="${it.injection}"><j:out value="${it.injection}" /></f:option>
</j:if>
<j:if test="${it.mode == 2}">
<!--double escaping done, the value and the display value are badly escaped, but at least secured-->
<f:option value="${it.injection}">${h.xmlEscape(it.injection)}</f:option>
</j:if>
<j:if test="${it.mode == 3}">
<option value="${it.injection}">${it.injection}</option>
</j:if>
</j:if>
<j:if test="${!it.withValue}">
<j:if test="${it.mode == 0}">
<f:option>${it.injection}</f:option>
</j:if>
<j:if test="${it.mode == 1}">
<f:option><j:out value="${it.injection}" /></f:option>
</j:if>
<j:if test="${it.mode == 2}">
<!--double escaping done, the value and the display value are badly escaped, but at least secured-->
<f:option>${h.xmlEscape(it.injection)}</f:option>
</j:if>
</j:if>
</select>
</body>
</html>
</j:jelly>
......@@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>2.107-SNAPSHOT</version>
<version>2.108-SNAPSHOT</version>
</parent>
<artifactId>jenkins-war</artifactId>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册