提交 9e68fed9 编写于 作者: K Kohsuke Kawaguchi

merged back the RC branch

Conflicts:
	core/src/main/java/jenkins/model/Jenkins.java
	test/src/test/java/hudson/bugs/JnlpAccessWithSecuredHudsonTest.java
......@@ -80,6 +80,9 @@ Upcoming changes</a>
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.502>What's new in 1.502</a> <!--=DATE=--></h3>
<ul class=image>
<li class='major bug'>
Miscellaneous security vulnerability fixes. See the advisory for more details.
(SECURITY-13,16,46,47,54,55,59,60,61)
<li class='major bug'>
Builds disappear from build history after completion.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-15156">issue 15156</a>)
......
......@@ -43,6 +43,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.199</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
</properties>
<dependencies>
......@@ -422,18 +423,18 @@ THE SOFTWARE.
Ideally we should be able to modify BeanBuilder so as not to depend on this.
-->
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5</version>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5</version>
<version>${spring.version}</version>
</dependency>
<dependency><!-- Jenkins core doesn't use it but HUDSON-3811 requires us to put it. -->
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5</version>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>
......
......@@ -88,6 +88,7 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import org.kohsuke.stapler.interceptor.RequirePOST;
/**
* Base implementation of {@link Run}s that build software.
......@@ -1360,6 +1361,7 @@ public abstract class AbstractBuild<P extends AbstractProject<P,R>,R extends Abs
*
* @since 1.489
*/
@RequirePOST
public synchronized HttpResponse doStop() throws IOException, ServletException {
Executor e = getExecutor();
if (e==null)
......
......@@ -1752,10 +1752,17 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
*/
public void doBuild( StaplerRequest req, StaplerResponse rsp, @QueryParameter TimeDuration delay ) throws IOException, ServletException {
if (delay==null) delay=new TimeDuration(getQuietPeriod());
BuildAuthorizationToken.checkPermission(this, authToken, req, rsp);
// if a build is parameterized, let that take over
ParametersDefinitionProperty pp = getProperty(ParametersDefinitionProperty.class);
if (pp != null && !req.getMethod().equals("POST")) {
// show the parameter entry form.
req.getView(pp, "index.jelly").forward(req, rsp);
return;
}
BuildAuthorizationToken.checkPermission(this, authToken, req, rsp);
if (pp != null) {
pp._doBuild(req,rsp,delay);
return;
......@@ -1765,7 +1772,7 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
throw HttpResponses.error(SC_INTERNAL_SERVER_ERROR,new IOException(getFullName()+" is not buildable"));
Jenkins.getInstance().getQueue().schedule(this, (int)delay.getTime(), getBuildCause(req));
rsp.forwardToPreviousPage(req);
rsp.sendRedirect(".");
}
/**
......@@ -1825,12 +1832,13 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
public void doPolling( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
BuildAuthorizationToken.checkPermission(this, authToken, req, rsp);
schedulePolling();
rsp.forwardToPreviousPage(req);
rsp.sendRedirect(".");
}
/**
* Cancels a scheduled build.
*/
@RequirePOST
public void doCancelQueue( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
checkPermission(ABORT);
......@@ -1906,6 +1914,13 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
triggers = buildDescribable(req, Trigger.for_(this));
for (Trigger t : triggers)
t.start(this,true);
for (Publisher _t : Descriptor.newInstancesFromHeteroList(req, json, "publisher", Jenkins.getInstance().getExtensionList(BuildTrigger.DescriptorImpl.class))) {
BuildTrigger t = (BuildTrigger) _t;
for (AbstractProject downstream : t.getChildProjects(this)) {
downstream.checkPermission(BUILD);
}
}
}
/**
......
......@@ -45,6 +45,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -153,15 +154,14 @@ public class Api extends AbstractModelObject {
OutputStream o = rsp.getCompressedOutputStream(req);
try {
if(result instanceof CharacterData) {
rsp.setContentType("text/plain;charset=UTF-8");
o.write(((CharacterData)result).getText().getBytes("UTF-8"));
return;
}
if(result instanceof String || result instanceof Number || result instanceof Boolean) {
rsp.setContentType("text/plain;charset=UTF-8");
o.write(result.toString().getBytes("UTF-8"));
if (result instanceof CharacterData || result instanceof String || result instanceof Number || result instanceof Boolean) {
if (INSECURE) {
rsp.setContentType("text/plain;charset=UTF-8");
String text = result instanceof CharacterData ? ((CharacterData) result).getText() : result.toString();
o.write(text.getBytes("UTF-8"));
} else {
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "primitive XPath result sets forbidden; can use -Dhudson.model.Api.INSECURE=true if you run without security");
}
return;
}
......@@ -188,8 +188,12 @@ public class Api extends AbstractModelObject {
* Exposes the bean as JSON.
*/
public void doJson(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if (INSECURE || req.getParameter("jsonp") == null) {
setHeaders(rsp);
rsp.serveExposedBean(req,bean, Flavor.JSON);
rsp.serveExposedBean(req,bean, Flavor.JSON);
} else {
rsp.sendError(HttpURLConnection.HTTP_FORBIDDEN, "jsonp forbidden; can use -Dhudson.model.Api.INSECURE=true if you run without security");
}
}
/**
......@@ -207,4 +211,6 @@ public class Api extends AbstractModelObject {
private static final Logger LOGGER = Logger.getLogger(Api.class.getName());
private static final ModelBuilder MODEL_BUILDER = new ModelBuilder();
private static final boolean INSECURE = "true".equals(System.getProperty("hudson.model.Api.INSECURE"));
}
......@@ -31,7 +31,9 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import java.io.IOException;
import jenkins.security.ApiTokenProperty;
import org.acegisecurity.AccessDeniedException;
import org.kohsuke.stapler.HttpResponses;
/**
* Authorization token to allow projects to trigger themselves under the secured environment.
......@@ -73,6 +75,16 @@ public final class BuildAuthorizationToken {
}
project.checkPermission(AbstractProject.BUILD);
if (req.getMethod().equals("POST")) {
return;
}
if (req.getAttribute(ApiTokenProperty.class.getName()) instanceof User) {
return;
}
throw HttpResponses.forwardToView(project, "requirePOST.jelly");
}
public String getToken() {
......
......@@ -936,7 +936,10 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
for (Object o : JSONArray.fromObject(formData)) {
JSONObject jo = (JSONObject)o;
String kind = jo.getString("kind");
items.add(find(descriptors,kind).newInstance(req,jo));
Descriptor<T> d = find(descriptors, kind);
if (d != null) {
items.add(d.newInstance(req, jo));
}
}
}
......@@ -946,7 +949,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
/**
* Finds a descriptor from a collection by its class name.
*/
public static <T extends Descriptor> T find(Collection<? extends T> list, String className) {
public static @CheckForNull <T extends Descriptor> T find(Collection<? extends T> list, String className) {
for (T d : list) {
if(d.getClass().getName().equals(className))
return d;
......@@ -960,7 +963,7 @@ public abstract class Descriptor<T extends Describable<T>> implements Saveable {
return null;
}
public static Descriptor find(String className) {
public static @CheckForNull Descriptor find(String className) {
return find(Jenkins.getInstance().getExtensionList(Descriptor.class),className);
}
......
......@@ -57,6 +57,7 @@ import java.lang.reflect.Method;
import static hudson.model.queue.Executables.*;
import static java.util.logging.Level.FINE;
import org.kohsuke.stapler.interceptor.RequirePOST;
/**
......@@ -468,6 +469,7 @@ public class Executor extends Thread implements ModelObject {
* @deprecated as of 1.489
* Use {@link #doStop()}.
*/
@RequirePOST
public void doStop( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
doStop().generateResponse(req,rsp,this);
}
......
......@@ -113,11 +113,6 @@ public class ParametersDefinitionProperty extends JobProperty<AbstractProject<?,
* This method is supposed to be invoked from {@link AbstractProject#doBuild(StaplerRequest, StaplerResponse, TimeDuration)}.
*/
public void _doBuild(StaplerRequest req, StaplerResponse rsp, @QueryParameter TimeDuration delay) throws IOException, ServletException {
if(!req.getMethod().equals("POST")) {
// show the parameter entry form.
req.getView(this,"index.jelly").forward(req,rsp);
return;
}
if (delay==null) delay=new TimeDuration(owner.getQuietPeriod());
......
......@@ -106,6 +106,7 @@ import org.kohsuke.stapler.export.ExportedBean;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.interceptor.RequirePOST;
/**
* Build queue.
......@@ -612,6 +613,7 @@ public class Queue extends ResourceController implements Saveable {
/**
* Called from {@code queue.jelly} and {@code entries.jelly}.
*/
@RequirePOST
public HttpResponse doCancelItem(@QueryParameter int id) throws IOException, ServletException {
Item item = getItem(id);
if (item != null) {
......@@ -1435,6 +1437,7 @@ public class Queue extends ResourceController implements Saveable {
/** @deprecated Use {@link #doCancelItem} instead. */
@Deprecated
@RequirePOST
public HttpResponse doCancelQueue() throws IOException, ServletException {
Jenkins.getInstance().getQueue().cancel(this);
return HttpResponses.forwardToPreviousPage();
......
......@@ -21,6 +21,13 @@ import hudson.model.Api;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.util.MultipartFormDataParser;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.ServletException;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerResponse;
/**
* A CrumbIssuer represents an algorithm to generate a nonce value, known as a
......@@ -153,7 +160,7 @@ public abstract class CrumbIssuer implements Describable<CrumbIssuer>, Extension
}
public Api getApi() {
return new Api(this);
return new RestrictedApi(this);
}
/**
......@@ -180,4 +187,45 @@ public abstract class CrumbIssuer implements Describable<CrumbIssuer>, Extension
}
});
}
@Restricted(NoExternalUse.class)
public static class RestrictedApi extends Api {
RestrictedApi(CrumbIssuer instance) {
super(instance);
}
@Override public void doXml(StaplerRequest req, StaplerResponse rsp, @QueryParameter String xpath, @QueryParameter String wrapper, @QueryParameter String tree, @QueryParameter int depth) throws IOException, ServletException {
String text;
CrumbIssuer ci = (CrumbIssuer) bean;
if ("/*/crumbRequestField/text()".equals(xpath)) { // old FullDuplexHttpStream
text = ci.getCrumbRequestField();
} else if ("/*/crumb/text()".equals(xpath)) { // ditto
text = ci.getCrumb();
} else if ("concat(//crumbRequestField,\":\",//crumb)".equals(xpath)) { // new FullDuplexHttpStream; Main
text = ci.getCrumbRequestField() + ':' + ci.getCrumb();
} else if ("concat(//crumbRequestField,'=',//crumb)".equals(xpath)) { // NetBeans
if (ci.getCrumbRequestField().startsWith(".")) {
text = ci.getCrumbRequestField() + '=' + ci.getCrumb();
} else {
text = null;
}
} else {
text = null;
}
if (text != null) {
OutputStream o = rsp.getCompressedOutputStream(req);
try {
rsp.setContentType("text/plain;charset=UTF-8");
o.write(text.getBytes("UTF-8"));
} finally {
o.close();
}
} else {
super.doXml(req, rsp, xpath, wrapper, tree, depth);
}
}
}
}
......@@ -319,7 +319,7 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer {
/**
* Form validation method.
*/
public FormValidation doCheck(@AncestorInPath Item project, @QueryParameter String value ) {
public FormValidation doCheck(@AncestorInPath Item project, @QueryParameter String value, @QueryParameter boolean upstream) {
// Require CONFIGURE permission on this project
if(!project.hasPermission(Item.CONFIGURE)) return FormValidation.ok();
......@@ -334,6 +334,9 @@ public class BuildTrigger extends Recorder implements DependencyDeclarer {
AbstractProject.findNearest(projectName,project.getParent()).getRelativeNameFrom(project)));
if(!(item instanceof AbstractProject))
return FormValidation.error(Messages.BuildTrigger_NotBuildable(projectName));
if (!upstream && !item.hasPermission(Item.BUILD)) {
return FormValidation.error(Messages.BuildTrigger_you_have_no_permission_to_build_(projectName));
}
hasProjects = true;
}
}
......
......@@ -51,6 +51,7 @@ public class ApiTokenFilter implements Filter {
// as the user might be passing in a real password.
SecurityContext oldContext = ACL.impersonate(u.impersonate());
try {
request.setAttribute(ApiTokenProperty.class.getName(), u);
chain.doFilter(request,response);
return;
} finally {
......
......@@ -50,7 +50,7 @@ THE SOFTWARE.
</f:optionalBlock>
<f:entry title="${%Display Name}" field="displayNameOrNull">
<f:textbox checkUrl="'${rootURL}/checkDisplayName?displayName='+encodeURIComponent(this.value)+'&amp;jobName='+encodeURIComponent('${it.name}')"/>
<f:textbox checkUrl="'${rootURL}/checkDisplayName?displayName='+encodeURIComponent(this.value)+'&amp;jobName='+encodeURIComponent('${h.jsStringEscape(it.name)}')"/>
</f:entry>
</f:advanced>
</f:section>
......
......@@ -63,7 +63,7 @@ THE SOFTWARE.
<p:config-blockWhenDownstreamBuilding />
<st:include page="configure-advanced.jelly" optional="true" />
<f:entry title="${%Display Name}" field="displayNameOrNull">
<f:textbox checkUrl="'${rootURL}/checkDisplayName?displayName='+encodeURIComponent(this.value)+'&amp;jobName='+encodeURIComponent('${it.name}')"/>
<f:textbox checkUrl="'${rootURL}/checkDisplayName?displayName='+encodeURIComponent(this.value)+'&amp;jobName='+encodeURIComponent('${h.jsStringEscape(it.name)}')"/>
</f:entry>
</f:advanced>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright 2013 Jesse Glick.
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:st="jelly:stapler" xmlns:d="jelly:define" xmlns:i="jelly:fmt" xmlns:l="/lib/layout">
<l:layout title="${%Form post required}">
<l:main-panel>
<p>${%use_post}</p>
<form method="POST">
<input type="submit" value="${%Proceed}"/>
</form>
</l:main-panel>
</l:layout>
</j:jelly>
# The MIT License
#
# Copyright 2013 Jesse Glick.
#
# 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.
use_post=\
You must use POST method to trigger builds. \
(From scripts you may instead pass a per-project authentication token, or authenticate with your API token.) \
If you see this page, it may be because a plugin offered a GET link; file a bug report for that plugin.
......@@ -47,6 +47,7 @@ BuildTrigger.NoSuchProject=No such project ''{0}''. Did you mean ''{1}''?
BuildTrigger.NoProjectSpecified=No project specified
BuildTrigger.NotBuildable={0} is not buildable
BuildTrigger.Triggering=Triggering a new build of {0}
BuildTrigger.you_have_no_permission_to_build_=You have no permission to build {0}
CommandInterpreter.CommandFailed=command execution failed
CommandInterpreter.UnableToDelete=Unable to delete script file {0}
......
......@@ -29,8 +29,16 @@ THE SOFTWARE.
<a href="${jobBaseUrl}${job.shortUrl}build?delay=0sec">
<img src="${imagesURL}/${subIconSize}/clock.png"
title="${%Schedule a build}" alt="${%Schedule a build}"
onclick="${job.parameterized ? null : 'return build(this)'}"
border="0"/>
</a>
<script>
function build(img) {
new Ajax.Request(img.parentNode.href);
hoverNotification('${%Build scheduled}', img, -100);
return false;
}
</script>
</j:if>
</td>
</j:jelly>
\ No newline at end of file
......@@ -43,9 +43,7 @@ THE SOFTWARE.
<td style="white-space:normal;" colspan="2">
<div style="float:right">
<j:if test="${item.hasCancelPermission()}">
<a href="${rootURL}/queue/cancelItem?id=${item.id}">
<img src="${imagesURL}/16x16/stop.png" alt="${%cancel this build}" height="16" width="16"/>
</a>
<l:stopButton href="${rootURL}/queue/cancelItem?id=${item.id}" alt="${%cancel this build}"/>
</j:if>
</div>
<j:set var="cause" value="${item.getCauseOfBlockage()}"/>
......
......@@ -58,7 +58,7 @@ THE SOFTWARE.
</td><td style="padding:0">
<!-- Check ABORT permission for Project, Admin permission otherwise -->
<j:if test="${empty(it.owner.ABORT) ? h.hasPermission(app.ADMINISTER) : it.owner.hasPermission(it.owner.ABORT)}">
<a href="${link}stop"><img src="${imagesURL}/16x16/stop.png" alt="[cancel]" height="16" width="16"/></a>
<l:stopButton href="${link}stop" alt="[cancel]"/>
</j:if>
</td></tr>
</table>
......
......@@ -37,7 +37,7 @@ THE SOFTWARE.
<t:buildProgressBar build="${it}"/>
</td><td>
<j:if test="${it.parent.hasAbortPermission()}">
<a href="stop"><img src="${imagesURL}/16x16/stop.png" alt="[${%cancel}]" height="16" width="16" /></a>
<l:stopButton href="stop" alt="[${%cancel}]"/>
</j:if>
</td>
</tr></table>
......
......@@ -91,7 +91,7 @@ THE SOFTWARE.
</st:include>
<td class="pane" align="center" valign="middle">
<j:if test="${e.hasStopPermission()}">
<a href="${rootURL}/${c.url}${url}/stop"><img src="${imagesURL}/16x16/stop.png" alt="${%terminate this build}" height="16" width="16" /></a>
<l:stopButton href="${rootURL}/${c.url}${url}/stop" alt="${%terminate this build}"/>
</j:if>
</td>
</j:otherwise>
......
......@@ -38,7 +38,7 @@ THE SOFTWARE.
<f:entry title="${%Project names}"
description="${%Multiple projects can be specified like 'abc, def'}">
<f:textbox name="upstreamProjects" value="${h.getProjectListString(up)}"
checkUrl="'descriptorByName/hudson.tasks.BuildTrigger/check?value='+encodeURIComponent(this.value)"
checkUrl="'descriptorByName/hudson.tasks.BuildTrigger/check?upstream=true&amp;value='+encodeURIComponent(this.value)"
autoCompleteField="upstreamProjects"/>
</f:entry>
</f:optionalBlock>
......
......@@ -79,7 +79,7 @@ THE SOFTWARE.
</td>
<td class="pane" width="16" align="center" valign="middle">
<j:if test="${item.hasCancelPermission()}">
<a href="${rootURL}/queue/cancelItem?id=${item.id}"><img src="${imagesURL}/16x16/stop.png" alt="cancel this build" height="16" width="16"/></a>
<l:stopButton href="${rootURL}/queue/cancelItem?id=${item.id}" alt="cancel this build"/>
</j:if>
</td>
</tr>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright 2013 Jesse Glick.
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:st="jelly:stapler" xmlns:d="jelly:define" xmlns:i="jelly:fmt">
<st:documentation>
Creates a clickable “Stop” button.
<st:attribute name="href" use="required">
Link target. Relative to the current page.
</st:attribute>
<st:attribute name="alt" use="required">
Alt text for image.
</st:attribute>
</st:documentation>
<a href="${href}" onclick="new Ajax.Request(this.href); return false">
<img src="${imagesURL}/16x16/stop.png" alt="${alt}" height="16" width="16"/>
</a>
</j:jelly>
jenkins (1.502) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
-- Kohsuke Kawaguchi <kk@kohsuke.org> Sat, 16 Feb 2013 08:33:01 -0800
jenkins (1.501) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
......
......@@ -93,11 +93,6 @@ complete {
}
match("nekohtml:xercesMinimal:1.9.6.2") {
if (dependency.licenses.isEmpty())
rewriteLicense([], apacheLicense)
}
//
// Choose from multi-licensed modules
//==========================================================================
......
......@@ -301,17 +301,6 @@ THE SOFTWARE.
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-webdav</artifactId>
</exclusion>
<!-- prefer net.sourceforge.nekohtml:nekohtml:jar:1.9.13 so that we use consistent version across Jenkins -->
<exclusion>
<groupId>nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</exclusion>
<exclusion>
<groupId>nekohtml</groupId>
<artifactId>xercesMinimal</artifactId>
</exclusion>
</exclusions>
</dependency>
......@@ -330,7 +319,7 @@ THE SOFTWARE.
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
<!-- prefer net.sourceforge.nekohtml:nekohtml:jar:1.9.13 so that we use consistent version across Jenkins -->
<!-- SECURITY-61: unused -->
<exclusion>
<groupId>nekohtml</groupId>
<artifactId>nekohtml</artifactId>
......@@ -342,12 +331,6 @@ THE SOFTWARE.
</exclusions>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>token-macro</artifactId>
......
......@@ -177,6 +177,7 @@ import com.gargoylesoftware.htmlunit.DefaultCssErrorHandler;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
......@@ -186,6 +187,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.HtmlUnitContextFactory;
import com.gargoylesoftware.htmlunit.javascript.host.xml.XMLHttpRequest;
import com.gargoylesoftware.htmlunit.xml.XmlPage;
import java.net.HttpURLConnection;
/**
* Base class for all Jenkins test cases.
......@@ -1852,6 +1854,19 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
return null;
}
/**
* Verify that the server rejects an attempt to load the given page.
* @param url a URL path (relative to Jenkins root)
* @param statusCode the expected failure code (such as {@link HttpURLConnection#HTTP_FORBIDDEN})
* @since 1.502
*/
public void assertFails(String url, int statusCode) throws Exception {
try {
fail(url + " should have been rejected but produced: " + super.getPage(getContextPath() + url).getWebResponse().getContentAsString());
} catch (FailingHttpStatusCodeException x) {
assertEquals(statusCode, x.getStatusCode());
}
}
/**
* Returns the URL of the webapp top page.
......
......@@ -72,8 +72,10 @@ public class ApiTest extends HudsonTestCase {
public void testUnwrappedLongString() throws Exception {
jenkins.setSystemMessage("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
Page page = new WebClient().goTo("api/xml?xpath=/hudson/description/text()", "text/plain");
assertEquals(jenkins.getSystemMessage(), page.getWebResponse().getContentAsString());
Page page = new WebClient().goTo("api/xml?xpath=/hudson/description", "application/xml");
assertEquals(
"<description>"+jenkins.getSystemMessage()+"</description>",
page.getWebResponse().getContentAsString());
}
public void testUnwrappedMultipleItems() throws Exception {
......
......@@ -8,6 +8,7 @@ package hudson.security.csrf;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import java.net.HttpURLConnection;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.HudsonTestCase;
......@@ -95,4 +96,31 @@ public class DefaultCrumbIssuerTest extends HudsonTestCase {
// The crumb should still match if we remove the proxy info
submit(p.getFormByName("config"));
}
public void testApiXml() throws Exception {
WebClient wc = new WebClient();
assertXPathValue(wc.goToXml("crumbIssuer/api/xml"), "//crumbRequestField", jenkins.getCrumbIssuer().getCrumbRequestField());
String text = wc.goTo("crumbIssuer/api/xml?xpath=concat(//crumbRequestField,'=',//crumb)", "text/plain").getWebResponse().getContentAsString();
assertTrue(text, text.matches("\\Q" + jenkins.getCrumbIssuer().getCrumbRequestField() + "\\E=[0-9a-f]+"));
text = wc.goTo("crumbIssuer/api/xml?xpath=concat(//crumbRequestField,\":\",//crumb)", "text/plain").getWebResponse().getContentAsString();
assertTrue(text, text.matches("\\Q" + jenkins.getCrumbIssuer().getCrumbRequestField() + "\\E:[0-9a-f]+"));
text = wc.goTo("crumbIssuer/api/xml?xpath=/*/crumbRequestField/text()", "text/plain").getWebResponse().getContentAsString();
assertEquals(jenkins.getCrumbIssuer().getCrumbRequestField(), text);
text = wc.goTo("crumbIssuer/api/xml?xpath=/*/crumb/text()", "text/plain").getWebResponse().getContentAsString();
assertTrue(text, text.matches("[0-9a-f]+"));
wc.assertFails("crumbIssuer/api/xml?xpath=concat('hack=\"',//crumb,'\"')", HttpURLConnection.HTTP_FORBIDDEN);
wc.assertFails("crumbIssuer/api/xml?xpath=concat(\"hack='\",//crumb,\"'\")", HttpURLConnection.HTTP_FORBIDDEN);
wc.assertFails("crumbIssuer/api/xml?xpath=concat('{',//crumb,':1}')", HttpURLConnection.HTTP_FORBIDDEN); // 37.5% chance that crumb ~ /[a-f].+/
wc.assertFails("crumbIssuer/api/xml?xpath=concat('hack.',//crumb,'=1')", HttpURLConnection.HTTP_FORBIDDEN); // ditto
jenkins.getCrumbIssuer().getDescriptor().setCrumbRequestField("_crumb");
wc.assertFails("crumbIssuer/api/xml?xpath=concat(//crumbRequestField,'=',//crumb)", HttpURLConnection.HTTP_FORBIDDEN); // perhaps interpretable as JS number
}
public void testApiJson() throws Exception {
WebClient wc = new WebClient();
String json = wc.goTo("crumbIssuer/api/json", "application/json").getWebResponse().getContentAsString();
assertTrue(json, json.matches("\\Q{\"crumb\":\"\\E[0-9a-f]+\\Q\",\"crumbRequestField\":\"" + jenkins.getCrumbIssuer().getCrumbRequestField() + "\"}\\E"));
wc.assertFails("crumbIssuer/api/json?jsonp=hack", HttpURLConnection.HTTP_FORBIDDEN);
}
}
......@@ -1649,7 +1649,7 @@ function createSearchBox(searchURL) {
function updatePos() {
function max(a,b) { if(a>b) return a; else return b; }
sizer.innerHTML = box.value;
sizer.innerHTML = box.value.escapeHTML();
var w = max(sizer.offsetWidth,minW.offsetWidth);
box.style.width =
comp.style.width =
......@@ -1919,11 +1919,14 @@ var hoverNotification = (function() {
msgBox.render();
}
return function(title,anchor) {
return function(title, anchor, offset) {
if (typeof offset === 'undefined') {
offset = 48;
}
init();
body.innerHTML = title;
var xy = YAHOO.util.Dom.getXY(anchor);
xy[0] += 48;
xy[0] += offset;
xy[1] += anchor.offsetHeight;
msgBox.cfg.setProperty("xy",xy);
msgBox.show();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册