Skip to content

Commit 6727a9a

Browse files
committed
Do not transfer mountable file when tar name is "." or ".." (docker is unable to use this files)
1 parent add6eb4 commit 6727a9a

File tree

4 files changed

+154
-43
lines changed

4 files changed

+154
-43
lines changed

core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.testcontainers.utility.MountableFile;
44

55
import java.io.File;
6+
import java.nio.file.Files;
67
import java.nio.file.Path;
78

89
/**
@@ -50,6 +51,16 @@ default SELF withFileFromFile(String path, File file, Integer mode) {
5051
* @return self
5152
*/
5253
default SELF withFileFromPath(String path, Path filePath, Integer mode) {
54+
final boolean fileStoredToDir = Files.isRegularFile(filePath) &&
55+
(path.endsWith("/.")
56+
|| path.endsWith("/..")
57+
|| path.endsWith("./")
58+
|| path.endsWith("../")
59+
|| ".".equals(path)
60+
|| "..".equals(path));
61+
if (fileStoredToDir) {
62+
throw new IllegalArgumentException("Unable to store file '" + filePath + "' to docker path '" + path + "'");
63+
}
5364
final MountableFile mountableFile = MountableFile.forHostPath(filePath, mode);
5465
return ((SELF) this).withFileFromTransferable(path, mountableFile);
5566
}

core/src/main/java/org/testcontainers/utility/MountableFile.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ private void recursiveTar(String entryFilename, String rootPath, String itemPath
322322
tarEntry.setMode(getUnixFileMode(itemPath));
323323
tarArchive.putArchiveEntry(tarEntry);
324324

325+
log.trace("Transferring {} '{}'", sourceFile.isFile() ? "file" : "directory", sourceFile);
325326
if (sourceFile.isFile()) {
326327
Files.copy(sourceFile.toPath(), tarArchive);
327328
}
@@ -377,7 +378,7 @@ public static int getUnixFileMode(final Path path) {
377378
// Truncate mode bits for z/OS
378379
if ("OS/390".equals(SystemUtils.OS_NAME) ||
379380
"z/OS".equals(SystemUtils.OS_NAME) ||
380-
"zOS".equals(SystemUtils.OS_NAME) ) {
381+
"zOS".equals(SystemUtils.OS_NAME)) {
381382
unixMode &= TarConstants.MAXID;
382383
unixMode |= Files.isDirectory(path) ? 040000 : 0100000;
383384
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package org.testcontainers.images.builder;
2+
3+
import org.junit.Rule;
4+
import org.junit.Test;
5+
import org.junit.experimental.runners.Enclosed;
6+
import org.junit.rules.ExpectedException;
7+
import org.junit.runner.RunWith;
8+
import org.junit.runners.Parameterized;
9+
10+
import java.io.File;
11+
import java.nio.file.Paths;
12+
13+
@RunWith(Enclosed.class)
14+
public class ImageFromDockerfileTest {
15+
@RunWith(Parameterized.class)
16+
public static class InvalidTarPathTests {
17+
@Rule
18+
public final ExpectedException expectedException = ExpectedException.none();
19+
20+
@Parameterized.Parameters(name = "{index} - {0}")
21+
public static String[] getTestCases() {
22+
return new String[]{
23+
"..",
24+
".",
25+
"../",
26+
"./",
27+
"/..",
28+
"/.",
29+
"/../",
30+
"/./",
31+
".",
32+
"..",
33+
"aa/.",
34+
"aa/..",
35+
"bb/./",
36+
"bb/../"
37+
};
38+
}
39+
40+
@Parameterized.Parameter
41+
public String tarPath;
42+
43+
@Test
44+
public void unableToTransferFileWithDotsToDockerDaemon() {
45+
expectedException.expect(IllegalArgumentException.class);
46+
expectedException.expectMessage("Unable to store file '" +
47+
Paths.get("src", "test", "resources", "mappable-resource", "test-resource.txt") +
48+
"' to docker path '" + tarPath + "'");
49+
50+
final ImageFromDockerfile imageFromDockerfile = new ImageFromDockerfile()
51+
.withFileFromFile(tarPath, new File("src/test/resources/mappable-resource/test-resource.txt"));
52+
imageFromDockerfile.resolve();
53+
}
54+
}
55+
}

core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,77 +10,121 @@
1010

1111
import java.io.File;
1212
import java.util.Arrays;
13+
import java.util.List;
1314

1415
import static org.hamcrest.CoreMatchers.allOf;
1516
import static org.hamcrest.CoreMatchers.containsString;
17+
import static org.hamcrest.CoreMatchers.endsWith;
1618
import static org.rnorth.visibleassertions.VisibleAssertions.assertThat;
1719
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
1820

1921
public class DirectoryTarResourceTest {
20-
2122
@Test
2223
public void simpleRecursiveFileTest() {
2324
// 'src' is expected to be the project base directory, so all source code/resources should be copied in
2425
File directory = new File("src");
2526

26-
GenericContainer container = new GenericContainer(
27-
new ImageFromDockerfile()
28-
.withDockerfileFromBuilder(builder ->
29-
builder.from("alpine:3.3")
30-
.copy("/tmp/foo", "/foo")
31-
.cmd("cat /foo/test/resources/test-recursive-file.txt")
32-
.build()
33-
).withFileFromFile("/tmp/foo", directory))
34-
.withStartupCheckStrategy(new OneShotStartupCheckStrategy());
27+
try (GenericContainer container = new GenericContainer(
28+
new ImageFromDockerfile()
29+
.withDockerfileFromBuilder(builder ->
30+
builder.from("alpine:3.3")
31+
.copy("/tmp/foo", "/foo")
32+
.cmd("cat /foo/test/resources/test-recursive-file.txt")
33+
.build()
34+
).withFileFromFile("/tmp/foo", directory))
35+
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())) {
3536

36-
container.start();
37+
container.start();
3738

38-
final String results = container.getLogs();
39+
final String results = container.getLogs();
3940

40-
assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest"));
41+
assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest"));
42+
}
4143
}
4244

4345
@Test
4446
public void simpleRecursiveFileWithPermissionTest() {
45-
GenericContainer container = new GenericContainer(
46-
new ImageFromDockerfile()
47-
.withDockerfileFromBuilder(builder ->
48-
builder.from("alpine:3.3")
49-
.copy("/tmp/foo", "/foo")
50-
.cmd("ls", "-al", "/")
51-
.build()
52-
).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"),
53-
0754))
54-
.withStartupCheckStrategy(new OneShotStartupCheckStrategy());
55-
56-
container.start();
57-
String listing = container.getLogs();
58-
59-
assertThat("Listing shows that file is copied with mode requested.",
47+
try (GenericContainer container = new GenericContainer(
48+
new ImageFromDockerfile()
49+
.withDockerfileFromBuilder(builder ->
50+
builder.from("alpine:3.3")
51+
.copy("/tmp/foo", "/foo")
52+
.cmd("ls", "-al", "/")
53+
.build()
54+
).withFileFromFile("/tmp/foo", new File("/mappable-resource/test-resource.txt"),
55+
0754))
56+
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())) {
57+
58+
container.start();
59+
String listing = container.getLogs();
60+
61+
assertThat("Listing shows that file is copied with mode requested.",
6062
Arrays.asList(listing.split("\\n")),
61-
exactlyNItems(1, allOf(containsString("-rwxr-xr--"), containsString("foo"))));
63+
exactlyOnce(allOf(containsString("-rwxr-xr--"), containsString("foo"))));
64+
}
65+
}
66+
67+
@Test
68+
public void transferFileDockerDaemon() {
69+
final File theFile = new File("src/test/resources/mappable-resource/test-resource.txt");
70+
try (GenericContainer container = new GenericContainer(
71+
new ImageFromDockerfile()
72+
.withDockerfileFromBuilder(builder ->
73+
builder.from("alpine:3.3")
74+
.copy(".", "/foo/")
75+
.cmd("ls", "-al", "/foo")
76+
.build()
77+
).withFileFromFile("bar1", theFile)
78+
.withFileFromFile("./bar2", theFile)
79+
.withFileFromFile("../bar3", theFile)
80+
.withFileFromFile(".bar4", theFile)
81+
.withFileFromFile("..bar5", theFile)
82+
.withFileFromFile("xxx/../bar6", theFile)
83+
.withFileFromFile("x7/./bar7", theFile)
84+
.withFileFromFile("x8/././bar8", theFile)
85+
.withFileFromFile("x9/../../bar9", theFile))
86+
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())) {
87+
88+
container.start();
89+
90+
final List<String> logLines = Arrays.asList(container.getLogs().split("\\n"));
91+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" bar1")));
92+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" bar2")));
93+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" bar3")));
94+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" .bar4")));
95+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" ..bar5")));
96+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" bar6")));
97+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" x7")));
98+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" x8")));
99+
assertThat("Listing shows that file is copied.", logLines, exactlyOnce(endsWith(" bar9")));
100+
}
62101
}
63102

