Browse code

integration-cli: cp: added tests for cp

This patch adds integration tests for the copying of resources
from a container, to ensure that regressions in the security of
resource copying can be easily discovered.

Docker-DCO-1.1-Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> (github: cyphar)

cyphar authored on 2014/05/10 21:51:45
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,208 @@
0
+package main
1
+
2
+import (
3
+	"fmt"
4
+	"io/ioutil"
5
+	"os"
6
+	"path/filepath"
7
+	"testing"
8
+)
9
+
10
+const (
11
+	cpTestPathParent = "/some"
12
+	cpTestPath       = "/some/path"
13
+	cpTestName       = "test"
14
+	cpFullPath       = "/some/path/test"
15
+
16
+	cpContainerContents = "holla, i am the container"
17
+	cpHostContents      = "hello, i am the host"
18
+)
19
+
20
+// Test for #5656
21
+// Check that garbage paths don't escape the container's rootfs
22
+func TestCpGarbagePath(t *testing.T) {
23
+	out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
24
+	if err != nil || exitCode != 0 {
25
+		t.Fatal("failed to create a container", out, err)
26
+	}
27
+
28
+	cleanedContainerID := stripTrailingCharacters(out)
29
+	defer deleteContainer(cleanedContainerID)
30
+
31
+	out, _, err = cmd(t, "wait", cleanedContainerID)
32
+	if err != nil || stripTrailingCharacters(out) != "0" {
33
+		t.Fatal("failed to set up container", out, err)
34
+	}
35
+
36
+	if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
37
+		t.Fatal(err)
38
+	}
39
+
40
+	hostFile, err := os.Create(cpFullPath)
41
+	if err != nil {
42
+		t.Fatal(err)
43
+	}
44
+	defer hostFile.Close()
45
+	defer os.RemoveAll(cpTestPathParent)
46
+
47
+	fmt.Fprintf(hostFile, "%s", cpHostContents)
48
+
49
+	tmpdir, err := ioutil.TempDir("", "docker-integration")
50
+	if err != nil {
51
+		t.Fatal(err)
52
+	}
53
+
54
+	tmpname := filepath.Join(tmpdir, cpTestName)
55
+	defer os.RemoveAll(tmpdir)
56
+
57
+	path := filepath.Join("../../../../../../../../../../../../", cpFullPath)
58
+
59
+	_, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
60
+	if err != nil {
61
+		t.Fatalf("couldn't copy from garbage path: %s:%s %s", cleanedContainerID, path, err)
62
+	}
63
+
64
+	file, _ := os.Open(tmpname)
65
+	defer file.Close()
66
+
67
+	test, err := ioutil.ReadAll(file)
68
+	if err != nil {
69
+		t.Fatal(err)
70
+	}
71
+
72
+	if string(test) == cpHostContents {
73
+		t.Errorf("output matched host file -- garbage path can escape container rootfs")
74
+	}
75
+
76
+	if string(test) != cpContainerContents {
77
+		t.Errorf("output doesn't match the input for garbage path")
78
+	}
79
+
80
+	logDone("cp - garbage paths relative to container's rootfs")
81
+}
82
+
83
+// Check that relative paths are relative to the container's rootfs
84
+func TestCpRelativePath(t *testing.T) {
85
+	out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
86
+	if err != nil || exitCode != 0 {
87
+		t.Fatal("failed to create a container", out, err)
88
+	}
89
+
90
+	cleanedContainerID := stripTrailingCharacters(out)
91
+	defer deleteContainer(cleanedContainerID)
92
+
93
+	out, _, err = cmd(t, "wait", cleanedContainerID)
94
+	if err != nil || stripTrailingCharacters(out) != "0" {
95
+		t.Fatal("failed to set up container", out, err)
96
+	}
97
+
98
+	if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
99
+		t.Fatal(err)
100
+	}
101
+
102
+	hostFile, err := os.Create(cpFullPath)
103
+	if err != nil {
104
+		t.Fatal(err)
105
+	}
106
+	defer hostFile.Close()
107
+	defer os.RemoveAll(cpTestPathParent)
108
+
109
+	fmt.Fprintf(hostFile, "%s", cpHostContents)
110
+
111
+	tmpdir, err := ioutil.TempDir("", "docker-integration")
112
+
113
+	if err != nil {
114
+		t.Fatal(err)
115
+	}
116
+
117
+	tmpname := filepath.Join(tmpdir, cpTestName)
118
+	defer os.RemoveAll(tmpdir)
119
+
120
+	path, _ := filepath.Rel("/", cpFullPath)
121
+
122
+	_, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
123
+	if err != nil {
124
+		t.Fatalf("couldn't copy from relative path: %s:%s %s", cleanedContainerID, path, err)
125
+	}
126
+
127
+	file, _ := os.Open(tmpname)
128
+	defer file.Close()
129
+
130
+	test, err := ioutil.ReadAll(file)
131
+	if err != nil {
132
+		t.Fatal(err)
133
+	}
134
+
135
+	if string(test) == cpHostContents {
136
+		t.Errorf("output matched host file -- relative path can escape container rootfs")
137
+	}
138
+
139
+	if string(test) != cpContainerContents {
140
+		t.Errorf("output doesn't match the input for relative path")
141
+	}
142
+
143
+	logDone("cp - relative paths relative to container's rootfs")
144
+}
145
+
146
+// Check that absolute paths are relative to the container's rootfs
147
+func TestCpAbsolutePath(t *testing.T) {
148
+	out, exitCode, err := cmd(t, "run", "-d", "busybox", "/bin/sh", "-c", "mkdir -p '"+cpTestPath+"' && echo -n '"+cpContainerContents+"' > "+cpFullPath)
149
+	if err != nil || exitCode != 0 {
150
+		t.Fatal("failed to create a container", out, err)
151
+	}
152
+
153
+	cleanedContainerID := stripTrailingCharacters(out)
154
+	defer deleteContainer(cleanedContainerID)
155
+
156
+	out, _, err = cmd(t, "wait", cleanedContainerID)
157
+	if err != nil || stripTrailingCharacters(out) != "0" {
158
+		t.Fatal("failed to set up container", out, err)
159
+	}
160
+
161
+	if err := os.MkdirAll(cpTestPath, os.ModeDir); err != nil {
162
+		t.Fatal(err)
163
+	}
164
+
165
+	hostFile, err := os.Create(cpFullPath)
166
+	if err != nil {
167
+		t.Fatal(err)
168
+	}
169
+	defer hostFile.Close()
170
+	defer os.RemoveAll(cpTestPathParent)
171
+
172
+	fmt.Fprintf(hostFile, "%s", cpHostContents)
173
+
174
+	tmpdir, err := ioutil.TempDir("", "docker-integration")
175
+
176
+	if err != nil {
177
+		t.Fatal(err)
178
+	}
179
+
180
+	tmpname := filepath.Join(tmpdir, cpTestName)
181
+	defer os.RemoveAll(tmpdir)
182
+
183
+	path := cpFullPath
184
+
185
+	_, _, err = cmd(t, "cp", cleanedContainerID+":"+path, tmpdir)
186
+	if err != nil {
187
+		t.Fatalf("couldn't copy from absolute path: %s:%s %s", cleanedContainerID, path, err)
188
+	}
189
+
190
+	file, _ := os.Open(tmpname)
191
+	defer file.Close()
192
+
193
+	test, err := ioutil.ReadAll(file)
194
+	if err != nil {
195
+		t.Fatal(err)
196
+	}
197
+
198
+	if string(test) == cpHostContents {
199
+		t.Errorf("output matched host file -- absolute path can escape container rootfs")
200
+	}
201
+
202
+	if string(test) != cpContainerContents {
203
+		t.Errorf("output doesn't match the input for absolute path")
204
+	}
205
+
206
+	logDone("cp - absolute paths relative to container's rootfs")
207
+}