Skip to content

Commit 4d26454

Browse files
committed
changed to assume always path separator are always normalized before comparison/ordering
1 parent 255a745 commit 4d26454

File tree

3 files changed

+58
-21
lines changed

3 files changed

+58
-21
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileUtils.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,18 @@ private FileUtils() {}
5050
return len1 - len2;
5151
};
5252

53-
/**
54-
* Java allows to use both forward and backslash on Windows systems as path separators;
55-
* On posix filesystems however '\' is a valid character for file or directory names, so we stick to just the
56-
* standard platform separator for anything that is not Windows.
57-
*/
5853
@SuppressForbidden(reason = "we need the separator as a char, not a string")
5954
private static boolean isPathSeparator(char c) {
60-
if (Platform.WINDOWS.isCurrent()) {
61-
return isSlash(c);
62-
}
6355
return c == File.separatorChar;
6456
}
6557

6658
/**
6759
* Tests if a path is absolute or relative, taking into consideration both Unix and Windows conventions.
6860
* Note that this leads to a conflict, resolved in favor of Unix rules: `/foo` can be either a Unix absolute path, or a Windows
69-
* relative path with "wrong" directory separator (using non-canonical slash in Windows).
61+
* relative path with "wrong" directory separator (using non-canonical / in Windows).
62+
* This method is intended to be used as validation for different file entitlements format: therefore it is preferable to reject a
63+
* relative path that is definitely absolute on Unix, rather than accept it as a possible relative path on Windows (if that is the case,
64+
* the developer can easily fix the path by using the correct platform separators).
7065
*/
7166
public static boolean isAbsolutePath(String path) {
7267
if (path.isEmpty()) {
@@ -80,6 +75,10 @@ public static boolean isAbsolutePath(String path) {
8075
return isWindowsAbsolutePath(path);
8176
}
8277

78+
/**
79+
* When testing for path separators in a platform-agnostic way, we may encounter both kinds of slashes, especially when
80+
* processing windows paths. The JDK parses paths the same way under Windows.
81+
*/
8382
static boolean isSlash(char c) {
8483
return (c == '\\') || (c == '/');
8584
}

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727

2828
import static org.elasticsearch.core.PathUtils.getDefaultFileSystem;
29+
import static org.hamcrest.Matchers.equalTo;
2930
import static org.hamcrest.Matchers.is;
3031

3132
@ESTestCase.WithoutSecurityManager
@@ -194,6 +195,48 @@ public void testNormalizePath() {
194195
assertThat(tree.canRead(path("")), is(false));
195196
}
196197

198+
public void testNormalizeDirectorySeparatorWindows() {
199+
assumeTrue("normalization of windows paths", Platform.WINDOWS.isCurrent());
200+
201+
assertThat(FileAccessTree.normalizePath(Path.of("C:\\a\\b")), equalTo("C:\\a\\b"));
202+
assertThat(FileAccessTree.normalizePath(Path.of("C:/a.xml")), equalTo("C:\\a.xml"));
203+
assertThat(FileAccessTree.normalizePath(Path.of("C:/a/b.txt")), equalTo("C:\\a\\b.txt"));
204+
assertThat(FileAccessTree.normalizePath(Path.of("C:/a/c\\foo.txt")), equalTo("C:\\a\\c\\foo.txt"));
205+
206+
var tree = accessTree(
207+
entitlement("C:\\a\\b", "read", "C:/a.xml", "read", "C:/a/b.txt", "read", "C:/a/c\\foo.txt", "read"),
208+
List.of()
209+
);
210+
211+
assertThat(tree.canRead(Path.of("C:/a.xml")), is(true));
212+
assertThat(tree.canRead(Path.of("C:\\a.xml")), is(true));
213+
assertThat(tree.canRead(Path.of("C:/a/")), is(false));
214+
assertThat(tree.canRead(Path.of("C:/a/b.txt")), is(true));
215+
assertThat(tree.canRead(Path.of("C:/a/b/c.txt")), is(true));
216+
assertThat(tree.canRead(Path.of("C:\\a\\b\\c.txt")), is(true));
217+
assertThat(tree.canRead(Path.of("C:\\a\\c\\")), is(false));
218+
assertThat(tree.canRead(Path.of("C:\\a\\c\\foo.txt")), is(true));
219+
}
220+
221+
public void testNormalizeDirectorySeparatorPosix() {
222+
assumeFalse("normalization of posix paths", Platform.WINDOWS.isCurrent());
223+
224+
assertThat(FileAccessTree.normalizePath(Path.of("\\a\\b")), equalTo("/a/b"));
225+
assertThat(FileAccessTree.normalizePath(Path.of("/a.xml")), equalTo("/a.xml"));
226+
assertThat(FileAccessTree.normalizePath(Path.of("/a/c\\foo.txt")), equalTo("/a/c/foo.txt"));
227+
228+
var tree = accessTree(entitlement("\\a\\b", "read", "/a.xml", "read", "/a/b.txt", "read", "/a/c\\foo.txt", "read"), List.of());
229+
230+
assertThat(tree.canRead(Path.of("/a.xml")), is(true));
231+
assertThat(tree.canRead(Path.of("\\a.xml")), is(true));
232+
assertThat(tree.canRead(Path.of("/a/")), is(false));
233+
assertThat(tree.canRead(Path.of("/a/b.txt")), is(true));
234+
assertThat(tree.canRead(Path.of("/a/b/c.txt")), is(true));
235+
assertThat(tree.canRead(Path.of("\\a\\b\\c.txt")), is(true));
236+
assertThat(tree.canRead(Path.of("\\a\\c\\")), is(false));
237+
assertThat(tree.canRead(Path.of("\\a\\c\\foo.txt")), is(true));
238+
}
239+
197240
public void testNormalizeTrailingSlashes() {
198241
var tree = accessTree(entitlement("/trailing/slash/", "read", "/no/trailing/slash", "read"), List.of());
199242
assertThat(tree.canRead(path("/trailing/slash")), is(true));

libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileUtilsTests.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ public void testPathIsAbsolute() {
4242
assertThat(isAbsolutePath(""), is(false));
4343
}
4444

45-
public void testPathOrder() {
45+
public void testPathOrderPosix() {
46+
assumeFalse("path ordering rules specific to non-Windows path styles", Platform.WINDOWS.isCurrent());
47+
4648
// Unix-style
4749
// Directories come BEFORE files; note that this differs from natural lexicographical order
4850
assertThat(PATH_ORDER.compare("/a/b", "/a.xml"), lessThan(0));
@@ -60,27 +62,20 @@ public void testPathOrder() {
6062
assertThat(PATH_ORDER.compare("C:/a/b", "C:/a/b.txt"), lessThan(0));
6163
assertThat(PATH_ORDER.compare("C:/a/c", "C:/a/b.txt"), greaterThan(0));
6264
assertThat(PATH_ORDER.compare("C:/a/b", "C:/a/b/foo.txt"), lessThan(0));
63-
}
64-
65-
public void testPathOrderPosix() {
66-
assumeFalse("path ordering rules specific to non-Windows platforms", Platform.WINDOWS.isCurrent());
6765

66+
// "\" is a valid file name character on Posix, test we treat it like that
6867
assertThat(PATH_ORDER.compare("/a\\b", "/a/b.txt"), greaterThan(0));
6968
}
7069

7170
public void testPathOrderWindows() {
7271
assumeTrue("path ordering rules specific to Windows", Platform.WINDOWS.isCurrent());
7372

74-
// Windows-style
73+
// Directories come BEFORE files; note that this differs from natural lexicographical order
7574
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a.xml"), lessThan(0));
75+
76+
// Natural lexicographical order is respected in all the other cases
7677
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a\\b.txt"), lessThan(0));
7778
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a\\b\\foo.txt"), lessThan(0));
7879
assertThat(PATH_ORDER.compare("C:\\a\\c", "C:\\a\\b.txt"), greaterThan(0));
79-
80-
// Mixed-style
81-
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:/a.xml"), lessThan(0));
82-
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:/a/b.txt"), lessThan(0));
83-
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:/a/b\\foo.txt"), lessThan(0));
84-
assertThat(PATH_ORDER.compare("C:/a\\b", "C:\\a\\b.txt"), lessThan(0));
8580
}
8681
}

0 commit comments

Comments
 (0)