Skip to content

Commit 33c9164

Browse files
prdoyleelasticsearchmachine
andauthored
Entitlement tests using reflection (#121436) (#121452)
* Entitlement IT cases for reflection * EntitlementBootstrap selfTest using reflection * Remove errant logging setting * Lambdas instead of booleans * [CI] Auto commit changes from spotless * Refactor: Extract lambdas to method refs --------- Co-authored-by: elasticsearchmachine <[email protected]>
1 parent 772b9e6 commit 33c9164

File tree

4 files changed

+71
-13
lines changed

4 files changed

+71
-13
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.core;
11+
12+
/**
13+
* A {@link java.util.function.Supplier}-like interface which allows throwing checked exceptions.
14+
*/
15+
@FunctionalInterface
16+
public interface CheckedSupplier<T, E extends Exception> {
17+
T get() throws E;
18+
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@
5252
* <p>
5353
* A bit like Mockito but way more painful.
5454
*/
55-
class DummyImplementations {
56-
57-
static class DummyLocaleServiceProvider extends LocaleServiceProvider {
55+
public class DummyImplementations {
5856

57+
public static class DummyLocaleServiceProvider extends LocaleServiceProvider {
5958
@Override
6059
public Locale[] getAvailableLocales() {
6160
throw unexpected();

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
9696

9797
private static final Map<String, CheckAction> checkActions = Stream.concat(
9898
Stream.<Entry<String, CheckAction>>of(
99+
entry("static_reflection", deniedToPlugins(RestEntitlementsCheckAction::staticMethodNeverEntitledViaReflection)),
100+
entry("nonstatic_reflection", deniedToPlugins(RestEntitlementsCheckAction::nonstaticMethodNeverEntitledViaReflection)),
101+
entry("constructor_reflection", deniedToPlugins(RestEntitlementsCheckAction::constructorNeverEntitledViaReflection)),
99102
entry("runtime_exit", deniedToPlugins(RestEntitlementsCheckAction::runtimeExit)),
100103
entry("runtime_halt", deniedToPlugins(RestEntitlementsCheckAction::runtimeHalt)),
101104
entry("system_exit", deniedToPlugins(RestEntitlementsCheckAction::systemExit)),
@@ -338,6 +341,11 @@ private static void systemExit() {
338341
System.exit(123);
339342
}
340343

344+
private static void staticMethodNeverEntitledViaReflection() throws Exception {
345+
Method systemExit = System.class.getMethod("exit", int.class);
346+
systemExit.invoke(null, 123);
347+
}
348+
341349
private static void createClassLoader() throws IOException {
342350
try (var classLoader = new URLClassLoader("test", new URL[0], RestEntitlementsCheckAction.class.getClassLoader())) {
343351
logger.info("Created URLClassLoader [{}]", classLoader.getName());
@@ -348,6 +356,11 @@ private static void processBuilder_start() throws IOException {
348356
new ProcessBuilder("").start();
349357
}
350358

359+
private static void nonstaticMethodNeverEntitledViaReflection() throws Exception {
360+
Method processBuilderStart = ProcessBuilder.class.getMethod("start");
361+
processBuilderStart.invoke(new ProcessBuilder(""));
362+
}
363+
351364
private static void processBuilder_startPipeline() throws IOException {
352365
ProcessBuilder.startPipeline(List.of());
353366
}
@@ -386,6 +399,10 @@ private static void setHttpsConnectionProperties() {
386399
new DummyLocaleServiceProvider();
387400
}
388401

402+
private static void constructorNeverEntitledViaReflection() throws Exception {
403+
DummyLocaleServiceProvider.class.getConstructor().newInstance();
404+
}
405+
389406
private static void breakIteratorProvider$() {
390407
new DummyBreakIteratorProvider();
391408
}

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

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import com.sun.tools.attach.AttachNotSupportedException;
1515
import com.sun.tools.attach.VirtualMachine;
1616

17+
import org.elasticsearch.core.CheckedConsumer;
18+
import org.elasticsearch.core.CheckedSupplier;
1719
import org.elasticsearch.core.SuppressForbidden;
1820
import org.elasticsearch.entitlement.initialization.EntitlementInitialization;
1921
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
@@ -22,8 +24,10 @@
2224
import org.elasticsearch.logging.Logger;
2325

2426
import java.io.IOException;
27+
import java.lang.reflect.InvocationTargetException;
2528
import java.nio.file.Files;
2629
import java.nio.file.Path;
30+
import java.nio.file.attribute.FileAttribute;
2731
import java.util.Map;
2832
import java.util.function.Function;
2933

@@ -144,30 +148,31 @@ private static String findAgentJar() {
144148
* @throws IllegalStateException if the entitlements system can't prevent an unauthorized action of our choosing
145149
*/
146150
private static void selfTest() {
147-
ensureCannotStartProcess();
148-
ensureCanCreateTempFile();
151+
ensureCannotStartProcess(ProcessBuilder::start);
152+
ensureCanCreateTempFile(EntitlementBootstrap::createTempFile);
153+
154+
// Try again with reflection
155+
ensureCannotStartProcess(EntitlementBootstrap::reflectiveStartProcess);
156+
ensureCanCreateTempFile(EntitlementBootstrap::reflectiveCreateTempFile);
149157
}
150158

151-
private static void ensureCannotStartProcess() {
159+
private static void ensureCannotStartProcess(CheckedConsumer<ProcessBuilder, ?> startProcess) {
152160
try {
153161
// The command doesn't matter; it doesn't even need to exist
154-
new ProcessBuilder("").start();
162+
startProcess.accept(new ProcessBuilder(""));
155163
} catch (NotEntitledException e) {
156164
logger.debug("Success: Entitlement protection correctly prevented process creation");
157165
return;
158-
} catch (IOException e) {
166+
} catch (Exception e) {
159167
throw new IllegalStateException("Failed entitlement protection self-test", e);
160168
}
161169
throw new IllegalStateException("Entitlement protection self-test was incorrectly permitted");
162170
}
163171

164-
/**
165-
* Originally {@code Security.selfTest}.
166-
*/
167172
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
168-
private static void ensureCanCreateTempFile() {
173+
private static void ensureCanCreateTempFile(CheckedSupplier<Path, ?> createTempFile) {
169174
try {
170-
Path p = Files.createTempFile(null, null);
175+
Path p = createTempFile.get();
171176
p.toFile().deleteOnExit();
172177

173178
// Make an effort to clean up the file immediately; also, deleteOnExit leaves the file if the JVM exits abnormally.
@@ -184,5 +189,24 @@ private static void ensureCanCreateTempFile() {
184189
logger.debug("Success: Entitlement protection correctly permitted temp file creation");
185190
}
186191

192+
@SuppressForbidden(reason = "accesses jvm default tempdir as a self-test")
193+
private static Path createTempFile() throws Exception {
194+
return Files.createTempFile(null, null);
195+
}
196+
197+
private static void reflectiveStartProcess(ProcessBuilder pb) throws Exception {
198+
try {
199+
var start = ProcessBuilder.class.getMethod("start");
200+
start.invoke(pb);
201+
} catch (InvocationTargetException e) {
202+
throw (Exception) e.getCause();
203+
}
204+
}
205+
206+
private static Path reflectiveCreateTempFile() throws Exception {
207+
return (Path) Files.class.getMethod("createTempFile", String.class, String.class, FileAttribute[].class)
208+
.invoke(null, null, null, new FileAttribute<?>[0]);
209+
}
210+
187211
private static final Logger logger = LogManager.getLogger(EntitlementBootstrap.class);
188212
}

0 commit comments

Comments
 (0)