diff --git a/changelog.html b/changelog.html
index 5a721ec994..8d01ce5fca 100644
--- a/changelog.html
+++ b/changelog.html
@@ -82,11 +82,19 @@ Upcoming changes
+
+ -
+ HistoryWidget/entry.jelly throws NullPointerException
+ (issue 15499)
-
ArrayIndexOutOfBoundsException or StringIndexOutOfBoundsException launching processes such as Mercurial polling. (Regression in 1.489.)
(issue 15733)
+ -
+ Fixed an XSS and a few open-redirect problems
-
Support failsafe the same way as surefire in maven2 jobs
-
@@ -95,7 +103,6 @@ Upcoming changes
Invert dependency of maven-plugin and config-file-provider plugin (if config-file-provider is installed, this change requires an update of the config-file-provider to >= 2.3)
(issue 14914)
-
-
diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java
index 4f15ec7786..d2bd7f3776 100644
--- a/core/src/main/java/hudson/Util.java
+++ b/core/src/main/java/hudson/Util.java
@@ -1240,6 +1240,31 @@ public class Util {
return s==null ? s : s.intern();
}
+ /**
+ * Return true if the systemId denotes an absolute URI .
+ *
+ * The same algorithm can be seen in {@link URI}, but
+ * implementing this by ourselves allow it to be more lenient about
+ * escaping of URI.
+ */
+ public static boolean isAbsoluteUri(String uri) {
+ int idx = uri.indexOf(':');
+ if (idx<0) return false; // no ':'. can't be absolute
+
+ // #, ?, and / must not be before ':'
+ return idx<_indexOf(uri, '#') && idx<_indexOf(uri,'?') && idx<_indexOf(uri,'/');
+ }
+
+ /**
+ * Works like {@link String#indexOf(int)} but 'not found' is returned as s.length(), not -1.
+ * This enables more straight-forward comparison.
+ */
+ private static int _indexOf(String s, char ch) {
+ int idx = s.indexOf(ch);
+ if (idx<0) return s.length();
+ return idx;
+ }
+
/**
* Loads a key/value pair string as {@link Properties}
* @since 1.392
diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
index 3d9753a6f9..d924710c76 100644
--- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
+++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java
@@ -137,7 +137,7 @@ public final class DirectoryBrowserSupport implements HttpResponse {
String pattern = req.getParameter("pattern");
if(pattern==null)
pattern = req.getParameter("path"); // compatibility with Hudson<1.129
- if(pattern!=null) {
+ if(pattern!=null && !Util.isAbsoluteUri(pattern)) {// avoid open redirect
rsp.sendRedirect2(pattern);
return;
}
diff --git a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
index faef1690a6..7f282199e8 100644
--- a/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
+++ b/core/src/main/java/hudson/security/AuthenticationProcessingFilter2.java
@@ -31,6 +31,7 @@ import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import hudson.Util;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
@@ -51,6 +52,9 @@ public class AuthenticationProcessingFilter2 extends AuthenticationProcessingFil
if (targetUrl == null)
return getDefaultTargetUrl();
+ if (Util.isAbsoluteUri(targetUrl))
+ return "."; // avoid open redirect
+
// URL returned from determineTargetUrl() is resolved against the context path,
// whereas the "from" URL is resolved against the top of the website, so adjust this.
if(targetUrl.startsWith(request.getContextPath()))
diff --git a/core/src/main/java/hudson/util/Iterators.java b/core/src/main/java/hudson/util/Iterators.java
index 1a33bef083..2b5d88f74b 100644
--- a/core/src/main/java/hudson/util/Iterators.java
+++ b/core/src/main/java/hudson/util/Iterators.java
@@ -23,11 +23,8 @@
*/
package hudson.util;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
-import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -188,6 +185,33 @@ public class Iterators {
};
}
+ /**
+ * Returns an {@link Iterable} that lists items in the normal order
+ * but which hides the base iterator implementation details.
+ *
+ * @since 1.492
+ */
+ public static Iterable wrap(final Iterable base) {
+ return new Iterable() {
+ public Iterator iterator() {
+ final Iterator itr = base.iterator();
+ return new Iterator() {
+ public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ public T next() {
+ return itr.next();
+ }
+
+ public void remove() {
+ itr.remove();
+ }
+ };
+ }
+ };
+ }
+
/**
* Returns a list that represents [start,end).
*
diff --git a/core/src/main/java/hudson/widgets/HistoryWidget.java b/core/src/main/java/hudson/widgets/HistoryWidget.java
index 28f7daf404..a2ec051839 100644
--- a/core/src/main/java/hudson/widgets/HistoryWidget.java
+++ b/core/src/main/java/hudson/widgets/HistoryWidget.java
@@ -26,6 +26,8 @@ package hudson.widgets;
import hudson.Functions;
import hudson.model.ModelObject;
import hudson.model.Run;
+import hudson.util.Iterators;
+
import org.kohsuke.stapler.Header;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.Stapler;
@@ -132,8 +134,10 @@ public class HistoryWidget extends Widget {
trimmed = itr.hasNext(); // if we don't have enough items in the base list, setting this to false will optimize the next getRenderList() invocation.
return updateFirstTransientBuildKey(lst);
}
- } else
- return updateFirstTransientBuildKey(baseList);
+ } else {
+ // to prevent baseList's concrete type from getting picked up by in view
+ return updateFirstTransientBuildKey(Iterators.wrap(baseList));
+ }
}
public boolean isTrimmed() {
diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly
index 38f944a438..c5d99b5778 100644
--- a/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly
+++ b/core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly
@@ -68,7 +68,7 @@ THE SOFTWARE.
|
-
+
|
diff --git a/core/src/test/java/hudson/UtilTest.java b/core/src/test/java/hudson/UtilTest.java
index 4d5cb0fee1..4b5d25ed94 100644
--- a/core/src/test/java/hudson/UtilTest.java
+++ b/core/src/test/java/hudson/UtilTest.java
@@ -291,4 +291,14 @@ public class UtilTest {
}
}
}
+
+ public void testIsAbsoluteUri() {
+ assertTrue(Util.isAbsoluteUri("http://foobar/"));
+ assertTrue(Util.isAbsoluteUri("mailto:kk@kohsuke.org"));
+ assertTrue(Util.isAbsoluteUri("d123://test/"));
+ assertFalse(Util.isAbsoluteUri("foo/bar/abc:def"));
+ assertFalse(Util.isAbsoluteUri("foo?abc:def"));
+ assertFalse(Util.isAbsoluteUri("foo#abc:def"));
+ assertFalse(Util.isAbsoluteUri("foo/bar"));
+ }
}
diff --git a/core/src/test/java/hudson/util/IteratorsTest.java b/core/src/test/java/hudson/util/IteratorsTest.java
index ec0a518f38..5c6df4f4c1 100644
--- a/core/src/test/java/hudson/util/IteratorsTest.java
+++ b/core/src/test/java/hudson/util/IteratorsTest.java
@@ -26,6 +26,7 @@ package hudson.util;
import hudson.util.Iterators.CountingPredicate;
import junit.framework.TestCase;
+import java.util.Iterator;
import java.util.List;
import static java.util.Arrays.*;
@@ -51,6 +52,20 @@ public class IteratorsTest extends TestCase {
assertEquals(3, lst.size());
}
+ public void testWrap() {
+ List lst = Iterators.sequence(1,4);
+ Iterable wrapped = Iterators.wrap(lst);
+ assertFalse(wrapped instanceof List);
+ Iterator iter = wrapped.iterator();
+ assertTrue(iter.hasNext());
+ assertEquals(1,(int)iter.next());
+ assertTrue(iter.hasNext());
+ assertEquals(2,(int)iter.next());
+ assertTrue(iter.hasNext());
+ assertEquals(3,(int)iter.next());
+ assertFalse(iter.hasNext());
+ }
+
public void testLimit() {
assertEquals("[0]",com.google.common.collect.Iterators.toString(Iterators.limit(asList(0,1,2,3,4).iterator(), EVEN)));
assertEquals("[]", com.google.common.collect.Iterators.toString(Iterators.limit(asList(1,2,4,6).iterator(), EVEN)));
diff --git a/debian/debian/changelog b/debian/debian/changelog
index ea3a5a4e79..42137b523c 100644
--- a/debian/debian/changelog
+++ b/debian/debian/changelog
@@ -1,3 +1,9 @@
+jenkins (1.491) unstable; urgency=low
+
+ * See http://jenkins-ci.org/changelog for more details.
+
+ -- Kohsuke Kawaguchi Sun, 18 Nov 2012 17:42:14 -0800
+
jenkins (1.490) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
diff --git a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
index 63dc15e7c5..c7cc481250 100644
--- a/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
+++ b/test/src/main/java/org/jvnet/hudson/test/HudsonTestCase.java
@@ -258,7 +258,7 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
/**
* Number of seconds until the test times out.
*/
- public int timeout = 90;
+ public int timeout = 180;
private volatile Timer timeoutTimer;
diff --git a/test/src/test/groovy/hudson/widgets/HistoryWidgetTest.groovy b/test/src/test/groovy/hudson/widgets/HistoryWidgetTest.groovy
new file mode 100644
index 0000000000..6493c6cec2
--- /dev/null
+++ b/test/src/test/groovy/hudson/widgets/HistoryWidgetTest.groovy
@@ -0,0 +1,23 @@
+package hudson.widgets
+
+import org.jvnet.hudson.test.HudsonTestCase
+import org.jvnet.hudson.test.Bug
+
+/**
+ *
+ *
+ * @author Kohsuke Kawaguchi
+ */
+class HistoryWidgetTest extends HudsonTestCase {
+ @Bug(15499)
+ void testMoreLink() {
+ def p = createFreeStyleProject();
+ for (x in 1..3) {
+ assertBuildStatusSuccess(p.scheduleBuild2(0))
+ }
+
+ def wc = createWebClient()
+ wc.javaScriptEnabled = false
+ wc.goTo("job/${p.name}/buildHistory/all");
+ }
+}
diff --git a/war/pom.xml b/war/pom.xml
index f32bd3ae65..1e68490bb0 100644
--- a/war/pom.xml
+++ b/war/pom.xml
@@ -104,7 +104,7 @@ THE SOFTWARE.
-->
org.jenkins-ci
winstone
- 0.9.10-jenkins-39
+ 0.9.10-jenkins-40
test