You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2021/12/11 23:06:11 UTC
[logging-log4j2] branch master updated: LOG4J2-3208 - Disable JNDI by default
This is an automated email from the ASF dual-hosted git repository.
rgoers pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
The following commit(s) were added to refs/heads/master by this push:
new 4456909 LOG4J2-3208 - Disable JNDI by default
4456909 is described below
commit 44569090f1cf1e92c711fb96dfd18cd7dccc72ea
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Sat Dec 11 16:05:58 2021 -0700
LOG4J2-3208 - Disable JNDI by default
---
.../logging/log4j/core/lookup/Interpolator.java | 19 +++++---
.../apache/logging/log4j/core/net/JndiManager.java | 52 +++++++++++++++-------
.../log4j/core/selector/JndiContextSelector.java | 6 +++
.../routing/RoutingAppenderWithJndiTest.java | 8 +++-
.../log4j/core/lookup/InterpolatorTest.java | 2 +
...LookupTest.java => JndiDisabledLookupTest.java} | 26 +++--------
.../logging/log4j/core/lookup/JndiLookupTest.java | 6 +++
.../core/lookup/JndiRestrictedLookupTest.java | 1 +
.../logging/log4j/jms/appender/JmsManager.java | 13 ++++--
.../log4j/jms/appender/JmsAppenderTest.java | 6 +++
src/changes/changes.xml | 5 ++-
src/site/asciidoc/manual/appenders.adoc | 4 ++
src/site/asciidoc/manual/configuration.adoc | 26 +++++++++++
src/site/asciidoc/manual/logsep.adoc | 4 ++
src/site/asciidoc/manual/lookups.adoc | 4 ++
src/site/asciidoc/security.adoc | 2 +-
16 files changed, 135 insertions(+), 49 deletions(-)
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
index cea22d6..ff10705 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
@@ -24,6 +24,7 @@ import java.util.Map;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.ConfigurationAware;
+import org.apache.logging.log4j.core.net.JndiManager;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.util.ReflectionUtil;
import org.apache.logging.log4j.plugins.util.PluginManager;
@@ -77,7 +78,9 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
try {
final Class<? extends StrLookup> clazz = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
- strLookupMap.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
+ if (!clazz.getName().equals(JndiLookup.class.getName()) || JndiManager.isIsJndiEnabled()) {
+ strLookupMap.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
+ }
} catch (final Throwable t) {
handleError(entry.getKey(), t);
}
@@ -107,12 +110,14 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
strLookupMap.put("lower", new LowerLookup());
strLookupMap.put("upper", new UpperLookup());
// JNDI
- try {
- // [LOG4J2-703] We might be on Android
- strLookupMap.put(LOOKUP_KEY_JNDI,
- Loader.newCheckedInstanceOf("org.apache.logging.log4j.core.lookup.JndiLookup", StrLookup.class));
- } catch (final LinkageError | Exception e) {
- handleError(LOOKUP_KEY_JNDI, e);
+ if (JndiManager.isIsJndiEnabled()) {
+ try {
+ // [LOG4J2-703] We might be on Android
+ strLookupMap.put(LOOKUP_KEY_JNDI,
+ Loader.newCheckedInstanceOf("org.apache.logging.log4j.core.lookup.JndiLookup", StrLookup.class));
+ } catch (final LinkageError | Exception e) {
+ handleError(LOOKUP_KEY_JNDI, e);
+ }
}
// JMX input args
try {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
index 9f524e5..718a203 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
@@ -73,6 +73,10 @@ public class JndiManager extends AbstractManager {
private final DirContext context;
+ public static boolean isIsJndiEnabled() {
+ return PropertiesUtil.getProperties().getBooleanProperty("log4j2.enableJndi", false);
+ }
+
private JndiManager(final String name, final DirContext context, final List<String> allowedHosts,
final List<String> allowedClasses, final List<String> allowedProtocols) {
super(null, name);
@@ -82,6 +86,14 @@ public class JndiManager extends AbstractManager {
this.allowedProtocols = allowedProtocols;
}
+ private JndiManager(final String name) {
+ super(null, name);
+ this.context = null;
+ this.allowedProtocols = null;
+ this.allowedClasses = null;
+ this.allowedHosts = null;
+ }
+
/**
* Gets the default JndiManager using the default {@link javax.naming.InitialContext}.
*
@@ -194,6 +206,9 @@ public class JndiManager extends AbstractManager {
@Override
protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) {
+ if (context != null) {
+ return JndiCloser.closeSilently(this.context);
+ }
return JndiCloser.closeSilently(this.context);
}
@@ -207,6 +222,9 @@ public class JndiManager extends AbstractManager {
*/
@SuppressWarnings("unchecked")
public synchronized <T> T lookup(final String name) throws NamingException {
+ if (context == null) {
+ return null;
+ }
try {
URI uri = new URI(name);
if (uri.getScheme() != null) {
@@ -262,21 +280,25 @@ public class JndiManager extends AbstractManager {
@Override
public JndiManager createManager(final String name, final Properties data) {
- String hosts = data != null ? data.getProperty(ALLOWED_HOSTS) : null;
- String classes = data != null ? data.getProperty(ALLOWED_CLASSES) : null;
- String protocols = data != null ? data.getProperty(ALLOWED_PROTOCOLS) : null;
- List<String> allowedHosts = new ArrayList<>();
- List<String> allowedClasses = new ArrayList<>();
- List<String> allowedProtocols = new ArrayList<>();
- addAll(hosts, allowedHosts, permanentAllowedHosts, ALLOWED_HOSTS, data);
- addAll(classes, allowedClasses, permanentAllowedClasses, ALLOWED_CLASSES, data);
- addAll(protocols, allowedProtocols, permanentAllowedProtocols, ALLOWED_PROTOCOLS, data);
- try {
- return new JndiManager(name, new InitialDirContext(data), allowedHosts, allowedClasses,
- allowedProtocols);
- } catch (final NamingException e) {
- LOGGER.error("Error creating JNDI InitialContext.", e);
- return null;
+ if (isIsJndiEnabled()) {
+ String hosts = data != null ? data.getProperty(ALLOWED_HOSTS) : null;
+ String classes = data != null ? data.getProperty(ALLOWED_CLASSES) : null;
+ String protocols = data != null ? data.getProperty(ALLOWED_PROTOCOLS) : null;
+ List<String> allowedHosts = new ArrayList<>();
+ List<String> allowedClasses = new ArrayList<>();
+ List<String> allowedProtocols = new ArrayList<>();
+ addAll(hosts, allowedHosts, permanentAllowedHosts, ALLOWED_HOSTS, data);
+ addAll(classes, allowedClasses, permanentAllowedClasses, ALLOWED_CLASSES, data);
+ addAll(protocols, allowedProtocols, permanentAllowedProtocols, ALLOWED_PROTOCOLS, data);
+ try {
+ return new JndiManager(name, new InitialDirContext(data), allowedHosts, allowedClasses,
+ allowedProtocols);
+ } catch (final NamingException e) {
+ LOGGER.error("Error creating JNDI InitialContext.", e);
+ return null;
+ }
+ } else {
+ return new JndiManager(name);
}
}
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
index fef98cb..0ee87e1 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
@@ -93,6 +93,12 @@ public class JndiContextSelector implements NamedContextSelector {
private static final StatusLogger LOGGER = StatusLogger.getLogger();
+ public JndiContextSelector() {
+ if (!JndiManager.isIsJndiEnabled()) {
+ throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndi=true");
+ }
+ }
+
@Override
public void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext, final boolean allContexts) {
LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
index 9773c36..8d8b305 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.appender.routing;
import java.io.File;
import java.util.Collections;
+import java.util.Map;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
@@ -47,9 +48,14 @@ public class RoutingAppenderWithJndiTest {
public static LoggerContextRule loggerContextRule = new LoggerContextRule("log4j-routing-by-jndi.xml");
@ClassRule
- public static RuleChain rules = RuleChain.outerRule(new JndiRule(Collections.<String, Object>emptyMap()))
+ public static RuleChain rules = RuleChain.outerRule(new JndiRule(initBindings()))
.around(loggerContextRule);
+ private static Map<String, Object> initBindings() {
+ System.setProperty("log4j2.enableJndi", "true");
+ return Collections.emptyMap();
+ }
+
@Before
public void before() throws NamingException {
listAppender1 = RoutingAppenderWithJndiTest.loggerContextRule.getListAppender("List1");
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
index 2cf7c19..3f5de41 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
@@ -48,12 +48,14 @@ public class InterpolatorTest {
protected void before() throws Throwable {
System.setProperty(TESTKEY, TESTVAL);
System.setProperty(TESTKEY2, TESTVAL);
+ System.setProperty("log4j2.enableJndi", "true");
}
@Override
protected void after() {
System.clearProperty(TESTKEY);
System.clearProperty(TESTKEY2);
+ System.clearProperty("log4j2.enableJndi");
}
}).around(new JndiRule(
JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME));
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
similarity index 71%
copy from log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
copy to log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
index 35b6c26..365d7d8 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
@@ -25,12 +25,14 @@ import org.apache.logging.log4j.core.test.junit.JndiRule;
import org.junit.Rule;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNull;
/**
- * JndiLookupTest
+ * JndiDisabledLookupTest
+ *
+ * Verifies the Lookups are disabled without the log4j2.enableJndi property set to true.
*/
-public class JndiLookupTest {
+public class JndiDisabledLookupTest {
private static final String TEST_CONTEXT_RESOURCE_NAME = "logging/context-name";
private static final String TEST_CONTEXT_NAME = "app-1";
@@ -55,22 +57,6 @@ public class JndiLookupTest {
final StrLookup lookup = new JndiLookup();
String contextName = lookup.lookup(TEST_CONTEXT_RESOURCE_NAME);
- assertEquals(TEST_CONTEXT_NAME, contextName);
-
- contextName = lookup.lookup(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME);
- assertEquals(TEST_CONTEXT_NAME, contextName);
-
- final String nonExistingResource = lookup.lookup("logging/non-existing-resource");
- assertNull(nonExistingResource);
- }
-
- @Test
- public void testNonStringLookup() throws Exception {
- // LOG4J2-1310
- final StrLookup lookup = new JndiLookup();
- final String integralValue = lookup.lookup(TEST_INTEGRAL_NAME);
- assertEquals(String.valueOf(TEST_INTEGRAL_VALUE), integralValue);
- final String collectionValue = lookup.lookup(TEST_STRINGS_NAME);
- assertEquals(String.valueOf(TEST_STRINGS_COLLECTION), collectionValue);
+ assertNull(contextName);
}
}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
index 35b6c26..bc81613 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.core.test.junit.JndiRule;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -42,6 +43,11 @@ public class JndiLookupTest {
@Rule
public JndiRule jndiRule = new JndiRule(createBindings());
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty("log4j2.enableJndi", "true");
+ }
+
private Map<String, Object> createBindings() {
final Map<String, Object> map = new HashMap<>();
map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java
index b0023b2..2765afe 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java
@@ -54,6 +54,7 @@ public class JndiRestrictedLookupTest {
public static void beforeClass() {
System.setProperty("log4j2.allowedLdapClasses", Level.class.getName());
System.setProperty("log4j2.allowedJndiProtocols", "dns");
+ System.setProperty("log4j2.enableJndi", "true");
}
@Test
diff --git a/log4j-jms/src/main/java/org/apache/logging/log4j/jms/appender/JmsManager.java b/log4j-jms/src/main/java/org/apache/logging/log4j/jms/appender/JmsManager.java
index 291fd7d..dae562d 100644
--- a/log4j-jms/src/main/java/org/apache/logging/log4j/jms/appender/JmsManager.java
+++ b/log4j-jms/src/main/java/org/apache/logging/log4j/jms/appender/JmsManager.java
@@ -125,10 +125,15 @@ public class JmsManager extends AbstractManager {
@Override
public JmsManager createManager(final String name, final JmsManagerConfiguration data) {
- try {
- return new JmsManager(name, data);
- } catch (final Exception e) {
- logger().error("Error creating JmsManager using JmsManagerConfiguration [{}]", data, e);
+ if (JndiManager.isIsJndiEnabled()) {
+ try {
+ return new JmsManager(name, data);
+ } catch (final Exception e) {
+ logger().error("Error creating JmsManager using JmsManagerConfiguration [{}]", data, e);
+ return null;
+ }
+ } else {
+ logger().error("JNDI has not been enabled. The log4j2.enableJndi property must be set to true");
return null;
}
}
diff --git a/log4j-jms/src/test/java/org/apache/logging/log4j/jms/appender/JmsAppenderTest.java b/log4j-jms/src/test/java/org/apache/logging/log4j/jms/appender/JmsAppenderTest.java
index 05a56ad..a878640 100644
--- a/log4j-jms/src/test/java/org/apache/logging/log4j/jms/appender/JmsAppenderTest.java
+++ b/log4j-jms/src/test/java/org/apache/logging/log4j/jms/appender/JmsAppenderTest.java
@@ -49,6 +49,7 @@ import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.SimpleMessage;
import org.apache.logging.log4j.message.StringMapMessage;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@@ -83,6 +84,11 @@ public class JmsAppenderTest {
@Rule
public RuleChain rules = RuleChain.outerRule(jndiRule).around(ctx);
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ System.setProperty("log4j2.enableJndi", "true");
+ }
+
public JmsAppenderTest() throws Exception {
// this needs to set up before LoggerContextRule
given(connectionFactory.createConnection()).willReturn(connection);
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ef63c65..3327e35 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -168,7 +168,10 @@
Fixes incorrect constructor call in LocalizedMessageFactory.
</action>
</release>
- <release version="2.15.1" date="2021-12-XX" description="GA Release 2.15.0">
+ <release version="2.15.1" date="2021-12-XX" description="GA Release 2.15.1">
+ <action issue="LOG4J2-3208" dev="rgoers" type="fix">
+ Disable JNDI by default. Require log4j2.enableJndi to be set to true to allow JNDI.
+ </action>
</release>
<release version="2.15.0" date="2021-12-06" description="GA Release 2.15.0">
<!-- ADDS -->
diff --git a/src/site/asciidoc/manual/appenders.adoc b/src/site/asciidoc/manual/appenders.adoc
index 4b20bb3..c657537 100644
--- a/src/site/asciidoc/manual/appenders.adoc
+++ b/src/site/asciidoc/manual/appenders.adoc
@@ -1259,6 +1259,10 @@ As of Log4j 2.11.0, JPA support has moved from the existing module
The JMS Appender sends the formatted log event to a JMS Destination.
+The JMS Appender requires JNDI support so as of release 2.15.1 this appender will not function unless
+`log4j2.enableJndi=true` is configured as a system property or environment variable. See the
+link:./configuration.html#enableJndi[log4j2.enableJndi] system property.
+
Note that in Log4j 2.0, this appender was split into a JMSQueueAppender
and a JMSTopicAppender. Starting in Log4j 2.1, these appenders were
combined into the JMS Appender which makes no distinction between queues
diff --git a/src/site/asciidoc/manual/configuration.adoc b/src/site/asciidoc/manual/configuration.adoc
index 4ec763a..7f513d3 100644
--- a/src/site/asciidoc/manual/configuration.adoc
+++ b/src/site/asciidoc/manual/configuration.adoc
@@ -2073,6 +2073,32 @@ context class loader before falling back to the default class loader.
|If `true`, classes and configuration are only loaded with the default context class loader.
Otherwise, log4j also uses the log4j classloader, parent classloaders and the system classloader.
+|[[enableJndi]]log4j2.enableJndi +
+([[log4j.enableJndi]]log4j.enableJndi)
+|LOG4J_ENABLE_JNDI
+|false
+| When true, Log4j components that use JNDI are enabled. When false, the default, they are disabled.
+
+|[[allowedLdapClasses]]log4j2.allowedLdapClasses +
+([[log4j.allowedLdapClasses]]log4j.allowedLdapClasses)
+|LOG4J_ALLOWED_LDAP_CLASSES
+|
+| System property that specifies fully qualified class names that may be accessed by LDAP. The classes
+must implement Serializable. By default only Java primative classes are allowed.
+
+|[[allowedLdapHosts]]log4j2.allowedLdapHosts +
+([[log4j.allowedLdapHosts]]log4j.allowedLdapHosts)
+|LOG4J_ALLOWED_LDAP_HOSTS
+|
+| System property that adds host names or ip addresses that may be access by LDAP. By default it only allows
+the local host names and ip addresses.
+
+|[[allowedJndiProtocols]]log4j2.allowedJndiProtocols +
+([[log4j.allowedJndiProtocols]]log4j.allowedJndiProtocols)
+|LOG4J_ALLOWED_JNDI_PROTOCOLS
+|
+| System property that adds protocol names that JNDI will allow. By default it only allows java, ldap, and ldaps.
+
|[[uuidSequence]]log4j2.uuidSequence +
([[org.apache.logging.log4j.uuidSequence]]org.apache.logging.log4j.uuidSequence)
|LOG4J_UUID_SEQUENCE
diff --git a/src/site/asciidoc/manual/logsep.adoc b/src/site/asciidoc/manual/logsep.adoc
index ae52bc3..94c1687 100644
--- a/src/site/asciidoc/manual/logsep.adoc
+++ b/src/site/asciidoc/manual/logsep.adoc
@@ -116,6 +116,10 @@ cause the container to use JNDI to locate each web application's
context parameter to true and also set the `log4jContextName` and
`log4jConfiguration` context parameters.
+The `JndiContextSelector` will not work unless `log4j2.enableJndi=true` is set as a system property
+or environment variable. See the
+link:./configuration.html#enableJndi[log4j2.enableJndi] system property.
+
The exact method for setting system properties depends on the container.
For Tomcat, edit `$CATALINA_HOME/conf/catalina.properties`. Consult the
documentation for other web containers.
diff --git a/src/site/asciidoc/manual/lookups.adoc b/src/site/asciidoc/manual/lookups.adoc
index 986ac65..8408943 100644
--- a/src/site/asciidoc/manual/lookups.adoc
+++ b/src/site/asciidoc/manual/lookups.adoc
@@ -267,6 +267,10 @@ For example:
[#JndiLookup]
== JNDI Lookup
+As of Log4j 2.15.1 JNDI operations require that `log4j2.enableJndi=tru`e be set as a system property or the
+corresponding environment variable for this lookup to function. See the
+link:./configuration.html#enableJndi[log4j2.enableJndi] system property.
+
The JndiLookup allows variables to be retrieved via JNDI. By default the
key will be prefixed with java:comp/env/, however if the key contains a
":" no prefix will be added.
diff --git a/src/site/asciidoc/security.adoc b/src/site/asciidoc/security.adoc
index 7a6d022..1276615 100644
--- a/src/site/asciidoc/security.adoc
+++ b/src/site/asciidoc/security.adoc
@@ -63,7 +63,7 @@ substitution is enabled. From log4j 2.15.0, this behavior has been disabled by d
Mitigation: In releases >= 2.10, this behavior can be mitigated by setting either the system property
`log4j2.formatMsgNoLookups` or the environment variable `LOG4J_FORMAT_MSG_NO_LOOKUPS` to `true`.
For releases from 2.7 through 2.14.1 all PatternLayout patterns can be modified to specify the message converter as
-`%m{nnolookups}` instead of just `%m`.
+`%m{nolookups}` instead of just `%m`.
For releases from 2.0-beta9 to 2.7, the only mitigation is to remove the `JndiLookup` class from the classpath:
`zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class`.