checker.checkTree(data);
}
+ @Test
+ public void testValidPosixTree() throws CorruptObjectException {
+ checkOneName("a<b>c:d|e");
+ checkOneName("test ");
+ checkOneName("test.");
+ }
+
@Test
public void testValidTreeSorting1() throws CorruptObjectException {
final StringBuilder b = new StringBuilder();
}
}
+ @Test
+ public void testInvalidTreeNameIsMixedCaseGit() {
+ StringBuilder b = new StringBuilder();
+ entry(b, "100644 .GiT");
+ byte[] data = Constants.encodeASCII(b.toString());
+ try {
+ checker.setSafeForWindows(true);
+ 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();
}
}
+ @Test
+ public void testRejectSpaceAtEndOnWindows() {
+ checker.setSafeForWindows(true);
+ try {
+ checkOneName("test ");
+ fail("incorrectly accepted space at end");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid name ends with ' '", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testRejectDotAtEndOnWindows() {
+ checker.setSafeForWindows(true);
+ try {
+ checkOneName("test.");
+ fail("incorrectly accepted dot at end");
+ } catch (CorruptObjectException e) {
+ assertEquals("invalid name ends with '.'", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testRejectInvalidWindowsCharacters() {
+ checker.setSafeForWindows(true);
+ rejectName('<');
+ rejectName('>');
+ rejectName(':');
+ rejectName('"');
+ rejectName('/');
+ rejectName('\\');
+ rejectName('|');
+ rejectName('?');
+ rejectName('*');
+
+ for (int i = 1; i <= 31; i++)
+ rejectName((byte) i);
+ }
+
+ private void rejectName(char c) {
+ try {
+ checkOneName("te" + c + "st");
+ fail("incorrectly accepted with " + c);
+ } catch (CorruptObjectException e) {
+ assertEquals("name contains '" + c + "'", e.getMessage());
+ }
+ }
+
+ private void rejectName(byte c) {
+ String h = Integer.toHexString(c);
+ try {
+ checkOneName("te" + ((char) c) + "st");
+ fail("incorrectly accepted with 0x" + h);
+ } catch (CorruptObjectException e) {
+ assertEquals("name contains byte 0x" + h, e.getMessage());
+ }
+ }
+
+ private void checkOneName(String name) throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
+ entry(b, "100644 " + name);
+ checker.checkTree(Constants.encodeASCII(b.toString()));
+ }
+
private static void entry(final StringBuilder b, final String modeName) {
b.append(modeName);
b.append('\0');
private final MutableInteger ptrout = new MutableInteger();
private boolean allowZeroMode;
+ private boolean windows;
/**
* Enable accepting leading zero mode in tree entries.
return this;
}
+ /**
+ * Restrict trees to only names legal on Windows platforms.
+ * <p>
+ * Also rejects any mixed case forms of reserved names ({@code .git}).
+ *
+ * @param win true if Windows name checking should be performed.
+ * @return {@code this}.
+ * @since 3.4
+ */
+ public ObjectChecker setSafeForWindows(boolean win) {
+ windows = win;
+ return this;
+ }
+
/**
* Check an object for parsing errors.
*
break;
if (c == '/')
throw new CorruptObjectException("name contains '/'");
+ if (windows && isInvalidOnWindows(c)) {
+ if (c > 31)
+ throw new CorruptObjectException(String.format(
+ "name contains '%c'", c));
+ throw new CorruptObjectException(String.format(
+ "name contains byte 0x%x", c & 0xff));
+ }
}
checkPathSegment(raw, thisNameB, ptr - 1);
if (duplicateName(raw, thisNameB, ptr - 1))
}
}
- private static void checkPathSegment(byte[] raw, int ptr, int end)
+ private void checkPathSegment(byte[] raw, int ptr, int end)
throws CorruptObjectException {
if (ptr == end)
throw new CorruptObjectException("zero length name");
RawParseUtils.decode(raw, ptr, 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]) + "'");
+ }
+
+ private static boolean isInvalidOnWindows(byte c) {
+ // Windows disallows "special" characters in a path component.
+ switch (c) {
+ case '"':
+ case '*':
+ case ':':
+ case '<':
+ case '>':
+ case '?':
+ case '\\':
+ case '|':
+ return true;
+ }
+ return 1 <= c && c <= 31;
}
- private static boolean isDotGit(byte[] buf, int p) {
+ private boolean isDotGit(byte[] buf, int p) {
+ if (windows)
+ return toLower(buf[p]) == 'g'
+ && toLower(buf[p + 1]) == 'i'
+ && toLower(buf[p + 2]) == 't';
return buf[p] == 'g' && buf[p + 1] == 'i' && buf[p + 2] == 't';
}
+ private static char toLower(byte b) {
+ if ('A' <= b && b <= 'Z')
+ return (char) (b + ('a' - 'A'));
+ return (char) b;
+ }
+
/**
* Check a blob for errors.
*