diff --git a/test/src/test/java/hudson/PluginManagerSecurity3072Test.java b/test/src/test/java/hudson/PluginManagerSecurity3072Test.java deleted file mode 100644 index 774d6b5ce1c6..000000000000 --- a/test/src/test/java/hudson/PluginManagerSecurity3072Test.java +++ /dev/null @@ -1,107 +0,0 @@ -package hudson; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; - -import hudson.model.RootAction; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.attribute.PosixFilePermission; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import javax.servlet.ServletException; -import jenkins.model.Jenkins; -import org.htmlunit.html.HtmlForm; -import org.htmlunit.html.HtmlPage; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.TestExtension; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; - -public class PluginManagerSecurity3072Test { - - @Rule - public JenkinsRule r = PluginManagerUtil.newJenkinsRule(); - - @Rule - public TemporaryFolder tmp = new TemporaryFolder(); - - @Test - @Issue("SECURITY-3072") - public void verifyUploadedPluginFromURLPermission() throws Exception { - assumeFalse(Functions.isWindows()); - - HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); - HtmlForm f = page.getFormByName("uploadPlugin"); - f.getInputByName("pluginUrl").setValue(Jenkins.get().getRootUrl() + "pluginManagerGetPlugin/htmlpublisher.jpi"); - r.submit(f); - - File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); - File filesTmpDir = filesRef.getParentFile(); - filesRef.deleteOnExit(); - - final Set[] filesPermission = new Set[]{new HashSet<>()}; - await().pollInterval(250, TimeUnit.MILLISECONDS) - .atMost(10, TimeUnit.SECONDS) - .until(() -> { - Optional lastUploadedPluginDir = Arrays.stream(Objects.requireNonNull( - filesTmpDir.listFiles((file, fileName) -> - fileName.startsWith("uploadDir")))). - max(Comparator.comparingLong(File::lastModified)); - if (lastUploadedPluginDir.isPresent()) { - filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPluginDir.get().toPath(), LinkOption.NOFOLLOW_LINKS); - Optional pluginFile = Arrays.stream(Objects.requireNonNull( - lastUploadedPluginDir.get().listFiles((file, fileName) -> - fileName.startsWith("uploaded")))). - max(Comparator.comparingLong(File::lastModified)); - assertTrue(pluginFile.isPresent()); - return true; - } else { - return false; - } - }); - assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); - } - - @TestExtension("verifyUploadedPluginFromURLPermission") - public static final class ReturnPluginJpiAction implements RootAction { - - @Override - public String getIconFileName() { - return "gear2.png"; - } - - @Override - public String getDisplayName() { - return "URL to retrieve a plugin jpi"; - } - - @Override - public String getUrlName() { - return "pluginManagerGetPlugin"; - } - - public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException { - staplerResponse.setContentType("application/octet-stream"); - staplerResponse.setStatus(200); - staplerResponse.serveFile(staplerRequest, PluginManagerTest.class.getClassLoader().getResource("plugins/htmlpublisher.jpi")); - } - } -} diff --git a/test/src/test/java/hudson/PluginManagerTest.java b/test/src/test/java/hudson/PluginManagerTest.java index 507354b8e1b8..dddd9239d97a 100644 --- a/test/src/test/java/hudson/PluginManagerTest.java +++ b/test/src/test/java/hudson/PluginManagerTest.java @@ -804,6 +804,43 @@ public void noInjectionOnAvailablePluginsPage() throws Exception { } } + @Test + @Issue("SECURITY-3072") + public void verifyUploadedPluginFromURLPermission() throws Exception { + assumeFalse(Functions.isWindows()); + + HtmlPage page = r.createWebClient().goTo("pluginManager/advanced"); + HtmlForm f = page.getFormByName("uploadPlugin"); + f.getInputByName("pluginUrl").setValue(Jenkins.get().getRootUrl() + "pluginManagerGetPlugin/htmlpublisher.jpi"); + r.submit(f); + + File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); + File filesTmpDir = filesRef.getParentFile(); + filesRef.deleteOnExit(); + + final Set[] filesPermission = new Set[]{new HashSet<>()}; + await().pollInterval(250, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .until(() -> { + Optional lastUploadedPluginDir = Arrays.stream(Objects.requireNonNull( + filesTmpDir.listFiles((file, fileName) -> + fileName.startsWith("uploadDir")))). + max(Comparator.comparingLong(File::lastModified)); + if (lastUploadedPluginDir.isPresent()) { + filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPluginDir.get().toPath(), LinkOption.NOFOLLOW_LINKS); + Optional pluginFile = Arrays.stream(Objects.requireNonNull( + lastUploadedPluginDir.get().listFiles((file, fileName) -> + fileName.startsWith("uploaded")))). + max(Comparator.comparingLong(File::lastModified)); + assertTrue(pluginFile.isPresent()); + return true; + } else { + return false; + } + }); + assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); + } + static class AlertHandlerImpl implements AlertHandler { List messages = Collections.synchronizedList(new ArrayList<>()); @@ -838,4 +875,29 @@ public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResp } } + @TestExtension("verifyUploadedPluginFromURLPermission") + public static final class Security3072JpiAction implements RootAction { + + @Override + public String getIconFileName() { + return "gear2.png"; + } + + @Override + public String getDisplayName() { + return "URL to retrieve a plugin jpi"; + } + + @Override + public String getUrlName() { + return "pluginManagerGetPlugin"; + } + + public void doDynamic(StaplerRequest staplerRequest, StaplerResponse staplerResponse) throws ServletException, IOException { + staplerResponse.setContentType("application/octet-stream"); + staplerResponse.setStatus(200); + staplerResponse.serveFile(staplerRequest, PluginManagerTest.class.getClassLoader().getResource("plugins/htmlpublisher.jpi")); + } + } + } diff --git a/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java b/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java deleted file mode 100644 index 96c3fb3c634a..000000000000 --- a/test/src/test/java/jenkins/model/JenkinsSecurity3073Test.java +++ /dev/null @@ -1,77 +0,0 @@ -package jenkins.model; - -import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; -import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; -import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeFalse; - -import hudson.Functions; -import java.io.File; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.attribute.PosixFilePermission; -import java.util.Arrays; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import org.apache.commons.io.FileUtils; -import org.htmlunit.html.HtmlForm; -import org.htmlunit.html.HtmlPage; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; - -public class JenkinsSecurity3073Test { - - - @Rule - public JenkinsRule j = new JenkinsRule(); - - @Rule - public TemporaryFolder tmp = new TemporaryFolder(); - - @Test - @Issue("SECURITY-3073") - public void verifyUploadedFingerprintFilePermission() throws Exception { - assumeFalse(Functions.isWindows()); - - HtmlPage page = j.createWebClient().goTo("fingerprintCheck"); - // The form doesn't have a name, the page contain the search form and the one we're interested in - HtmlForm form = page.getForms().get(1); - File dir = tmp.newFolder(); - File plugin = new File(dir, "htmlpublisher.jpi"); - // We're using a plugin to have a file above DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD - FileUtils.copyURLToFile(Objects.requireNonNull(getClass().getClassLoader().getResource("plugins/htmlpublisher.jpi")), plugin); - form.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); - j.submit(form); - - File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); - File filesTmpDir = filesRef.getParentFile(); - filesRef.deleteOnExit(); - - final Set[] filesPermission = new Set[]{new HashSet<>()}; - await().pollInterval(250, TimeUnit.MILLISECONDS) - .atMost(10, TimeUnit.SECONDS) - .until(() -> { - Optional lastUploadedPlugin = Arrays.stream(Objects.requireNonNull( - filesTmpDir.listFiles((file, fileName) -> - fileName.startsWith("jenkins-multipart-uploads")))). - max(Comparator.comparingLong(File::lastModified)); - if (lastUploadedPlugin.isPresent()) { - filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPlugin.get().toPath(), LinkOption.NOFOLLOW_LINKS); - return true; - } else { - return false; - } - }); - assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); - } -} diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index 5c0c9acedbb2..6750eabbd3f1 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -24,6 +24,10 @@ package jenkins.model; +import static java.nio.file.attribute.PosixFilePermission.OWNER_EXECUTE; +import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; +import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.containsString; @@ -38,9 +42,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.ExtensionList; +import hudson.Functions; import hudson.XmlFile; import hudson.init.InitMilestone; import hudson.init.Initializer; @@ -67,27 +73,40 @@ import hudson.util.FormValidation; import hudson.util.HttpResponses; import hudson.util.VersionNumber; +import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.Socket; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import jenkins.AgentProtocol; +import org.apache.commons.io.FileUtils; import org.htmlunit.FailingHttpStatusCodeException; import org.htmlunit.HttpMethod; import org.htmlunit.Page; import org.htmlunit.TextPage; import org.htmlunit.WebRequest; import org.htmlunit.WebResponse; +import org.htmlunit.html.HtmlForm; import org.htmlunit.html.HtmlPage; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; @@ -110,6 +129,46 @@ public class JenkinsTest { @Rule public JenkinsRule j = new JenkinsRule(); + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + + @Test + @Issue("SECURITY-3073") + public void verifyUploadedFingerprintFilePermission() throws Exception { + assumeFalse(Functions.isWindows()); + + HtmlPage page = j.createWebClient().goTo("fingerprintCheck"); + // The form doesn't have a name, the page contain the search form and the one we're interested in + HtmlForm form = page.getForms().get(1); + File dir = tmp.newFolder(); + File plugin = new File(dir, "htmlpublisher.jpi"); + // We're using a plugin to have a file above DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD + FileUtils.copyURLToFile(Objects.requireNonNull(getClass().getClassLoader().getResource("plugins/htmlpublisher.jpi")), plugin); + form.getInputByName("name").setValueAttribute(plugin.getAbsolutePath()); + j.submit(form); + + File filesRef = Files.createTempFile("tmp", ".tmp").toFile(); + File filesTmpDir = filesRef.getParentFile(); + filesRef.deleteOnExit(); + + final Set[] filesPermission = new Set[]{new HashSet<>()}; + await().pollInterval(250, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .until(() -> { + Optional lastUploadedPlugin = Arrays.stream(Objects.requireNonNull( + filesTmpDir.listFiles((file, fileName) -> + fileName.startsWith("jenkins-multipart-uploads")))). + max(Comparator.comparingLong(File::lastModified)); + if (lastUploadedPlugin.isPresent()) { + filesPermission[0] = Files.getPosixFilePermissions(lastUploadedPlugin.get().toPath(), LinkOption.NOFOLLOW_LINKS); + return true; + } else { + return false; + } + }); + assertEquals(EnumSet.of(OWNER_EXECUTE, OWNER_READ, OWNER_WRITE), filesPermission[0]); + } + @Issue("SECURITY-406") @Test public void testUserCreationFromUrlForAdmins() throws Exception {