|
5 | 5 | import hudson.Launcher;
|
6 | 6 | import hudson.model.*;
|
7 | 7 | import hudson.model.queue.QueueTaskFuture;
|
| 8 | +import hudson.remoting.VirtualChannel; |
| 9 | +import hudson.slaves.DumbSlave; |
8 | 10 | import hudson.slaves.EnvironmentVariablesNodeProperty;
|
| 11 | +import hudson.slaves.JNLPLauncher; |
| 12 | +import hudson.slaves.RetentionStrategy; |
| 13 | +import jenkins.MasterToSlaveFileCallable; |
| 14 | +import org.apache.commons.io.FileUtils; |
| 15 | +import org.junit.After; |
9 | 16 | import org.junit.Rule;
|
10 | 17 | import org.junit.Test;
|
| 18 | +import org.jvnet.hudson.test.BuildWatcher; |
| 19 | +import org.jvnet.hudson.test.Issue; |
11 | 20 | import org.jvnet.hudson.test.JenkinsRule;
|
| 21 | +import org.jvnet.hudson.test.TemporaryDirectoryAllocator; |
12 | 22 | import org.jvnet.hudson.test.TestBuilder;
|
| 23 | +import org.testcontainers.DockerClientFactory; |
| 24 | +import org.testcontainers.containers.GenericContainer; |
13 | 25 |
|
14 | 26 | import java.io.File;
|
15 | 27 | import java.io.IOException;
|
| 28 | +import java.nio.charset.StandardCharsets; |
16 | 29 | import java.nio.file.Files;
|
| 30 | +import java.nio.file.Paths; |
17 | 31 | import java.util.ArrayList;
|
18 | 32 | import java.util.Arrays;
|
19 | 33 | import java.util.List;
|
| 34 | +import java.util.Map; |
20 | 35 |
|
21 |
| -import static org.junit.Assert.*; |
| 36 | +import static org.hamcrest.MatcherAssert.assertThat; |
| 37 | +import static org.hamcrest.collection.IsEmptyCollection.empty; |
| 38 | +import static org.hamcrest.core.IsNot.not; |
| 39 | +import static org.junit.Assert.assertEquals; |
| 40 | +import static org.junit.Assert.assertFalse; |
| 41 | +import static org.junit.Assert.assertTrue; |
| 42 | +import static org.junit.Assert.assertNotNull; |
| 43 | +import static org.junit.Assume.assumeTrue; |
22 | 44 |
|
23 | 45 | /**
|
24 | 46 | * @author Kohsuke Kawaguchi
|
25 | 47 | */
|
26 | 48 | public class HtmlPublisherIntegrationTest {
|
27 | 49 | @Rule
|
28 | 50 | public JenkinsRule j = new JenkinsRule();
|
| 51 | + @Rule |
| 52 | + public BuildWatcher buildWatcher = new BuildWatcher(); |
| 53 | + |
| 54 | + public TemporaryDirectoryAllocator tmp = new TemporaryDirectoryAllocator(); |
| 55 | + private GenericContainer agentContainer; |
| 56 | + private DumbSlave agent; |
| 57 | + |
| 58 | + @After |
| 59 | + public void dispose() throws IOException, InterruptedException { |
| 60 | + tmp.dispose(); |
| 61 | + if (agentContainer != null) { |
| 62 | + agentContainer.stop(); |
| 63 | + } |
| 64 | + } |
29 | 65 |
|
30 | 66 | /**
|
31 | 67 | * Makes sure that the configuration survives the round trip.
|
@@ -84,6 +120,39 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
|
84 | 120 | assertFalse(tab2Files.contains("dummy.html"));
|
85 | 121 | }
|
86 | 122 |
|
| 123 | + @Test @Issue("SECURITY-3303") |
| 124 | + public void testNotFollowingSymlinks() throws Exception { |
| 125 | + createDockerAgent(); |
| 126 | + final File directoryOnController = tmp.allocate(); |
| 127 | + FileUtils.write(new File(directoryOnController, "test.txt"), "test", StandardCharsets.UTF_8); |
| 128 | + final String directoryOnControllerPath = directoryOnController.getAbsolutePath(); |
| 129 | + FreeStyleProject p = j.createFreeStyleProject(); |
| 130 | + p.getBuildersList().add(new TestBuilder() { |
| 131 | + @Override |
| 132 | + public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { |
| 133 | + FilePath workspace = build.getWorkspace(); |
| 134 | + workspace.act(new MakeSymlink(directoryOnControllerPath)); |
| 135 | + workspace.child("test3.txt").write("Hello", "UTF-8"); |
| 136 | + return true; |
| 137 | + } |
| 138 | + }); |
| 139 | + HtmlPublisherTarget target1 = new HtmlPublisherTarget("tab1", "", "tab1/test.txt,tab1/test2.txt,**/test3.txt", true, false, true); |
| 140 | + p.getPublishersList().add(new HtmlPublisher(Arrays.asList(target1))); |
| 141 | + p.setAssignedLabel(Label.get("agent")); |
| 142 | + FreeStyleBuild build = j.buildAndAssertSuccess(p); |
| 143 | + File base = new File(build.getRootDir(), "htmlreports"); |
| 144 | + String[] list = base.list(); |
| 145 | + assertNotNull(list); |
| 146 | + assertThat(Arrays.asList(list), not(empty())); |
| 147 | + File tab1 = new File(base, "tab1"); |
| 148 | + list = tab1.list(); |
| 149 | + assertNotNull(list); |
| 150 | + assertThat(Arrays.asList(list), not(empty())); |
| 151 | + |
| 152 | + File reports = new File(tab1, "tab1"); |
| 153 | + assertFalse(reports.exists()); |
| 154 | + } |
| 155 | + |
87 | 156 | @Test
|
88 | 157 | public void testVariableExpansion() throws Exception {
|
89 | 158 | FreeStyleProject p = j.createFreeStyleProject("variable_job");
|
@@ -246,5 +315,41 @@ private void addEnvironmentVariable(String key, String value) {
|
246 | 315 | j.jenkins.getGlobalNodeProperties().add(prop);
|
247 | 316 | }
|
248 | 317 |
|
| 318 | + private void createDockerAgent() throws Exception { |
| 319 | + assumeTrue("Needs Docker", DockerClientFactory.instance().isDockerAvailable()); |
| 320 | + j.jenkins.setSlaveAgentPort(0); |
| 321 | + int port = j.jenkins.getTcpSlaveAgentListener().getAdvertisedPort(); |
| 322 | + synchronized (j.jenkins) { |
| 323 | + agent = new DumbSlave("dockeragentOne", "/home/jenkins/work", new JNLPLauncher(true)); |
| 324 | + agent.setLabelString("agent"); |
| 325 | + agent.setRetentionStrategy(RetentionStrategy.NOOP); |
| 326 | + j.jenkins.addNode(agent); |
| 327 | + } |
| 328 | + Map<String, String> env = Map.of("JENKINS_URL", JNLPLauncher.getInboundAgentUrl(), |
| 329 | + "JENKINS_SECRET", agent.getComputer().getJnlpMac(), |
| 330 | + "JENKINS_AGENT_NAME", agent.getNodeName(), |
| 331 | + "JENKINS_AGENT_WORKDIR", agent.getRemoteFS()); |
| 332 | + System.out.println(env); |
249 | 333 |
|
| 334 | + agentContainer = new GenericContainer<>("jenkins/inbound-agent:jdk" + System.getProperty("java.specification.version")) |
| 335 | + .withEnv(env) |
| 336 | + .withNetworkMode("host").withLogConsumer(outputFrame -> System.out.print(outputFrame.getUtf8String())); |
| 337 | + //agentContainer.getHost() |
| 338 | + agentContainer.start(); |
| 339 | + j.waitOnline(agent); |
| 340 | + } |
| 341 | + |
| 342 | + static class MakeSymlink extends MasterToSlaveFileCallable<Void> { |
| 343 | + final String target; |
| 344 | + |
| 345 | + MakeSymlink(String target) { |
| 346 | + this.target = target; |
| 347 | + } |
| 348 | + |
| 349 | + @Override |
| 350 | + public Void invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { |
| 351 | + Files.createSymbolicLink(Paths.get(f.getAbsolutePath(), "tab1"), Paths.get(target)); |
| 352 | + return null; |
| 353 | + } |
| 354 | + } |
250 | 355 | }
|
0 commit comments