From 10310bf8ef2ad1af16fbd2c406d813c17ea33793 Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Mon, 1 Dec 2014 15:01:07 -0800 Subject: [PATCH] ObjectChecker: Disallow ".git." and ".git" Windows treats "foo." and "foo " as "foo". The ".git" directory is special, as it contains metadata for a local Git repository. Disallow variations that Windows considers to be the same. Change-Id: I28eb48859a95a89111b4987c91de97557e3bb539 --- .../DirCacheCheckoutMaliciousPathTest.java | 10 +- .../eclipse/jgit/lib/ObjectCheckerTest.java | 96 +++++++++++++++++++ .../org/eclipse/jgit/lib/ObjectChecker.java | 28 +++++- 3 files changed, 124 insertions(+), 10 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java index d29a75ef7..ca3e0666e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutMaliciousPathTest.java @@ -186,10 +186,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { @Test public void testMaliciousGitPathEndSpaceUnixOk() throws Exception { - if (File.separatorChar == '\\') - return; // cannot emulate Unix on Windows for this test - ((MockSystemReader) SystemReader.getInstance()).setUnix(); - testMaliciousPathGoodFirstCheckout(".git ", "konfig"); + testMaliciousPathBadFirstCheckout(".git ", "konfig"); } @Test @@ -212,10 +209,7 @@ public class DirCacheCheckoutMaliciousPathTest extends RepositoryTestCase { @Test public void testMaliciousGitPathEndDotUnixOk() throws Exception { - if (File.separatorChar == '\\') - return; // cannot emulate Unix on Windows for this test - ((MockSystemReader) SystemReader.getInstance()).setUnix(); - testMaliciousPathGoodFirstCheckout(".git.", "konfig"); + testMaliciousPathBadFirstCheckout(".git.", "konfig"); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java index f3db2f65d..aa17ac32c 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java @@ -1307,6 +1307,102 @@ public class ObjectCheckerTest { } } + @Test + public void testInvalidTreeNameIsDotGitDot() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git."); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git.'", e.getMessage()); + } + } + + @Test + public void testValidTreeNameIsDotGitDotDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git.."); + checker.checkTree(Constants.encodeASCII(b.toString())); + } + + @Test + public void testInvalidTreeNameIsDotGitSpace() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git '", e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsDotGitSomething() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar"); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingSpaceSomething() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoo bar"); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar."); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitSomethingDotDot() + throws CorruptObjectException { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .gitfoobar.."); + byte[] data = Constants.encodeASCII(b.toString()); + checker.checkTree(data); + } + + @Test + public void testInvalidTreeNameIsDotGitDotSpace() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git. "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git. '", e.getMessage()); + } + } + + @Test + public void testInvalidTreeNameIsDotGitSpaceDot() { + StringBuilder b = new StringBuilder(); + entry(b, "100644 .git . "); + byte[] data = Constants.encodeASCII(b.toString()); + try { + checker.checkTree(data); + fail("incorrectly accepted an invalid tree"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '.git . '", e.getMessage()); + } + } + @Test public void testInvalidTreeTruncatedInName() { final StringBuilder b = new StringBuilder(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java index 92b4e39d5..1e4163e97 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java @@ -453,7 +453,13 @@ public class ObjectChecker { throw new CorruptObjectException("invalid name '..'"); break; case 4: - if (isDotGit(raw, ptr + 1)) + if (isGit(raw, ptr + 1)) + throw new CorruptObjectException(String.format( + "invalid name '%s'", + RawParseUtils.decode(raw, ptr, end))); + break; + default: + if (end - ptr > 4 && isNormalizedGit(raw, ptr + 1, end)) throw new CorruptObjectException(String.format( "invalid name '%s'", RawParseUtils.decode(raw, ptr, end))); @@ -540,12 +546,30 @@ public class ObjectChecker { return 1 <= c && c <= 31; } - private static boolean isDotGit(byte[] buf, int p) { + private static boolean isGit(byte[] buf, int p) { return toLower(buf[p]) == 'g' && toLower(buf[p + 1]) == 'i' && toLower(buf[p + 2]) == 't'; } + private static boolean isNormalizedGit(byte[] raw, int ptr, int end) { + if (isGit(raw, ptr)) { + int dots = 0; + boolean space = false; + int p = end - 1; + for (; (ptr + 2) < p; p--) { + if (raw[p] == '.') + dots++; + else if (raw[p] == ' ') + space = true; + else + break; + } + return p == ptr + 2 && (dots == 1 || space); + } + return false; + } + private static char toLower(byte b) { if ('A' <= b && b <= 'Z') return (char) (b + ('a' - 'A'));