1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package org.eclipse.jgit.lib;
39
40 import static java.nio.charset.StandardCharsets.UTF_8;
41 import static org.junit.Assert.assertTrue;
42 import static org.junit.Assert.fail;
43
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.Arrays;
47
48 import org.eclipse.jgit.api.Git;
49 import org.eclipse.jgit.api.errors.GitAPIException;
50 import org.eclipse.jgit.dircache.InvalidPathException;
51 import org.eclipse.jgit.junit.MockSystemReader;
52 import org.eclipse.jgit.junit.RepositoryTestCase;
53 import org.eclipse.jgit.revwalk.RevWalk;
54 import org.eclipse.jgit.util.SystemReader;
55 import org.junit.Test;
56
57 public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
58
59 protected ObjectId theHead;
60 protected ObjectId theMerge;
61
62 @Test
63 public void testMaliciousAbsolutePathIsOk() throws Exception {
64 testMaliciousPathGoodFirstCheckout("ok");
65 }
66
67 @Test
68 public void testMaliciousAbsolutePathIsOkSecondCheckout() throws Exception {
69 testMaliciousPathGoodSecondCheckout("ok");
70 }
71
72 @Test
73 public void testMaliciousAbsolutePathIsOkTwoLevels() throws Exception {
74 testMaliciousPathGoodSecondCheckout("a", "ok");
75 }
76
77 @Test
78 public void testMaliciousAbsolutePath() throws Exception {
79 testMaliciousPathBadFirstCheckout("/tmp/x");
80 }
81
82 @Test
83 public void testMaliciousAbsolutePathSecondCheckout() throws Exception {
84 testMaliciousPathBadSecondCheckout("/tmp/x");
85 }
86
87 @Test
88 public void testMaliciousAbsolutePathTwoLevelsFirstBad() throws Exception {
89 testMaliciousPathBadFirstCheckout("/tmp/x", "y");
90 }
91
92 @Test
93 public void testMaliciousAbsolutePathTwoLevelsSecondBad() throws Exception {
94 testMaliciousPathBadFirstCheckout("y", "/tmp/x");
95 }
96
97 @Test
98 public void testMaliciousAbsoluteCurDrivePathWindows() throws Exception {
99 ((MockSystemReader) SystemReader.getInstance()).setWindows();
100 testMaliciousPathBadFirstCheckout("\\somepath");
101 }
102
103 @Test
104 public void testMaliciousAbsoluteCurDrivePathWindowsOnUnix()
105 throws Exception {
106 ((MockSystemReader) SystemReader.getInstance()).setUnix();
107 testMaliciousPathGoodFirstCheckout("\\somepath");
108 }
109
110 @Test
111 public void testMaliciousAbsoluteUNCPathWindows1() throws Exception {
112 ((MockSystemReader) SystemReader.getInstance()).setWindows();
113 testMaliciousPathBadFirstCheckout("\\\\somepath");
114 }
115
116 @Test
117 public void testMaliciousAbsoluteUNCPathWindows1OnUnix() throws Exception {
118 ((MockSystemReader) SystemReader.getInstance()).setUnix();
119 testMaliciousPathGoodFirstCheckout("\\\\somepath");
120 }
121
122 @Test
123 public void testMaliciousAbsoluteUNCPathWindows2() throws Exception {
124 ((MockSystemReader) SystemReader.getInstance()).setWindows();
125 testMaliciousPathBadFirstCheckout("\\/somepath");
126 }
127
128 @Test
129 public void testMaliciousAbsoluteUNCPathWindows2OnUnix() throws Exception {
130 ((MockSystemReader) SystemReader.getInstance()).setUnix();
131 testMaliciousPathBadFirstCheckout("\\/somepath");
132 }
133
134 @Test
135 public void testMaliciousAbsoluteWindowsPath1() throws Exception {
136 ((MockSystemReader) SystemReader.getInstance()).setWindows();
137 testMaliciousPathBadFirstCheckout("c:\\temp\\x");
138 }
139
140 @Test
141 public void testMaliciousAbsoluteWindowsPath1OnUnix() throws Exception {
142 if (File.separatorChar == '\\')
143 return;
144 ((MockSystemReader) SystemReader.getInstance()).setUnix();
145 testMaliciousPathGoodFirstCheckout("c:\\temp\\x");
146 }
147
148 @Test
149 public void testMaliciousAbsoluteWindowsPath2() throws Exception {
150 ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
151 testMaliciousPathBadFirstCheckout("c:/temp/x");
152 }
153
154 @Test
155 public void testMaliciousGitPath1() throws Exception {
156 testMaliciousPathBadFirstCheckout(".git/konfig");
157 }
158
159 @Test
160 public void testMaliciousGitPath2() throws Exception {
161 testMaliciousPathBadFirstCheckout(".git", "konfig");
162 }
163
164 @Test
165 public void testMaliciousGitPath1Case() throws Exception {
166 ((MockSystemReader) SystemReader.getInstance()).setWindows();
167 testMaliciousPathBadFirstCheckout(".Git/konfig");
168 }
169
170 @Test
171 public void testMaliciousGitPath2Case() throws Exception {
172 ((MockSystemReader) SystemReader.getInstance()).setWindows();
173 testMaliciousPathBadFirstCheckout(".gIt", "konfig");
174 }
175
176 @Test
177 public void testMaliciousGitPath3Case() throws Exception {
178 ((MockSystemReader) SystemReader.getInstance()).setWindows();
179 testMaliciousPathBadFirstCheckout(".giT", "konfig");
180 }
181
182 @Test
183 public void testMaliciousGitPathEndSpaceWindows() throws Exception {
184 ((MockSystemReader) SystemReader.getInstance()).setWindows();
185 testMaliciousPathBadFirstCheckout(".git ", "konfig");
186 }
187
188 @Test
189 public void testMaliciousGitPathEndSpaceUnixOk() throws Exception {
190 testMaliciousPathBadFirstCheckout(".git ", "konfig");
191 }
192
193 @Test
194 public void testMaliciousGitPathEndDotWindows1() throws Exception {
195 ((MockSystemReader) SystemReader.getInstance()).setWindows();
196 testMaliciousPathBadFirstCheckout(".git.", "konfig");
197 }
198
199 @Test
200 public void testMaliciousGitPathEndDotWindows2() throws Exception {
201 ((MockSystemReader) SystemReader.getInstance()).setWindows();
202 testMaliciousPathBadFirstCheckout(".f.");
203 }
204
205 @Test
206 public void testMaliciousGitPathEndDotWindows3() throws Exception {
207 ((MockSystemReader) SystemReader.getInstance()).setWindows();
208 testMaliciousPathGoodFirstCheckout(".f");
209 }
210
211 @Test
212 public void testMaliciousGitPathEndDotUnixOk() throws Exception {
213 testMaliciousPathBadFirstCheckout(".git.", "konfig");
214 }
215
216 @Test
217 public void testMaliciousPathDotDot() throws Exception {
218 ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
219 testMaliciousPathBadFirstCheckout("..", "no");
220 }
221
222 @Test
223 public void testMaliciousPathDot() throws Exception {
224 ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
225 testMaliciousPathBadFirstCheckout(".", "no");
226 }
227
228 @Test
229 public void testMaliciousPathEmptyUnix() throws Exception {
230 ((MockSystemReader) SystemReader.getInstance()).setUnix();
231 testMaliciousPathBadFirstCheckout("", "no");
232 }
233
234 @Test
235 public void testMaliciousPathEmptyWindows() throws Exception {
236 ((MockSystemReader) SystemReader.getInstance()).setWindows();
237 testMaliciousPathBadFirstCheckout("", "no");
238 }
239
240 @Test
241 public void testMaliciousWindowsADS() throws Exception {
242 ((MockSystemReader) SystemReader.getInstance()).setWindows();
243 testMaliciousPathBadFirstCheckout("some:path");
244 }
245
246 @Test
247 public void testMaliciousWindowsADSOnUnix() throws Exception {
248 if (File.separatorChar == '\\')
249 return;
250 ((MockSystemReader) SystemReader.getInstance()).setUnix();
251 testMaliciousPathGoodFirstCheckout("some:path");
252 }
253
254 @Test
255 public void testForbiddenNamesOnWindowsEgCon() throws Exception {
256 ((MockSystemReader) SystemReader.getInstance()).setWindows();
257 testMaliciousPathBadFirstCheckout("con");
258 }
259
260 @Test
261 public void testForbiddenNamesOnWindowsEgConDotSuffix() throws Exception {
262 ((MockSystemReader) SystemReader.getInstance()).setWindows();
263 testMaliciousPathBadFirstCheckout("con.txt");
264 }
265
266 @Test
267 public void testForbiddenNamesOnWindowsEgLpt1() throws Exception {
268 ((MockSystemReader) SystemReader.getInstance()).setWindows();
269 testMaliciousPathBadFirstCheckout("lpt1");
270 }
271
272 @Test
273 public void testForbiddenNamesOnWindowsEgLpt1DotSuffix() throws Exception {
274 ((MockSystemReader) SystemReader.getInstance()).setWindows();
275 testMaliciousPathBadFirstCheckout("lpt1.txt");
276 }
277
278 @Test
279 public void testForbiddenNamesOnWindowsEgDotCon() throws Exception {
280 ((MockSystemReader) SystemReader.getInstance()).setWindows();
281 testMaliciousPathGoodFirstCheckout(".con");
282 }
283
284 @Test
285 public void testForbiddenNamesOnWindowsEgLpr() throws Exception {
286 ((MockSystemReader) SystemReader.getInstance()).setWindows();
287 testMaliciousPathGoodFirstCheckout("lpt");
288 }
289
290 @Test
291 public void testForbiddenNamesOnWindowsEgCon1() throws Exception {
292 ((MockSystemReader) SystemReader.getInstance()).setWindows();
293 testMaliciousPathGoodFirstCheckout("con1");
294 }
295
296 @Test
297 public void testForbiddenWindowsNamesOnUnixEgCon() throws Exception {
298 if (File.separatorChar == '\\')
299 return;
300 testMaliciousPathGoodFirstCheckout("con");
301 }
302
303 @Test
304 public void testForbiddenWindowsNamesOnUnixEgLpt1() throws Exception {
305 if (File.separatorChar == '\\')
306 return;
307 testMaliciousPathGoodFirstCheckout("lpt1");
308 }
309
310 private void testMaliciousPathBadFirstCheckout(String... paths)
311 throws Exception {
312 testMaliciousPath(false, false, paths);
313 }
314
315 private void testMaliciousPathBadSecondCheckout(String... paths) throws Exception {
316 testMaliciousPath(false, true, paths);
317 }
318
319 private void testMaliciousPathGoodFirstCheckout(String... paths)
320 throws Exception {
321 testMaliciousPath(true, false, paths);
322 }
323
324 private void testMaliciousPathGoodSecondCheckout(String... paths) throws Exception {
325 testMaliciousPath(true, true, paths);
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340 private void testMaliciousPath(boolean good, boolean secondCheckout,
341 String... path) throws GitAPIException, IOException {
342 try (Git git = new Git(db);
343 RevWalk revWalk = new RevWalk(git.getRepository())) {
344 ObjectId blobId;
345 try (ObjectInserter newObjectInserter = git.getRepository()
346 .newObjectInserter()) {
347 blobId = newObjectInserter.insert(Constants.OBJ_BLOB,
348 "data".getBytes(UTF_8));
349 }
350 FileMode mode = FileMode.REGULAR_FILE;
351 ObjectId insertId = blobId;
352 try (ObjectInserter newObjectInserter = git.getRepository()
353 .newObjectInserter()) {
354 for (int i = path.length - 1; i >= 0; --i) {
355 TreeFormatter treeFormatter = new TreeFormatter();
356 treeFormatter.append("goodpath", mode, insertId);
357 insertId = newObjectInserter.insert(treeFormatter);
358 mode = FileMode.TREE;
359 }
360 }
361 ObjectId firstCommitId;
362 try (ObjectInserter newObjectInserter = git.getRepository()
363 .newObjectInserter()) {
364 CommitBuilder commitBuilder = new CommitBuilder();
365 commitBuilder.setAuthor(author);
366 commitBuilder.setCommitter(committer);
367 commitBuilder.setMessage("foo#1");
368 commitBuilder.setTreeId(insertId);
369 firstCommitId = newObjectInserter.insert(commitBuilder);
370 }
371 ObjectId commitId;
372 try (ObjectInserter newObjectInserter = git.getRepository()
373 .newObjectInserter()) {
374 mode = FileMode.REGULAR_FILE;
375 insertId = blobId;
376 for (int i = path.length - 1; i >= 0; --i) {
377 TreeFormatter treeFormatter = new TreeFormatter();
378 treeFormatter.append(path[i].getBytes(UTF_8), 0,
379 path[i].getBytes(UTF_8).length, mode, insertId,
380 true);
381 insertId = newObjectInserter.insert(treeFormatter);
382 mode = FileMode.TREE;
383 }
384
385
386 CommitBuilder commitBuilder = new CommitBuilder();
387 commitBuilder.setAuthor(author);
388 commitBuilder.setCommitter(committer);
389 commitBuilder.setMessage("foo#2");
390 commitBuilder.setTreeId(insertId);
391 commitBuilder.setParentId(firstCommitId);
392 commitId = newObjectInserter.insert(commitBuilder);
393 }
394 if (!secondCheckout)
395 git.checkout().setStartPoint(revWalk.parseCommit(firstCommitId))
396 .setName("refs/heads/master").setCreateBranch(true).call();
397 try {
398 if (secondCheckout) {
399 git.checkout().setStartPoint(revWalk.parseCommit(commitId))
400 .setName("refs/heads/master").setCreateBranch(true)
401 .call();
402 } else {
403 git.branchCreate().setName("refs/heads/next")
404 .setStartPoint(commitId.name()).call();
405 git.checkout().setName("refs/heads/next")
406 .call();
407 }
408 if (!good)
409 fail("Checkout of Tree " + Arrays.asList(path) + " should fail");
410 } catch (InvalidPathException e) {
411 if (good)
412 throw e;
413 assertTrue(e.getMessage().startsWith("Invalid path"));
414 }
415 }
416 }
417
418 }