Skip to content

Commit 1536fb2

Browse files
committed
Merge branch 'entitlements/fix-fileaccesstree-ordering' of github.com:ldematte/elasticsearch into entitlements/fix-fileaccesstree-ordering
2 parents d66892f + 2072d54 commit 1536fb2

File tree

3 files changed

+68
-20
lines changed

3 files changed

+68
-20
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
@@ -193,6 +194,48 @@ public void testNormalizePath() {
193194
assertThat(tree.canRead(path("")), is(false));
194195
}
195196

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

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12+
import org.elasticsearch.core.PathUtils;
1213
import org.elasticsearch.test.ESTestCase;
1314

1415
import static org.elasticsearch.entitlement.runtime.policy.FileUtils.PATH_ORDER;
@@ -42,7 +43,9 @@ public void testPathIsAbsolute() {
4243
assertThat(isAbsolutePath(""), is(false));
4344
}
4445

45-
public void testPathOrder() {
46+
public void testPathOrderPosix() {
47+
assumeFalse("path ordering rules specific to non-Windows path styles", Platform.WINDOWS.isCurrent());
48+
4649
// Unix-style
4750
// Directories come BEFORE files; note that this differs from natural lexicographical order
4851
assertThat(PATH_ORDER.compare("/a/b", "/a.xml"), lessThan(0));
@@ -60,27 +63,30 @@ public void testPathOrder() {
6063
assertThat(PATH_ORDER.compare("C:/a/b", "C:/a/b.txt"), lessThan(0));
6164
assertThat(PATH_ORDER.compare("C:/a/c", "C:/a/b.txt"), greaterThan(0));
6265
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());
6766

67+
// "\" is a valid file name character on Posix, test we treat it like that
6868
assertThat(PATH_ORDER.compare("/a\\b", "/a/b.txt"), greaterThan(0));
6969
}
7070

7171
public void testPathOrderWindows() {
7272
assumeTrue("path ordering rules specific to Windows", Platform.WINDOWS.isCurrent());
7373

74-
// Windows-style
74+
// Directories come BEFORE files; note that this differs from natural lexicographical order
7575
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a.xml"), lessThan(0));
76+
77+
// Natural lexicographical order is respected in all the other cases
7678
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a\\b.txt"), lessThan(0));
7779
assertThat(PATH_ORDER.compare("C:\\a\\b", "C:\\a\\b\\foo.txt"), lessThan(0));
7880
assertThat(PATH_ORDER.compare("C:\\a\\c", "C:\\a\\b.txt"), greaterThan(0));
81+
}
82+
83+
public void testPathOrderingSpecialCharacters() {
84+
assertThat(PATH_ORDER.compare("aa\uD801\uDC28", "aa\uD801\uDC28"), is(0));
85+
assertThat(PATH_ORDER.compare("aa\uD801\uDC28", "aa\uD801\uDC28a"), lessThan(0));
7986

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));
87+
var s = PathUtils.getDefaultFileSystem().getSeparator();
88+
// Similarly to the other tests, we assert that Directories come BEFORE files, even when names are special characters
89+
assertThat(PATH_ORDER.compare(s + "\uD801\uDC28" + s + "b", s + "\uD801\uDC28.xml"), lessThan(0));
90+
assertThat(PATH_ORDER.compare(s + "\uD801\uDC28" + s + "b", s + "b.xml"), greaterThan(0));
8591
}
8692
}

0 commit comments

Comments
 (0)