This is an automated email from the ASF dual-hosted git repository.

ppkarwasz pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 4ce9212cd3 resolve external entities through ConfigurationSource in 
log4j 1.x XmlConfiguration (#4198)
4ce9212cd3 is described below

commit 4ce9212cd3ac5e1ebf4b0fdfe08dda3f09ee9a6b
Author: jmestwa-coder <[email protected]>
AuthorDate: Sun Aug 30 14:13:17 2026 +0530

    resolve external entities through ConfigurationSource in log4j 1.x 
XmlConfiguration (#4198)
    
    * disable external entity resolution in log4j 1.x XmlConfiguration
    
    * constrain external resolution to the bundled log4j.dtd in 
Log4jEntityResolver
    
    * resolve external entities through ConfigurationSource in 
Log4jEntityResolver
---
 .../org/apache/log4j/xml/Log4jEntityResolver.java  | 28 +++++-
 .../config/XmlConfigurationExternalEntityTest.java | 99 ++++++++++++++++++++++
 .../.2.x.x/fix_log4j1_xml_configuration_xxe.xml    | 12 +++
 3 files changed, 136 insertions(+), 3 deletions(-)

diff --git 
a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java 
b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
index 1533fc8938..6eafb999cc 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -18,7 +18,10 @@ package org.apache.log4j.xml;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.Constants;
 import org.xml.sax.EntityResolver;
@@ -27,7 +30,11 @@ import org.xml.sax.InputSource;
 /**
  * An {@link EntityResolver} specifically designed to return
  * <code>log4j.dtd</code> which is embedded within the log4j jar
- * file.
+ * file. Any other external resource is resolved through
+ * {@link ConfigurationSource#fromUri(URI)}, the same way the configuration
+ * file itself is resolved, so it is subject to the same restrictions (e.g.
+ * the {@code log4j2.Configuration.allowedProtocols} property). Resources
+ * that cannot be resolved this way are replaced by an empty source.
  */
 public class Log4jEntityResolver implements EntityResolver {
     private static final Logger LOGGER = StatusLogger.getLogger();
@@ -35,7 +42,7 @@ public class Log4jEntityResolver implements EntityResolver {
 
     @Override
     public InputSource resolveEntity(final String publicId, final String 
systemId) {
-        if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+        if ((systemId != null && systemId.endsWith("log4j.dtd")) || 
PUBLIC_ID.equals(publicId)) {
             final Class<?> clazz = getClass();
             InputStream in = 
clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
             if (in == null) {
@@ -46,6 +53,21 @@ public class Log4jEntityResolver implements EntityResolver {
             }
             return new InputSource(in);
         }
-        return null;
+        // Resolve using `ConfigurationSource`
+        if (systemId != null) {
+            try {
+                final ConfigurationSource source = 
ConfigurationSource.fromUri(new URI(systemId));
+                if (source != null) {
+                    final InputSource inputSource = new 
InputSource(source.getInputStream());
+                    inputSource.setSystemId(systemId);
+                    return inputSource;
+                }
+            } catch (final URISyntaxException e) {
+                LOGGER.warn("Resolution error: invalid URI {}", systemId, e);
+            }
+        }
+        // Fall back to empty resource
+        LOGGER.warn("Resolution error: unable to resolve {}", systemId);
+        return new InputSource(new 
ByteArrayInputStream(Constants.EMPTY_BYTE_ARRAY));
     }
 }
diff --git 
a/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationExternalEntityTest.java
 
b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationExternalEntityTest.java
new file mode 100644
index 0000000000..6b137bef0b
--- /dev/null
+++ 
b/log4j-1.2-api/src/test/java/org/apache/log4j/config/XmlConfigurationExternalEntityTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.config;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.apache.log4j.xml.XmlConfigurationFactory;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.test.junit.SetTestProperty;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Checks that the Log4j 1.x XML configuration reader resolves external 
entities through
+ * {@link ConfigurationSource}, so they are subject to the {@code 
log4j2.Configuration.allowedProtocols} restrictions.
+ */
+class XmlConfigurationExternalEntityTest {
+
+    private static final String INJECTED_LOGGER = "external-entity-injected";
+
+    /**
+     * A {@code file} entity is allowed by default and is still resolved.
+     */
+    @Test
+    void resolvesAllowedExternalEntities(@TempDir final Path tempDir) throws 
Exception {
+        final Path injected = tempDir.resolve("injected.xml");
+        Files.write(injected, ("<logger name=\"" + INJECTED_LOGGER + 
"\"></logger>").getBytes(StandardCharsets.UTF_8));
+
+        final Configuration configuration = configure(tempDir, 
injected.toUri().toString());
+
+        assertTrue(
+                configuration.getLoggers().containsKey(INJECTED_LOGGER),
+                "External entity was not resolved; allowed protocols must 
still be resolved");
+    }
+
+    /**
+     * An entity fetched over a protocol that is not allowed is replaced by an 
empty source. The {@code @Timeout}
+     * guards against the resolver reaching the network instead of rejecting 
the protocol.
+     */
+    @Test
+    @Timeout(5)
+    @SetTestProperty(key = "log4j2.configurationAllowedProtocols", value = 
"file")
+    void ignoresDisallowedExternalEntities(@TempDir final Path tempDir) throws 
Exception {
+        final Configuration configuration = configure(tempDir, 
"http://localhost:1/injected.xml";);
+
+        assertFalse(
+                configuration.getLoggers().containsKey(INJECTED_LOGGER),
+                "External entity was resolved; disallowed protocols must not 
be resolved");
+    }
+
+    private static Configuration configure(final Path tempDir, final String 
entitySystemId) throws Exception {
+        // If the external entity is resolved, its replacement text injects a 
logger into the configuration.
+        final Path configFile = tempDir.resolve("log4j1-external-entity.xml");
+        final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                + "<!DOCTYPE log4j:configuration SYSTEM \"log4j.dtd\" [\n"
+                + "  <!ENTITY injected SYSTEM \"" + entitySystemId + "\">\n"
+                + "]>\n"
+                + "<log4j:configuration 
xmlns:log4j=\"http://jakarta.apache.org/log4j/\";>\n"
+                + "  <appender name=\"console\" 
class=\"org.apache.log4j.ConsoleAppender\">\n"
+                + "    <layout class=\"org.apache.log4j.SimpleLayout\"/>\n"
+                + "  </appender>\n"
+                + "  &injected;\n"
+                + "  <root>\n"
+                + "    <priority value=\"debug\"/>\n"
+                + "    <appender-ref ref=\"console\"/>\n"
+                + "  </root>\n"
+                + "</log4j:configuration>\n";
+        Files.write(configFile, xml.getBytes(StandardCharsets.UTF_8));
+
+        final ConfigurationSource source = new 
ConfigurationSource(Files.newInputStream(configFile), configFile);
+        final LoggerContext context = LoggerContext.getContext(false);
+        final Configuration configuration = new 
XmlConfigurationFactory().getConfiguration(context, source);
+        assertNotNull(configuration, "No configuration created");
+        configuration.initialize();
+        return configuration;
+    }
+}
diff --git a/src/changelog/.2.x.x/fix_log4j1_xml_configuration_xxe.xml 
b/src/changelog/.2.x.x/fix_log4j1_xml_configuration_xxe.xml
new file mode 100644
index 0000000000..d2bd5d1d12
--- /dev/null
+++ b/src/changelog/.2.x.x/fix_log4j1_xml_configuration_xxe.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<entry xmlns="https://logging.apache.org/xml/ns";
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xsi:schemaLocation="
+           https://logging.apache.org/xml/ns
+           https://logging.apache.org/xml/ns/log4j-changelog-0.xsd";
+       type="fixed">
+  <issue id="4198" link="https://github.com/apache/logging-log4j2/pull/4198"/>
+  <description format="asciidoc">
+    Resolve external entities in the Log4j 1.x `XmlConfiguration` reader 
through `ConfigurationSource`, so they are subject to the 
`log4j2.Configuration.allowedProtocols` restrictions
+  </description>
+</entry>

Reply via email to