From ba0f89b4211fd57fe52120ec9a7c3cbaadbadd3b Mon Sep 17 00:00:00 2001 From: Shawn Pearce Date: Tue, 11 Mar 2014 22:49:36 -0700 Subject: [PATCH] Reject special Windows device names in ObjectChecker If Windows rejection is enabled reject special device names like NUL and PRN, including NUL.txt. This prevents a tree that might be used on a Windows client from referencing a confusing name. Change-Id: Ic700ea8fa68724509e0357d4b758a41178c4d70c --- .../eclipse/jgit/lib/ObjectCheckerTest.java | 24 +++++++ .../org/eclipse/jgit/lib/ObjectChecker.java | 70 +++++++++++++++++-- 2 files changed, 90 insertions(+), 4 deletions(-) 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 3e9195eea..d434c852e 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 @@ -1039,6 +1039,7 @@ public void testValidPosixTree() throws CorruptObjectException { checkOneName("ac:d|e"); checkOneName("test "); checkOneName("test."); + checkOneName("NUL"); } @Test @@ -1456,6 +1457,29 @@ public void testRejectDotAtEndOnWindows() { } } + @Test + public void testRejectDevicesOnWindows() { + checker.setSafeForWindows(true); + + String[] bad = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", + "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", + "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }; + for (String b : bad) { + try { + checkOneName(b); + fail("incorrectly accepted " + b); + } catch (CorruptObjectException e) { + assertEquals("invalid name '" + b + "'", e.getMessage()); + } + try { + checkOneName(b + ".txt"); + fail("incorrectly accepted " + b + ".txt"); + } catch (CorruptObjectException e) { + assertEquals("invalid name '" + b + "'", e.getMessage()); + } + } + } + @Test public void testRejectInvalidWindowsCharacters() { checker.setSafeForWindows(true); 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 39f071c98..260a643e6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java @@ -410,10 +410,68 @@ private void checkPathSegment(byte[] raw, int ptr, int end) } } - // Windows ignores space and dot at end of file name. - if (windows && (raw[end - 1] == ' ' || raw[end - 1] == '.')) - throw new CorruptObjectException("invalid name ends with '" - + ((char) raw[end - 1]) + "'"); + if (windows) { + // Windows ignores space and dot at end of file name. + if (raw[end - 1] == ' ' || raw[end - 1] == '.') + throw new CorruptObjectException("invalid name ends with '" + + ((char) raw[end - 1]) + "'"); + if (end - ptr >= 3) + checkNotWindowsDevice(raw, ptr, end); + } + } + + private static void checkNotWindowsDevice(byte[] raw, int ptr, int end) + throws CorruptObjectException { + switch (toLower(raw[ptr])) { + case 'a': // AUX + if (end - ptr >= 3 + && toLower(raw[ptr + 1]) == 'u' + && toLower(raw[ptr + 2]) == 'x' + && (end - ptr == 3 || raw[ptr + 3] == '.')) + throw new CorruptObjectException("invalid name 'AUX'"); + break; + + case 'c': // CON, COM[1-9] + if (end - ptr >= 3 + && toLower(raw[ptr + 2]) == 'n' + && toLower(raw[ptr + 1]) == 'o' + && (end - ptr == 3 || raw[ptr + 3] == '.')) + throw new CorruptObjectException("invalid name 'CON'"); + if (end - ptr >= 4 + && toLower(raw[ptr + 2]) == 'm' + && toLower(raw[ptr + 1]) == 'o' + && isPositiveDigit(raw[ptr + 3]) + && (end - ptr == 4 || raw[ptr + 4] == '.')) + throw new CorruptObjectException("invalid name 'COM" + + ((char) raw[ptr + 3]) + "'"); + break; + + case 'l': // LPT[1-9] + if (end - ptr >= 4 + && toLower(raw[ptr + 1]) == 'p' + && toLower(raw[ptr + 2]) == 't' + && isPositiveDigit(raw[ptr + 3]) + && (end - ptr == 4 || raw[ptr + 4] == '.')) + throw new CorruptObjectException("invalid name 'LPT" + + ((char) raw[ptr + 3]) + "'"); + break; + + case 'n': // NUL + if (end - ptr >= 3 + && toLower(raw[ptr + 1]) == 'u' + && toLower(raw[ptr + 2]) == 'l' + && (end - ptr == 3 || raw[ptr + 3] == '.')) + throw new CorruptObjectException("invalid name 'NUL'"); + break; + + case 'p': // PRN + if (end - ptr >= 3 + && toLower(raw[ptr + 1]) == 'r' + && toLower(raw[ptr + 2]) == 'n' + && (end - ptr == 3 || raw[ptr + 3] == '.')) + throw new CorruptObjectException("invalid name 'PRN'"); + break; + } } private static boolean isInvalidOnWindows(byte c) { @@ -446,6 +504,10 @@ private static char toLower(byte b) { return (char) b; } + private static boolean isPositiveDigit(byte b) { + return '1' <= b && b <= '9'; + } + /** * Check a blob for errors. *