From 3a7db29cd297bc29a85cdef64622f09a053c19a0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 29 Aug 2025 17:17:29 -0700 Subject: [PATCH] Remove java.xml from system modules (#133671) java.xml is part of the jdk, but it's really a utility module that shouldn't have direct access to network or files. This commit excludes java.xml from system modules. Note that since it is part of the jdk, it does need access to read jdk classes, so a new internal entitlement is also added to allow reading jrt urls. --- docs/changelog/133671.yaml | 5 +++++ .../entitlement/qa/test/FileCheckActions.java | 9 +++++++++ .../entitlement/qa/test/JvmActions.java | 10 ++++++++++ .../qa/test/NetworkAccessCheckActions.java | 9 +++++++++ .../bootstrap/HardcodedEntitlements.java | 16 ++++++++++++++++ .../runtime/policy/PolicyCheckerImpl.java | 7 +++++++ .../runtime/policy/PolicyManager.java | 2 +- .../entitlements/ReadJdkImageEntitlement.java | 19 +++++++++++++++++++ 8 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/133671.yaml create mode 100644 libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/ReadJdkImageEntitlement.java diff --git a/docs/changelog/133671.yaml b/docs/changelog/133671.yaml new file mode 100644 index 0000000000000..ee16f659e9b27 --- /dev/null +++ b/docs/changelog/133671.yaml @@ -0,0 +1,5 @@ +pr: 133671 +summary: Remove `java.xml` from system modules +area: Infra/Core +type: bug +issues: [] diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java index 0adfbf66c6a23..3ded3e3ac8924 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java @@ -13,6 +13,7 @@ import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.entitlement.qa.entitled.EntitledActions; import org.elasticsearch.env.Environment; +import org.xml.sax.helpers.DefaultHandler; import java.io.File; import java.io.FileDescriptor; @@ -39,6 +40,7 @@ import java.util.zip.ZipFile; import javax.imageio.stream.FileImageInputStream; +import javax.xml.parsers.SAXParserFactory; import static java.nio.charset.Charset.defaultCharset; import static java.nio.file.StandardOpenOption.CREATE; @@ -610,5 +612,12 @@ static void javaDesktopFileAccess() throws Exception { new FileImageInputStream(file.toFile()).close(); } + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void javaXmlFileRequest() throws Exception { + // java.xml is part of the jdk, but not a system module. this checks it can't access files + var saxParser = SAXParserFactory.newInstance().newSAXParser(); + saxParser.parse(readFile().toFile(), new DefaultHandler()); + } + private FileCheckActions() {} } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/JvmActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/JvmActions.java index 29e4ffccce0b3..8fa250c886572 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/JvmActions.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/JvmActions.java @@ -18,6 +18,9 @@ import java.util.Locale; import java.util.TimeZone; +import javax.xml.parsers.SAXParserFactory; + +import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_ALLOWED; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; @@ -80,5 +83,12 @@ static void createLogManager() { Thread.setDefaultUncaughtExceptionHandler(Thread.getDefaultUncaughtExceptionHandler()); } + @EntitlementTest(expectedAccess = ALWAYS_ALLOWED) + static void useJavaXmlParser() { + // java.xml is part of the jdk, but not a system module. this checks it's actually usable + // as it needs to read classes from the jdk which is not generally allowed + SAXParserFactory.newInstance(); + } + private JvmActions() {} } diff --git a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java index 7b99aab0a19e4..af82a29c750e9 100644 --- a/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java +++ b/libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java @@ -10,6 +10,7 @@ package org.elasticsearch.entitlement.qa.test; import org.elasticsearch.core.SuppressForbidden; +import org.xml.sax.helpers.DefaultHandler; import java.io.IOException; import java.net.DatagramPacket; @@ -46,6 +47,7 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; +import javax.xml.parsers.SAXParserFactory; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED; import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS; @@ -434,5 +436,12 @@ static void receiveDatagramSocket() throws IOException { } } + @EntitlementTest(expectedAccess = ALWAYS_DENIED) + static void javaXmlNetworkRequest() throws Exception { + // java.xml is part of the jdk, but not a system module. this checks it can't access the network + var saxParser = SAXParserFactory.newInstance().newSAXParser(); + saxParser.parse("http://127.0.0.1/foo.json", new DefaultHandler()); + } + private NetworkAccessCheckActions() {} } diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java index 01e1092f53d00..bdc4c92b404aa 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/HardcodedEntitlements.java @@ -21,6 +21,7 @@ import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.ManageThreadsEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement; +import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadJdkImageEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.SetHttpsConnectionPropertiesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.WriteSystemPropertiesEntitlement; @@ -114,6 +115,21 @@ private static List createServerEntitlements(Path pidFile) { ) ), new Scope("java.desktop", List.of(new LoadNativeLibrariesEntitlement())), + new Scope( + "java.xml", + List.of( + new ReadJdkImageEntitlement(), + // java.xml does some reflective stuff that reads calling jars, so allow reading the codebases + // of any code in the system so that they can all use java.xml + new FilesEntitlement( + List.of( + FilesEntitlement.FileData.ofBaseDirPath(LIB, READ), + FilesEntitlement.FileData.ofBaseDirPath(MODULES, READ), + FilesEntitlement.FileData.ofBaseDirPath(PLUGINS, READ) + ) + ) + ) + ), new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())), new Scope( "org.apache.lucene.core", diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java index acfdbb0caded7..e4dcd4758d544 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyCheckerImpl.java @@ -21,6 +21,7 @@ import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.ManageThreadsEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement; +import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadJdkImageEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.SetHttpsConnectionPropertiesEntitlement; import org.elasticsearch.entitlement.runtime.policy.entitlements.WriteSystemPropertiesEntitlement; @@ -490,6 +491,8 @@ public void checkEntitlementForUrl(Class callerClass, URL url) { if (jarFileUrl == null || handleNetworkOrFileUrlCheck(callerClass, jarFileUrl) == false) { checkUnsupportedURLProtocolConnection(callerClass, "jar with unsupported inner protocol"); } + } else if (isJrtUrl(url)) { + checkEntitlementPresent(callerClass, ReadJdkImageEntitlement.class); } else { checkUnsupportedURLProtocolConnection(callerClass, url.getProtocol()); } @@ -560,6 +563,10 @@ private static boolean isJarUrl(java.net.URL url) { return "jar".equals(url.getProtocol()); } + private static boolean isJrtUrl(java.net.URL url) { + return "jrt".equals(url.getProtocol()); + } + // We have to use class names for sun.net.www classes as java.base does not export them private static final List ADDITIONAL_NETWORK_URL_CONNECT_CLASS_NAMES = List.of( "sun.net.www.protocol.ftp.FtpURLConnection", diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java index df68da2e251ac..3c5948d7824b7 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java @@ -54,7 +54,7 @@ public class PolicyManager { */ static final Logger generalLogger = LogManager.getLogger(PolicyManager.class); - static final Set MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop"); + static final Set MODULES_EXCLUDED_FROM_SYSTEM_MODULES = Set.of("java.desktop", "java.xml"); /** * Identifies a particular entitlement {@link Scope} within a {@link Policy}. diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/ReadJdkImageEntitlement.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/ReadJdkImageEntitlement.java new file mode 100644 index 0000000000000..fa13203204c30 --- /dev/null +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/ReadJdkImageEntitlement.java @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +package org.elasticsearch.entitlement.runtime.policy.entitlements; + +/** + * Internal entitlement to read code from the jdk. + * + * Concretely this means the code can open jrt urls. Since the java + * runtime images (jrt) are read only, this implicitly only allows + * reading those urls. + */ +public class ReadJdkImageEntitlement implements Entitlement {}