mirror of https://github.com/jenkinsci/jenkins.git
[JENKINS-75388] Remove `?path` and `?pattern` support from DBS (#10650)
Co-authored-by: Daniel Beck <daniel-beck@users.noreply.github.com>
This commit is contained in:
parent
36dba29e91
commit
15c512cbe0
|
@ -203,15 +203,6 @@ public final class DirectoryBrowserSupport implements HttpResponse {
|
|||
}
|
||||
|
||||
private void serveFile(StaplerRequest2 req, StaplerResponse2 rsp, VirtualFile root, String icon, boolean serveDirIndex) throws IOException, ServletException, InterruptedException {
|
||||
// handle form submission
|
||||
String pattern = req.getParameter("pattern");
|
||||
if (pattern == null)
|
||||
pattern = req.getParameter("path"); // compatibility with Hudson<1.129
|
||||
if (pattern != null && Util.isSafeToRedirectTo(pattern)) { // avoid open redirect
|
||||
rsp.sendRedirect2(pattern);
|
||||
return;
|
||||
}
|
||||
|
||||
String path = getPath(req);
|
||||
if (path.replace('\\', '/').contains("/../")) {
|
||||
// don't serve anything other than files in the artifacts dir
|
||||
|
|
|
@ -33,17 +33,18 @@ THE SOFTWARE.
|
|||
<div class="dirTree">
|
||||
<!-- parent path -->
|
||||
<div class="parentPath">
|
||||
<form action="${backPath}" method="get">
|
||||
<form>
|
||||
<a href="${topPath}">${it.owner.name}</a> /
|
||||
<j:forEach var="p" items="${parentPath}">
|
||||
<a href="${p.href}">${p.title}</a>
|
||||
/
|
||||
</j:forEach>
|
||||
<input type="text" name="pattern" value="${pattern}" class="jenkins-input" />
|
||||
<button class="jenkins-button">
|
||||
<input type="text" name="pattern" value="${pattern}" class="jenkins-input" id="pattern" data-restofpath="${request2.restOfPath}" data-backpath="${backPath}" />
|
||||
<button class="jenkins-button" id="pattern-submit">
|
||||
<l:icon src="symbol-arrow-right" />
|
||||
</button>
|
||||
</form>
|
||||
<st:adjunct includes="hudson.model.DirectoryBrowserSupport.pattern" />
|
||||
</div>
|
||||
<j:choose>
|
||||
<j:when test="${empty(files)}">
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
document
|
||||
.getElementById("pattern-submit")
|
||||
.addEventListener("click", function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
let input = document.getElementById("pattern");
|
||||
let pattern = input.value;
|
||||
let back = input.dataset.backpath;
|
||||
|
||||
let baseurl = back;
|
||||
if (!baseurl.endsWith("/")) {
|
||||
baseurl = baseurl + "/";
|
||||
}
|
||||
baseurl = baseurl + pattern;
|
||||
document.location.href = baseurl;
|
||||
});
|
||||
});
|
|
@ -4,29 +4,56 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
import java.util.List;
|
||||
import jenkins.security.security3501Test.Security3501RootAction;
|
||||
import org.htmlunit.FailingHttpStatusCodeException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import org.jvnet.hudson.test.RealJenkinsRule;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class Security3501Test {
|
||||
// Workaround for https://github.com/jenkinsci/jenkins-test-harness/issues/933
|
||||
private final String contextPath;
|
||||
|
||||
@Rule
|
||||
public RealJenkinsRule jj = new RealJenkinsRule();
|
||||
public RealJenkinsRule jj = new RealJenkinsRule().addSyntheticPlugin(new RealJenkinsRule.SyntheticPlugin(Security3501RootAction.class.getPackage()).shortName("Security3501RootAction"));
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static List<String> contexts() {
|
||||
return List.of("/jenkins", "");
|
||||
}
|
||||
|
||||
public Security3501Test(String contextPath) {
|
||||
jj.withPrefix(contextPath);
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedirects() throws Throwable {
|
||||
jj.then(Security3501Test::_testRedirects);
|
||||
jj.then(new TestRedirectsStep(contextPath));
|
||||
}
|
||||
|
||||
public static void _testRedirects(JenkinsRule j) throws Exception {
|
||||
List<String> prohibitedPaths = List.of("%5C%5Cexample.org", "%5C/example.org", "/%5Cexample.org", "//example.org", "https://example.org", "\\example.org");
|
||||
for (String path : prohibitedPaths) {
|
||||
try (JenkinsRule.WebClient wc = j.createWebClient().withRedirectEnabled(false)) {
|
||||
final FailingHttpStatusCodeException fhsce = Assert.assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("userContent?path=" + path));
|
||||
assertThat(fhsce.getStatusCode(), is(302));
|
||||
assertThat(fhsce.getResponse().getResponseHeaderValue("Location"), is(j.getURL().toExternalForm() + "userContent/"));
|
||||
private record TestRedirectsStep(String context) implements RealJenkinsRule.Step {
|
||||
public void run(JenkinsRule j) throws Exception {
|
||||
List<String> prohibitedPaths = List.of("%5C%5Cexample.org", "%5C/example.org", "/%5Cexample.org", "//example.org", "https://example.org", "\\example.org");
|
||||
for (String path : prohibitedPaths) {
|
||||
try (JenkinsRule.WebClient wc = j.createWebClient().withRedirectEnabled(false)) {
|
||||
final FailingHttpStatusCodeException fhsce = Assert.assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("redirects/content?path=" + path));
|
||||
assertThat(fhsce.getStatusCode(), is(404));
|
||||
}
|
||||
}
|
||||
|
||||
List<String> allowedPaths = List.of("foo", "foo/bar");
|
||||
for (String path : allowedPaths) {
|
||||
try (JenkinsRule.WebClient wc = j.createWebClient().withRedirectEnabled(false)) {
|
||||
final FailingHttpStatusCodeException fhsce = Assert.assertThrows(FailingHttpStatusCodeException.class, () -> wc.goTo("redirects/content?path=" + path));
|
||||
assertThat(fhsce.getStatusCode(), is(302));
|
||||
assertThat(fhsce.getResponse().getResponseHeaderValue("Location"), is(context + "/redirects/" + path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package jenkins.security.security3501Test;
|
||||
|
||||
import hudson.Util;
|
||||
import hudson.model.InvisibleAction;
|
||||
import hudson.model.RootAction;
|
||||
import java.io.IOException;
|
||||
import org.jenkinsci.plugins.variant.OptionalExtension;
|
||||
import org.kohsuke.stapler.StaplerRequest2;
|
||||
import org.kohsuke.stapler.StaplerResponse2;
|
||||
|
||||
@OptionalExtension
|
||||
public class Security3501RootAction extends InvisibleAction implements RootAction {
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "redirects";
|
||||
}
|
||||
|
||||
public void doContent(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException {
|
||||
final String path = req.getParameter("path");
|
||||
if (path != null && Util.isSafeToRedirectTo(path)) {
|
||||
rsp.sendRedirect2(path);
|
||||
return;
|
||||
}
|
||||
rsp.setStatus(404);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
@OptionalPackage(requirePlugins = "Security3501RootAction")
|
||||
package jenkins.security.security3501Test;
|
||||
|
||||
import org.jenkinsci.plugins.variant.OptionalPackage;
|
Loading…
Reference in New Issue