提交 ea302e1f 编写于 作者: J Jeff Thompson 提交者: Jenkins CERT CI

[SECURITY-1986]

上级 89ec0c40
......@@ -33,13 +33,16 @@ import hudson.XmlFile;
import hudson.model.Action;
import hudson.model.Descriptor.FormException;
import hudson.model.Failure;
import hudson.model.FileParameterValue;
import hudson.util.*;
import jenkins.model.Jenkins;
import hudson.model.Label;
import hudson.model.Saveable;
import hudson.model.listeners.SaveableListener;
import jenkins.util.SystemProperties;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
......@@ -56,6 +59,8 @@ import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.Nullable;
......@@ -66,6 +71,12 @@ import edu.umd.cs.findbugs.annotations.Nullable;
* @since 1.372
*/
public class LabelAtom extends Label implements Saveable {
private static final Pattern PROHIBITED_DOUBLE_DOT = Pattern.compile(".*\\.\\.[\\\\/].*");
private static /* Script Console modifiable */ boolean ALLOW_FOLDER_TRAVERSAL =
SystemProperties.getBoolean(LabelAtom.class.getName() + ".allowFolderTraversal");
private DescribableList<LabelAtomProperty,LabelAtomPropertyDescriptor> properties =
new DescribableList<>(this);
......@@ -167,6 +178,9 @@ public class LabelAtom extends Label implements Saveable {
}
public void save() throws IOException {
if (isInvalidName()) {
throw new IOException("Invalid label");
}
if(BulkChange.contains(this)) return;
try {
getConfigFile().write(this);
......@@ -206,6 +220,10 @@ public class LabelAtom extends Label implements Saveable {
app.checkPermission(Jenkins.ADMINISTER);
if (isInvalidName()) {
throw new FormException("Invalid label", null);
}
properties.rebuild(req, req.getSubmittedForm(), getApplicablePropertyDescriptors());
this.description = req.getSubmittedForm().getString("description");
......@@ -216,6 +234,10 @@ public class LabelAtom extends Label implements Saveable {
FormApply.success(".").generateResponse(req, rsp, null);
}
private boolean isInvalidName() {
return !ALLOW_FOLDER_TRAVERSAL && PROHIBITED_DOUBLE_DOT.matcher(name).matches();
}
/**
* Accepts the new description.
*/
......
/*
* The MIT License
*
* Copyright (c) 2020, 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 hudson.model.labels;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import hudson.XmlFile;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import java.io.IOException;
import java.net.HttpURLConnection;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class LabelAtomSecurity1986Test {
@Rule
public JenkinsRule j = new JenkinsRule();
@Test
public void nonexisting() throws Exception {
LabelAtom nonexistent = j.jenkins.getLabelAtom("nonexistent");
XmlFile configFile = nonexistent.getConfigFile();
assertFalse(configFile.getFile().exists());
}
@Test
public void normal() throws Exception {
j.submit(j.createWebClient().goTo("labelAtom/foo/configure").getFormByName("config"));
LabelAtom foo = j.jenkins.getLabelAtom("foo");
XmlFile configFile = foo.getConfigFile();
assertTrue(configFile.getFile().exists());
assertThat(configFile.getFile().getParentFile().getName(), equalTo("labels"));
}
@Test
@Issue("SECURITY-1986")
public void startsWithDoubleDotSlash() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/..%2ffoo/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("../foo");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test
public void startsWithSlash() throws Exception {
// Not a great result, but it works and doesn't cause problems.
j.submit(j.createWebClient().goTo("labelAtom/%2ffoo/configure").getFormByName("config"));
LabelAtom foo = j.jenkins.getLabelAtom("/foo");
XmlFile configFile = foo.getConfigFile();
assertTrue(configFile.getFile().exists());
assertThat(configFile.getFile().getParentFile().getName(), equalTo("labels"));
}
@Test
public void startsWithDoubleDot() throws Exception {
j.submit(j.createWebClient().goTo("labelAtom/..foo/configure").getFormByName("config"));
LabelAtom foo = j.jenkins.getLabelAtom("..foo");
XmlFile configFile = foo.getConfigFile();
assertTrue(configFile.getFile().exists());
assertThat(configFile.getFile().getParentFile().getName(), equalTo("labels"));
}
@Test
@Issue("SECURITY-1986")
public void endsWithDoubleDotSlash() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/foo..%2f/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("foo../");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test
public void endsWithDoubleDot() throws Exception {
j.submit(j.createWebClient().goTo("labelAtom/foo../configure").getFormByName("config"));
LabelAtom foo = j.jenkins.getLabelAtom("foo..");
XmlFile configFile = foo.getConfigFile();
assertTrue(configFile.getFile().exists());
assertThat(configFile.getFile().getParentFile().getName(), equalTo("labels"));
}
@Test
@Issue("SECURITY-1986")
public void startsWithDoubleDotBackslash() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/..\\foo/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("..\\foo");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test
@Issue("SECURITY-1986")
public void endsWithDoubleDotBackslash() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/foo..\\/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("foo..\\");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test
@Issue("SECURITY-1986")
public void middleDotsSlashes() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/foo%2f..%2fgoo/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("foo/../goo");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test
@Issue("SECURITY-1986")
public void middleDotsBackslashes() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/foo%\\..\\goo/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom("foo\\..\\");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
@Test(expected = Exception.class)
@Issue("SECURITY-1986")
public void programmaticCreationInvalidName() throws IOException {
LabelAtom label = new LabelAtom("foo/../goo");
label.save();
}
@Test
public void programmaticCreation() throws IOException {
LabelAtom label = new LabelAtom("foo");
label.save();
LabelAtom foo = j.jenkins.getLabelAtom("foo");
XmlFile configFile = foo.getConfigFile();
assertTrue(configFile.getFile().exists());
assertThat(configFile.getFile().getParentFile().getName(), equalTo("labels"));
}
@Test
@Issue("SECURITY-1986")
public void startsWithTripleDotBackslash() throws Exception {
try {
j.submit(j.createWebClient().goTo("labelAtom/...%2ffoo/configure").getFormByName("config"));
fail("Should have rejected label.");
} catch (FailingHttpStatusCodeException e) {
assertThat(e.getStatusCode(), is(HttpURLConnection.HTTP_BAD_REQUEST));
LabelAtom foo = j.jenkins.getLabelAtom(".../foo");
XmlFile configFile = foo.getConfigFile();
assertFalse(configFile.getFile().exists());
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册