Skip to content

Commit 8efb1ad

Browse files
committed
Check file entitlements on the Lucene FilterFileSystem in tests (#130825)
So far most file entitlement checks have been trivially allowed due to usage of the Lucene FilterFileSystem. This makes sure we properly check for that file system in tests. Fixes #127686 Fixes #127193 Fixes #127192 Fixes #127190 Relates to ES-12210 Relates to ES-12242 (cherry picked from commit 3f0b14b) # Conflicts: # muted-tests.yml
1 parent 6f0ed56 commit 8efb1ad

File tree

11 files changed

+63
-37
lines changed

11 files changed

+63
-37
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ private static PolicyManager createPolicyManager(
161161
PathLookup pathLookup,
162162
Policy serverPolicyPatch,
163163
Function<Class<?>, PolicyManager.PolicyScope> scopeResolver,
164-
Map<String, Collection<Path>> pluginSourcePaths
164+
Map<String, Collection<Path>> pluginSourcePathsResolver
165165
) {
166166
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
167167

@@ -170,7 +170,7 @@ private static PolicyManager createPolicyManager(
170170
HardcodedEntitlements.agentEntitlements(),
171171
pluginPolicies,
172172
scopeResolver,
173-
pluginSourcePaths,
173+
pluginSourcePathsResolver::get,
174174
pathLookup
175175
);
176176
}

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

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

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

12+
import org.elasticsearch.core.PathUtils;
13+
1214
import java.nio.file.Path;
1315
import java.util.stream.Stream;
1416

1517
/**
1618
* Resolves paths for known directories checked by entitlements.
1719
*/
1820
public interface PathLookup {
21+
Class<?> DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass();
22+
1923
enum BaseDir {
2024
USER_HOME,
2125
CONFIG,
@@ -37,4 +41,6 @@ enum BaseDir {
3741
* paths of the given {@code baseDir}.
3842
*/
3943
Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName);
44+
45+
boolean isPathOnDefaultFilesystem(Path path);
4046
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,9 @@ public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
7575
.toList();
7676
return getBaseDirPaths(baseDir).flatMap(path -> relativePaths.stream().map(path::resolve));
7777
}
78+
79+
@Override
80+
public boolean isPathOnDefaultFilesystem(Path path) {
81+
return path.getFileSystem().getClass() == DEFAULT_FILESYSTEM_CLASS;
82+
}
7883
}

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

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

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

12-
import org.elasticsearch.core.PathUtils;
1312
import org.elasticsearch.core.Strings;
1413
import org.elasticsearch.core.SuppressForbidden;
1514
import org.elasticsearch.entitlement.instrumentation.InstrumentationService;
@@ -58,7 +57,7 @@
5857
*/
5958
@SuppressForbidden(reason = "Explicitly checking APIs that are forbidden")
6059
public class PolicyCheckerImpl implements PolicyChecker {
61-
static final Class<?> DEFAULT_FILESYSTEM_CLASS = PathUtils.getDefaultFileSystem().getClass();
60+
6261
protected final Set<Package> suppressFailureLogPackages;
6362
/**
6463
* Frames originating from this module are ignored in the permission logic.
@@ -81,15 +80,14 @@ public PolicyCheckerImpl(
8180
this.pathLookup = pathLookup;
8281
}
8382

84-
private static boolean isPathOnDefaultFilesystem(Path path) {
85-
var pathFileSystemClass = path.getFileSystem().getClass();
86-
if (path.getFileSystem().getClass() != DEFAULT_FILESYSTEM_CLASS) {
83+
private boolean isPathOnDefaultFilesystem(Path path) {
84+
if (pathLookup.isPathOnDefaultFilesystem(path) == false) {
8785
PolicyManager.generalLogger.trace(
8886
() -> Strings.format(
8987
"File entitlement trivially allowed: path [%s] is for a different FileSystem class [%s], default is [%s]",
9088
path.toString(),
91-
pathFileSystemClass.getName(),
92-
DEFAULT_FILESYSTEM_CLASS.getName()
89+
path.getFileSystem().getClass().getName(),
90+
PathLookup.DEFAULT_FILESYSTEM_CLASS.getName()
9391
)
9492
);
9593
return false;
@@ -217,7 +215,7 @@ public void checkFileRead(Class<?> callerClass, Path path) {
217215

218216
@Override
219217
public void checkFileRead(Class<?> callerClass, Path path, boolean followLinks) throws NoSuchFileException {
220-
if (PolicyCheckerImpl.isPathOnDefaultFilesystem(path) == false) {
218+
if (isPathOnDefaultFilesystem(path) == false) {
221219
return;
222220
}
223221
var requestingClass = requestingClass(callerClass);
@@ -265,7 +263,7 @@ public void checkFileWrite(Class<?> callerClass, File file) {
265263

266264
@Override
267265
public void checkFileWrite(Class<?> callerClass, Path path) {
268-
if (PolicyCheckerImpl.isPathOnDefaultFilesystem(path) == false) {
266+
if (isPathOnDefaultFilesystem(path) == false) {
269267
return;
270268
}
271269
var requestingClass = requestingClass(callerClass);

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import java.nio.file.Paths;
2323
import java.util.ArrayList;
2424
import java.util.Collection;
25+
import java.util.Collections;
2526
import java.util.HashSet;
2627
import java.util.List;
2728
import java.util.Map;
29+
import java.util.Objects;
2830
import java.util.Set;
2931
import java.util.concurrent.ConcurrentHashMap;
3032
import java.util.function.Function;
@@ -217,7 +219,7 @@ private static Set<Module> findSystemLayerModules() {
217219
.filter(m -> SYSTEM_LAYER_MODULES.contains(m) == false)
218220
.collect(Collectors.toUnmodifiableSet());
219221

220-
private final Map<String, Collection<Path>> pluginSourcePaths;
222+
private final Function<String, Collection<Path>> pluginSourcePathsResolver;
221223

222224
/**
223225
* Paths that are only allowed for a single module. Used to generate
@@ -231,7 +233,7 @@ public PolicyManager(
231233
List<Entitlement> apmAgentEntitlements,
232234
Map<String, Policy> pluginPolicies,
233235
Function<Class<?>, PolicyScope> scopeResolver,
234-
Map<String, Collection<Path>> pluginSourcePaths,
236+
Function<String, Collection<Path>> pluginSourcePathsResolver,
235237
PathLookup pathLookup
236238
) {
237239
this.serverEntitlements = buildScopeEntitlementsMap(requireNonNull(serverPolicy));
@@ -240,7 +242,7 @@ public PolicyManager(
240242
.stream()
241243
.collect(toUnmodifiableMap(Map.Entry::getKey, e -> buildScopeEntitlementsMap(e.getValue())));
242244
this.scopeResolver = scopeResolver;
243-
this.pluginSourcePaths = pluginSourcePaths;
245+
this.pluginSourcePathsResolver = pluginSourcePathsResolver;
244246
this.pathLookup = requireNonNull(pathLookup);
245247

246248
List<ExclusiveFileEntitlement> exclusiveFileEntitlements = new ArrayList<>();
@@ -334,7 +336,10 @@ protected final ModuleEntitlements computeEntitlements(Class<?> requestingClass)
334336
default -> {
335337
assert policyScope.kind() == PLUGIN;
336338
var pluginEntitlements = pluginsEntitlements.get(componentName);
337-
Collection<Path> componentPaths = pluginSourcePaths.getOrDefault(componentName, List.of());
339+
Collection<Path> componentPaths = Objects.requireNonNullElse(
340+
pluginSourcePathsResolver.apply(componentName),
341+
Collections.emptyList()
342+
);
338343
if (pluginEntitlements == null) {
339344
return defaultEntitlements(componentName, componentPaths, moduleName);
340345
} else {

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.net.URLClassLoader;
3434
import java.nio.file.Path;
3535
import java.util.Collection;
36+
import java.util.Collections;
3637
import java.util.List;
3738
import java.util.Map;
3839
import java.util.Set;
@@ -95,7 +96,7 @@ public void testGetEntitlements() {
9596
List.of(),
9697
Map.of("plugin1", new Policy("plugin1", List.of(new Scope("plugin.module1", List.of(new ExitVMEntitlement()))))),
9798
c -> policyScope.get(),
98-
Map.of("plugin1", plugin1SourcePaths),
99+
Map.of("plugin1", plugin1SourcePaths)::get,
99100
TEST_PATH_LOOKUP
100101
);
101102
Collection<Path> thisSourcePaths = policyManager.getComponentPathsFromClass(getClass());
@@ -170,7 +171,7 @@ public void testAgentsEntitlements() throws IOException, ClassNotFoundException
170171
c -> c.getPackageName().startsWith(TEST_AGENTS_PACKAGE_NAME)
171172
? PolicyScope.apmAgent("test.agent.module")
172173
: PolicyScope.plugin("test", "test.plugin.module"),
173-
Map.of(),
174+
name -> Collections.emptyList(),
174175
TEST_PATH_LOOKUP
175176
);
176177
ModuleEntitlements agentsEntitlements = policyManager.getEntitlements(TestAgent.class);
@@ -197,7 +198,7 @@ public void testDuplicateEntitlements() {
197198
List.of(),
198199
Map.of(),
199200
c -> PolicyScope.plugin("test", moduleName(c)),
200-
Map.of(),
201+
name -> Collections.emptyList(),
201202
TEST_PATH_LOOKUP
202203
)
203204
);
@@ -213,7 +214,7 @@ public void testDuplicateEntitlements() {
213214
List.of(new CreateClassLoaderEntitlement(), new CreateClassLoaderEntitlement()),
214215
Map.of(),
215216
c -> PolicyScope.plugin("test", moduleName(c)),
216-
Map.of(),
217+
name -> Collections.emptyList(),
217218
TEST_PATH_LOOKUP
218219
)
219220
);
@@ -249,7 +250,7 @@ public void testDuplicateEntitlements() {
249250
)
250251
),
251252
c -> PolicyScope.plugin("plugin1", moduleName(c)),
252-
Map.of("plugin1", List.of(Path.of("modules", "plugin1"))),
253+
Map.of("plugin1", List.of(Path.of("modules", "plugin1")))::get,
253254
TEST_PATH_LOOKUP
254255
)
255256
);
@@ -299,7 +300,7 @@ public void testFilesEntitlementsWithExclusive() {
299300
)
300301
),
301302
c -> PolicyScope.plugin("", moduleName(c)),
302-
Map.of("plugin1", List.of(Path.of("modules", "plugin1")), "plugin2", List.of(Path.of("modules", "plugin2"))),
303+
Map.of("plugin1", List.of(Path.of("modules", "plugin1")), "plugin2", List.of(Path.of("modules", "plugin2")))::get,
303304
TEST_PATH_LOOKUP
304305
)
305306
);
@@ -350,7 +351,7 @@ public void testFilesEntitlementsWithExclusive() {
350351
)
351352
),
352353
c -> PolicyScope.plugin("", moduleName(c)),
353-
Map.of(),
354+
name -> Collections.emptyList(),
354355
TEST_PATH_LOOKUP
355356
)
356357
);

test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@
3434
import java.nio.file.Path;
3535
import java.util.ArrayList;
3636
import java.util.Arrays;
37-
import java.util.Collection;
3837
import java.util.HashMap;
3938
import java.util.List;
4039
import java.util.Map;
4140
import java.util.Set;
4241
import java.util.TreeSet;
4342

4443
import static java.util.stream.Collectors.toCollection;
45-
import static java.util.stream.Collectors.toMap;
4644
import static java.util.stream.Collectors.toSet;
4745
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.CONFIG;
4846
import static org.elasticsearch.entitlement.runtime.policy.PathLookup.BaseDir.TEMP;
@@ -128,8 +126,6 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
128126
} else {
129127
classPathEntries = Arrays.stream(classPathProperty.split(separator)).map(PathUtils::get).collect(toCollection(TreeSet::new));
130128
}
131-
Map<String, Collection<Path>> pluginSourcePaths = pluginNames.stream().collect(toMap(n -> n, n -> classPathEntries));
132-
133129
FilesEntitlementsValidation.validate(pluginPolicies, pathLookup);
134130

135131
String testOnlyPathString = System.getenv("es.entitlement.testOnlyPath");
@@ -148,8 +144,8 @@ private static TestPolicyManager createPolicyManager(PathLookup pathLookup) thro
148144
HardcodedEntitlements.agentEntitlements(),
149145
pluginPolicies,
150146
scopeResolver,
151-
pluginSourcePaths,
152147
pathLookup,
148+
classPathEntries,
153149
testOnlyClassPath
154150
);
155151
}

test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPathLookup.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

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

12+
import org.apache.lucene.tests.mockfile.FilterFileSystem;
13+
1214
import java.nio.file.Path;
1315
import java.util.Collection;
1416
import java.util.List;
@@ -37,4 +39,14 @@ public Stream<Path> resolveSettingPaths(BaseDir baseDir, String settingName) {
3739
return Stream.empty();
3840
}
3941

42+
@Override
43+
public boolean isPathOnDefaultFilesystem(Path path) {
44+
var fileSystem = path.getFileSystem();
45+
if (fileSystem.getClass() != DEFAULT_FILESYSTEM_CLASS) {
46+
while (fileSystem instanceof FilterFileSystem ffs) {
47+
fileSystem = ffs.getDelegate();
48+
}
49+
}
50+
return fileSystem.getClass() == DEFAULT_FILESYSTEM_CLASS;
51+
}
4052
}

test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,20 @@ public class TestPolicyManager extends PolicyManager {
3838
* We need this larger map per class instead.
3939
*/
4040
final Map<Class<?>, ModuleEntitlements> classEntitlementsMap = new ConcurrentHashMap<>();
41-
41+
final Collection<Path> classpath;
4242
final Collection<URI> testOnlyClasspath;
4343

4444
public TestPolicyManager(
4545
Policy serverPolicy,
4646
List<Entitlement> apmAgentEntitlements,
4747
Map<String, Policy> pluginPolicies,
4848
Function<Class<?>, PolicyScope> scopeResolver,
49-
Map<String, Collection<Path>> pluginSourcePaths,
5049
PathLookup pathLookup,
50+
Collection<Path> classpath,
5151
Collection<URI> testOnlyClasspath
5252
) {
53-
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, pluginSourcePaths, pathLookup);
53+
super(serverPolicy, apmAgentEntitlements, pluginPolicies, scopeResolver, name -> classpath, pathLookup);
54+
this.classpath = classpath;
5455
this.testOnlyClasspath = testOnlyClasspath;
5556
reset();
5657
}
@@ -118,6 +119,11 @@ boolean isTriviallyAllowed(Class<?> requestingClass) {
118119
return super.isTriviallyAllowed(requestingClass);
119120
}
120121

122+
@Override
123+
protected Collection<Path> getComponentPathsFromClass(Class<?> requestingClass) {
124+
return classpath; // required to grant read access to the production source and test resources
125+
}
126+
121127
private boolean isEntitlementClass(Class<?> requestingClass) {
122128
return requestingClass.getPackageName().startsWith("org.elasticsearch.entitlement")
123129
&& (requestingClass.getName().contains("Test") == false);
@@ -180,6 +186,9 @@ private boolean isTestCode(Class<?> requestingClass) {
180186
URI needle;
181187
try {
182188
needle = codeSource.getLocation().toURI();
189+
if (needle.getScheme().equals("jrt")) {
190+
return false; // won't be on testOnlyClasspath
191+
}
183192
} catch (URISyntaxException e) {
184193
throw new IllegalStateException(e);
185194
}

test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ public void setupPolicyManager() {
3434
List.of(),
3535
Map.of(),
3636
c -> new PolicyScope(PLUGIN, "example-plugin" + scopeCounter.incrementAndGet(), "org.example.module"),
37-
Map.of(),
3837
new TestPathLookup(Map.of()),
38+
List.of(),
3939
List.of()
4040
);
4141
policyManager.setActive(true);

0 commit comments

Comments
 (0)