This adds support for the Log4j URI conventions (such as the {@code classpath:} scheme) and subjects every * referenced resource to the {@code ALLOWED_PROTOCOLS} restrictions.
+ * + *Returning {@code null} for an unresolved resource is safe: the {@code commons-secure-xml} fallback resolver + * substitutes empty content instead of letting the parser fetch the resource itself.
*/ private static final class ConfigurationSourceResolver extends DefaultHandler2 implements LSResourceResolver { @@ -355,26 +335,20 @@ public InputSource resolveEntity( throws SAXException { try { final ConfigurationSource source = toConfigurationSource(systemId, baseURI); - final InputSource inputSource; if (source != null) { - inputSource = toInputSource(source); - } else { - inputSource = new InputSource(emptyReader()); - inputSource.setSystemId(systemId); + final InputSource inputSource = toInputSource(source); + inputSource.setPublicId(publicId); + return inputSource; } - inputSource.setPublicId(publicId); - return inputSource; } catch (final URISyntaxException e) { throw new SAXException(e); } + // Fallback to Commons XML ignore-all floor. + return null; } /** * Resolves a resource imported by an XML Schema ({@code xsd:import}/{@code xsd:include}). - * - *Returns an empty input when the resource cannot be resolved, instead of returning {@code null}: a - * {@code null} return would let the parser fall back to its own URL resolution, bypassing the - * {@code ALLOWED_PROTOCOLS} restrictions.
*/ @Override public LSInput resolveResource( @@ -385,25 +359,19 @@ public LSInput resolveResource( final String baseURI) { try { final ConfigurationSource source = toConfigurationSource(systemId, baseURI); - final LSInput input = domLs.createLSInput(); if (source != null) { + final LSInput input = domLs.createLSInput(); input.setByteStream(source.getInputStream()); input.setSystemId(source.getLocation()); - } else { - input.setCharacterStream(emptyReader()); - input.setSystemId(systemId); + input.setPublicId(publicId); } - input.setPublicId(publicId); - return input; } catch (final URISyntaxException e) { final LSException lsException = new LSException(LSException.PARSE_ERR, e.getMessage()); lsException.initCause(e); throw lsException; } - } - - private static Reader emptyReader() { - return new StringReader(""); + // Fallback to Commons XML ignore-all floor. + return null; } private static ConfigurationSource toConfigurationSource(final String systemId, final String baseURI) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java index 248ae965cec..974bd87602a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/package-info.java @@ -18,7 +18,7 @@ * Classes and interfaces supporting configuration of Log4j 2 with XML. */ @Export -@Version("2.26.0") +@Version("2.26.1") package org.apache.logging.log4j.core.config.xml; import org.osgi.annotation.bundle.Export; diff --git a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java index 06bb63611d2..d66b7765c4f 100644 --- a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java +++ b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java @@ -75,6 +75,10 @@ private Bundle getApiTestsBundle() throws BundleException { return installBundle("org.apache.logging.log4j.api.test"); } + private Bundle getSecureXmlBundle() throws BundleException { + return installBundle("org.apache.commons.xml.secure"); + } + /** * Tests starting, then stopping, then restarting, then stopping, and finally uninstalling the API and Core bundles */ @@ -82,19 +86,20 @@ private Bundle getApiTestsBundle() throws BundleException { public void testApiCoreStartStopStartStop() throws BundleException { final Bundle api = getApiBundle(); + final Bundle secureXml = getSecureXmlBundle(); final Bundle core = getCoreBundle(); assertEquals(Bundle.INSTALLED, api.getState(), "api is not in INSTALLED state"); assertEquals(Bundle.INSTALLED, core.getState(), "core is not in INSTALLED state"); // 1st start-stop - doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, core); - doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, api); + doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, secureXml, core); + doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, secureXml, api); // 2nd start-stop - doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, core); - doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, api); + doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, secureXml, core); + doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, secureXml, api); - doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, core, api); + doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, core, secureXml, api); } /** @@ -104,9 +109,10 @@ public void testApiCoreStartStopStartStop() throws BundleException { public void testClassNotFoundErrorLogger() throws BundleException { final Bundle api = getApiBundle(); + final Bundle secureXml = getSecureXmlBundle(); final Bundle core = getCoreBundle(); - doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api); + doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, secureXml); // fails if LOG4J2-1637 is not fixed try { core.start(); @@ -126,8 +132,8 @@ public void testClassNotFoundErrorLogger() throws BundleException { } assertEquals(Bundle.ACTIVE, core.getState(), String.format("`%s` bundle state mismatch", core)); - doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, api); - doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, core, api); + doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, secureXml, api); + doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, core, secureXml, api); } /** @@ -138,10 +144,11 @@ public void testClassNotFoundErrorLogger() throws BundleException { public void testLog4J12Fragement() throws BundleException, ReflectiveOperationException { final Bundle api = getApiBundle(); + final Bundle secureXml = getSecureXmlBundle(); final Bundle core = getCoreBundle(); final Bundle compat = get12ApiBundle(); - doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, core); + doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, secureXml, core); final Class> coreClassFromCore = core.loadClass("org.apache.logging.log4j.core.Core"); final Class> levelClassFrom12API = core.loadClass("org.apache.log4j.Level"); @@ -156,8 +163,8 @@ public void testLog4J12Fragement() throws BundleException, ReflectiveOperationEx levelClassFromAPI.getClassLoader(), "expected 1.2 API Level NOT to have the same class loader as API Level"); - doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, api); - doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, compat, core, api); + doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, core, secureXml, api); + doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, compat, core, secureXml, api); } /** @@ -166,13 +173,14 @@ public void testLog4J12Fragement() throws BundleException, ReflectiveOperationEx @Test public void testServiceLoader() throws BundleException, ReflectiveOperationException { final Bundle api = getApiBundle(); + final Bundle secureXml = getSecureXmlBundle(); final Bundle core = getCoreBundle(); final Bundle apiTests = getApiTestsBundle(); final Class> osgiServiceLocator = api.loadClass("org.apache.logging.log4j.util.OsgiServiceLocator"); assertTrue((boolean) osgiServiceLocator.getMethod("isAvailable").invoke(null), "OsgiServiceLocator is active"); - doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, core, apiTests); + doOnBundlesAndVerifyState(Bundle::start, Bundle.ACTIVE, api, secureXml, core, apiTests); final Class> osgiServiceLocatorTest = apiTests.loadClass("org.apache.logging.log4j.test.util.OsgiServiceLocatorTest"); @@ -187,8 +195,8 @@ public void testServiceLoader() throws BundleException, ReflectiveOperationExcep "org.apache.logging.log4j.core.impl.Log4jProvider", services.get(0).getClass().getName()); - doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, apiTests, core, api); - doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, apiTests, core, api); + doOnBundlesAndVerifyState(Bundle::stop, Bundle.RESOLVED, apiTests, core, secureXml, api); + doOnBundlesAndVerifyState(Bundle::uninstall, Bundle.UNINSTALLED, apiTests, core, secureXml, api); } private static void doOnBundlesAndVerifyState( diff --git a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CoreOsgiTest.java b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CoreOsgiTest.java index 493bb61c9e4..9dcc2acc09c 100644 --- a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CoreOsgiTest.java +++ b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/CoreOsgiTest.java @@ -46,6 +46,7 @@ public class CoreOsgiTest { public Option[] config() { return options( linkBundle("org.apache.logging.log4j.api"), + linkBundle("org.apache.commons.xml.secure"), linkBundle("org.apache.logging.log4j.core"), linkBundle("org.apache.logging.log4j.1.2.api").start(false), // required by Pax Exam's logging diff --git a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/DisruptorTest.java b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/DisruptorTest.java index 813331210c5..96bf98eb59d 100644 --- a/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/DisruptorTest.java +++ b/log4j-osgi-test/src/test/java/org/apache/logging/log4j/osgi/tests/DisruptorTest.java @@ -52,6 +52,7 @@ public class DisruptorTest { public Option[] config() { return options( linkBundle("org.apache.logging.log4j.api"), + linkBundle("org.apache.commons.xml.secure"), linkBundle("org.apache.logging.log4j.core"), linkBundle("com.lmax.disruptor"), // required by Pax Exam's logging diff --git a/log4j-parent/pom.xml b/log4j-parent/pom.xml index 28c371abefa..e5f8bd6594d 100644 --- a/log4j-parent/pom.xml +++ b/log4j-parent/pom.xml @@ -76,6 +76,7 @@