64103
@Test
65104
public void simpleRecursiveClasspathResourceTest() {
66105
// This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image
67106

68-
GenericContainer container = new GenericContainer(
69-
new ImageFromDockerfile()
70-
.withDockerfileFromBuilder(builder ->
71-
builder.from("alpine:3.3")
72-
.copy("/tmp/foo", "/foo")
73-
.cmd("ls -lRt /foo")
74-
.build()
75-
).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath
76-
.withStartupCheckStrategy(new OneShotStartupCheckStrategy());
107+
try (GenericContainer container = new GenericContainer(
108+
new ImageFromDockerfile()
109+
.withDockerfileFromBuilder(builder ->
110+
builder.from("alpine:3.3")
111+
.copy("/tmp/foo", "/foo")
112+
.cmd("ls -lRt /foo")
113+
.build()
114+
).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath
115+
.withStartupCheckStrategy(new OneShotStartupCheckStrategy())) {
116+
117+
container.start();
77118

78-
container.start();
119+
final String results = container.getLogs();
79120

80-
final String results = container.getLogs();
121+
// ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in
122+
assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt"));
123+
}
124+
}
81125

82-
// ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in
83-
assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt"));
126+
public static <T> Matcher<Iterable<? super T>> exactlyOnce(Matcher<? super T> elementMatcher) {
127+
return exactlyNItems(1, elementMatcher);
84128
}
85129

86130
public static <T> Matcher<Iterable<? super T>> exactlyNItems(final int n, Matcher<? super T> elementMatcher) {

0 commit comments

Comments
 (0)