You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirCacheCheckoutMaliciousPathTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * Copyright (C) 2011, Robin Rosenberg <robin.rosenberg@dewire.com>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v1.0 which accompanies this
  7. * distribution, is reproduced below, and is available at
  8. * http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. *
  15. * - Redistributions of source code must retain the above copyright notice, this
  16. * list of conditions and the following disclaimer.
  17. *
  18. * - Redistributions in binary form must reproduce the above copyright notice,
  19. * this list of conditions and the following disclaimer in the documentation
  20. * and/or other materials provided with the distribution.
  21. *
  22. * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
  23. * contributors may be used to endorse or promote products derived from this
  24. * software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. */
  38. package org.eclipse.jgit.lib;
  39. import static org.hamcrest.Matchers.startsWith;
  40. import static org.junit.Assert.assertThat;
  41. import static org.junit.Assert.fail;
  42. import java.io.File;
  43. import java.io.IOException;
  44. import java.util.Arrays;
  45. import org.eclipse.jgit.api.Git;
  46. import org.eclipse.jgit.api.errors.CheckoutConflictException;
  47. import org.eclipse.jgit.api.errors.InvalidRefNameException;
  48. import org.eclipse.jgit.api.errors.JGitInternalException;
  49. import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
  50. import org.eclipse.jgit.api.errors.RefNotFoundException;
  51. import org.eclipse.jgit.dircache.InvalidPathException;
  52. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  53. import org.eclipse.jgit.errors.MissingObjectException;
  54. import org.eclipse.jgit.junit.MockSystemReader;
  55. import org.eclipse.jgit.revwalk.RevWalk;
  56. import org.eclipse.jgit.util.SystemReader;
  57. import org.junit.Test;
  58. public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase {
  59. protected ObjectId theHead;
  60. protected ObjectId theMerge;
  61. @Test
  62. public void testMaliciousAbsolutePathIsOk() throws Exception {
  63. testMaliciousPathGoodFirstCheckout("ok");
  64. }
  65. @Test
  66. public void testMaliciousAbsolutePathIsOkSecondCheckout() throws Exception {
  67. testMaliciousPathGoodSecondCheckout("ok");
  68. }
  69. @Test
  70. public void testMaliciousAbsolutePathIsOkTwoLevels() throws Exception {
  71. testMaliciousPathGoodSecondCheckout("a", "ok");
  72. }
  73. @Test
  74. public void testMaliciousAbsolutePath() throws Exception {
  75. testMaliciousPathBadFirstCheckout("/tmp/x");
  76. }
  77. @Test
  78. public void testMaliciousAbsolutePathSecondCheckout() throws Exception {
  79. testMaliciousPathBadSecondCheckout("/tmp/x");
  80. }
  81. @Test
  82. public void testMaliciousAbsolutePathTwoLevelsFirstBad() throws Exception {
  83. testMaliciousPathBadFirstCheckout("/tmp/x", "y");
  84. }
  85. @Test
  86. public void testMaliciousAbsolutePathTwoLevelsSecondBad() throws Exception {
  87. testMaliciousPathBadFirstCheckout("y", "/tmp/x");
  88. }
  89. @Test
  90. public void testMaliciousAbsoluteCurDrivePathWindows() throws Exception {
  91. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  92. testMaliciousPathBadFirstCheckout("\\somepath");
  93. }
  94. @Test
  95. public void testMaliciousAbsoluteCurDrivePathWindowsOnUnix()
  96. throws Exception {
  97. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  98. testMaliciousPathGoodFirstCheckout("\\somepath");
  99. }
  100. @Test
  101. public void testMaliciousAbsoluteUNCPathWindows1() throws Exception {
  102. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  103. testMaliciousPathBadFirstCheckout("\\\\somepath");
  104. }
  105. @Test
  106. public void testMaliciousAbsoluteUNCPathWindows1OnUnix() throws Exception {
  107. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  108. testMaliciousPathGoodFirstCheckout("\\\\somepath");
  109. }
  110. @Test
  111. public void testMaliciousAbsoluteUNCPathWindows2() throws Exception {
  112. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  113. testMaliciousPathBadFirstCheckout("\\/somepath");
  114. }
  115. @Test
  116. public void testMaliciousAbsoluteUNCPathWindows2OnUnix() throws Exception {
  117. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  118. testMaliciousPathBadFirstCheckout("\\/somepath");
  119. }
  120. @Test
  121. public void testMaliciousAbsoluteWindowsPath1() throws Exception {
  122. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  123. testMaliciousPathBadFirstCheckout("c:\\temp\\x");
  124. }
  125. @Test
  126. public void testMaliciousAbsoluteWindowsPath1OnUnix() throws Exception {
  127. if (File.separatorChar == '\\')
  128. return; // cannot emulate Unix on Windows for this test
  129. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  130. testMaliciousPathGoodFirstCheckout("c:\\temp\\x");
  131. }
  132. @Test
  133. public void testMaliciousAbsoluteWindowsPath2() throws Exception {
  134. ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
  135. testMaliciousPathBadFirstCheckout("c:/temp/x");
  136. }
  137. @Test
  138. public void testMaliciousGitPath1() throws Exception {
  139. testMaliciousPathBadFirstCheckout(".git/konfig");
  140. }
  141. @Test
  142. public void testMaliciousGitPath2() throws Exception {
  143. testMaliciousPathBadFirstCheckout(".git", "konfig");
  144. }
  145. @Test
  146. public void testMaliciousGitPath1Case() throws Exception {
  147. ((MockSystemReader) SystemReader.getInstance()).setWindows(); // or OS X
  148. testMaliciousPathBadFirstCheckout(".Git/konfig");
  149. }
  150. @Test
  151. public void testMaliciousGitPath2Case() throws Exception {
  152. ((MockSystemReader) SystemReader.getInstance()).setWindows(); // or OS X
  153. testMaliciousPathBadFirstCheckout(".gIt", "konfig");
  154. }
  155. @Test
  156. public void testMaliciousGitPath3Case() throws Exception {
  157. ((MockSystemReader) SystemReader.getInstance()).setWindows(); // or OS X
  158. testMaliciousPathBadFirstCheckout(".giT", "konfig");
  159. }
  160. @Test
  161. public void testMaliciousGitPathEndSpaceWindows() throws Exception {
  162. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  163. testMaliciousPathBadFirstCheckout(".git ", "konfig");
  164. }
  165. @Test
  166. public void testMaliciousGitPathEndSpaceUnixOk() throws Exception {
  167. if (File.separatorChar == '\\')
  168. return; // cannot emulate Unix on Windows for this test
  169. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  170. testMaliciousPathGoodFirstCheckout(".git ", "konfig");
  171. }
  172. @Test
  173. public void testMaliciousGitPathEndDotWindows1() throws Exception {
  174. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  175. testMaliciousPathBadFirstCheckout(".git.", "konfig");
  176. }
  177. @Test
  178. public void testMaliciousGitPathEndDotWindows2() throws Exception {
  179. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  180. testMaliciousPathBadFirstCheckout(".f.");
  181. }
  182. @Test
  183. public void testMaliciousGitPathEndDotWindows3() throws Exception {
  184. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  185. testMaliciousPathGoodFirstCheckout(".f");
  186. }
  187. @Test
  188. public void testMaliciousGitPathEndDotUnixOk() throws Exception {
  189. if (File.separatorChar == '\\')
  190. return; // cannot emulate Unix on Windows for this test
  191. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  192. testMaliciousPathGoodFirstCheckout(".git.", "konfig");
  193. }
  194. @Test
  195. public void testMaliciousPathDotDot() throws Exception {
  196. ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
  197. testMaliciousPathBadFirstCheckout("..", "no");
  198. }
  199. @Test
  200. public void testMaliciousPathDot() throws Exception {
  201. ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
  202. testMaliciousPathBadFirstCheckout(".", "no");
  203. }
  204. @Test
  205. public void testMaliciousPathEmpty() throws Exception {
  206. ((MockSystemReader) SystemReader.getInstance()).setCurrentPlatform();
  207. testMaliciousPathBadFirstCheckout("", "no");
  208. }
  209. @Test
  210. public void testMaliciousWindowsADS() throws Exception {
  211. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  212. testMaliciousPathBadFirstCheckout("some:path");
  213. }
  214. @Test
  215. public void testMaliciousWindowsADSOnUnix() throws Exception {
  216. if (File.separatorChar == '\\')
  217. return; // cannot emulate Unix on Windows for this test
  218. ((MockSystemReader) SystemReader.getInstance()).setUnix();
  219. testMaliciousPathGoodFirstCheckout("some:path");
  220. }
  221. @Test
  222. public void testForbiddenNamesOnWindowsEgCon() throws Exception {
  223. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  224. testMaliciousPathBadFirstCheckout("con");
  225. }
  226. @Test
  227. public void testForbiddenNamesOnWindowsEgConDotSuffix() throws Exception {
  228. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  229. testMaliciousPathBadFirstCheckout("con.txt");
  230. }
  231. @Test
  232. public void testForbiddenNamesOnWindowsEgLpt1() throws Exception {
  233. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  234. testMaliciousPathBadFirstCheckout("lpt1");
  235. }
  236. @Test
  237. public void testForbiddenNamesOnWindowsEgLpt1DotSuffix() throws Exception {
  238. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  239. testMaliciousPathBadFirstCheckout("lpt1.txt");
  240. }
  241. @Test
  242. public void testForbiddenNamesOnWindowsEgDotCon() throws Exception {
  243. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  244. testMaliciousPathGoodFirstCheckout(".con");
  245. }
  246. @Test
  247. public void testForbiddenNamesOnWindowsEgLpr() throws Exception {
  248. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  249. testMaliciousPathGoodFirstCheckout("lpt"); // good name
  250. }
  251. @Test
  252. public void testForbiddenNamesOnWindowsEgCon1() throws Exception {
  253. ((MockSystemReader) SystemReader.getInstance()).setWindows();
  254. testMaliciousPathGoodFirstCheckout("con1"); // good name
  255. }
  256. @Test
  257. public void testForbiddenWindowsNamesOnUnixEgCon() throws Exception {
  258. if (File.separatorChar == '\\')
  259. return; // cannot emulate Unix on Windows for this test
  260. testMaliciousPathGoodFirstCheckout("con");
  261. }
  262. @Test
  263. public void testForbiddenWindowsNamesOnUnixEgLpt1() throws Exception {
  264. if (File.separatorChar == '\\')
  265. return; // cannot emulate Unix on Windows for this test
  266. testMaliciousPathGoodFirstCheckout("lpt1");
  267. }
  268. private void testMaliciousPathBadFirstCheckout(String... paths)
  269. throws Exception {
  270. testMaliciousPath(false, false, paths);
  271. }
  272. private void testMaliciousPathBadSecondCheckout(String... paths) throws Exception {
  273. testMaliciousPath(false, true, paths);
  274. }
  275. private void testMaliciousPathGoodFirstCheckout(String... paths)
  276. throws Exception {
  277. testMaliciousPath(true, false, paths);
  278. }
  279. private void testMaliciousPathGoodSecondCheckout(String... paths) throws Exception {
  280. testMaliciousPath(true, true, paths);
  281. }
  282. /**
  283. * Create a bad tree and tries to check it out
  284. *
  285. * @param good
  286. * true if we expect this to pass
  287. * @param secondCheckout
  288. * perform the actual test on the second checkout
  289. * @param path
  290. * to the blob, one or more levels
  291. * @throws IOException
  292. * @throws RefAlreadyExistsException
  293. * @throws RefNotFoundException
  294. * @throws InvalidRefNameException
  295. * @throws MissingObjectException
  296. * @throws IncorrectObjectTypeException
  297. * @throws CheckoutConflictException
  298. * @throws JGitInternalException
  299. */
  300. private void testMaliciousPath(boolean good, boolean secondCheckout, String... path)
  301. throws IOException, RefAlreadyExistsException,
  302. RefNotFoundException, InvalidRefNameException,
  303. MissingObjectException, IncorrectObjectTypeException,
  304. JGitInternalException, CheckoutConflictException {
  305. Git git = new Git(db);
  306. ObjectInserter newObjectInserter;
  307. newObjectInserter = git.getRepository().newObjectInserter();
  308. ObjectId blobId = newObjectInserter.insert(Constants.OBJ_BLOB,
  309. "data".getBytes());
  310. newObjectInserter = git.getRepository().newObjectInserter();
  311. FileMode mode = FileMode.REGULAR_FILE;
  312. ObjectId insertId = blobId;
  313. for (int i = path.length - 1; i >= 0; --i) {
  314. TreeFormatter treeFormatter = new TreeFormatter();
  315. treeFormatter.append("goodpath", mode, insertId);
  316. insertId = newObjectInserter.insert(treeFormatter);
  317. mode = FileMode.TREE;
  318. }
  319. newObjectInserter = git.getRepository().newObjectInserter();
  320. CommitBuilder commitBuilder = new CommitBuilder();
  321. commitBuilder.setAuthor(author);
  322. commitBuilder.setCommitter(committer);
  323. commitBuilder.setMessage("foo#1");
  324. commitBuilder.setTreeId(insertId);
  325. ObjectId firstCommitId = newObjectInserter.insert(commitBuilder);
  326. newObjectInserter = git.getRepository().newObjectInserter();
  327. mode = FileMode.REGULAR_FILE;
  328. insertId = blobId;
  329. for (int i = path.length - 1; i >= 0; --i) {
  330. TreeFormatter treeFormatter = new TreeFormatter();
  331. treeFormatter.append(path[i], mode, insertId);
  332. insertId = newObjectInserter.insert(treeFormatter);
  333. mode = FileMode.TREE;
  334. }
  335. // Create another commit
  336. commitBuilder = new CommitBuilder();
  337. commitBuilder.setAuthor(author);
  338. commitBuilder.setCommitter(committer);
  339. commitBuilder.setMessage("foo#2");
  340. commitBuilder.setTreeId(insertId);
  341. commitBuilder.setParentId(firstCommitId);
  342. ObjectId commitId = newObjectInserter.insert(commitBuilder);
  343. RevWalk revWalk = new RevWalk(git.getRepository());
  344. if (!secondCheckout)
  345. git.checkout().setStartPoint(revWalk.parseCommit(firstCommitId))
  346. .setName("refs/heads/master").setCreateBranch(true).call();
  347. try {
  348. if (secondCheckout) {
  349. git.checkout().setStartPoint(revWalk.parseCommit(commitId))
  350. .setName("refs/heads/master").setCreateBranch(true)
  351. .call();
  352. } else {
  353. git.branchCreate().setName("refs/heads/next")
  354. .setStartPoint(commitId.name()).call();
  355. git.checkout().setName("refs/heads/next")
  356. .call();
  357. }
  358. if (!good)
  359. fail("Checkout of Tree " + Arrays.asList(path) + " should fail");
  360. } catch (InvalidPathException e) {
  361. if (good)
  362. throw e;
  363. assertThat(e.getMessage(), startsWith("Invalid path: "));
  364. }
  365. }
  366. }