From 1614226c683e8996440f7f7462301b80ef7e33ce Mon Sep 17 00:00:00 2001
From: Tamas Cservenak <tamas@cservenak.net>
Date: Sat, 16 Nov 2024 14:55:43 +0100
Subject: [PATCH] [MNG-8379] Decrypt all of settings on building it (#1913)

The decryption should happen transparently, also, resolver should not decrypt for itself only, whole maven should have access to all.

This PR also disables Maven 3.0 IT MNG-4459 as Maven4 stops with this "security through obscurity" (keep encrypted pw in memory kinda for "security reasons" but in reality any mojo or extension can decrypt anything they want).

---

https://issues.apache.org/jira/browse/MNG-8379
---
 .../crypto/DefaultSettingsDecrypter.java      |   2 +-
 .../invoker/mvnenc/ConsolePasswordPrompt.java |   7 +-
 ...DefaultRepositorySystemSessionFactory.java |  28 +----
 ...ultRepositorySystemSessionFactoryTest.java |  14 ---
 impl/maven-impl/pom.xml                       |   4 +
 .../internal/impl/DefaultSettingsBuilder.java |  51 ++++++++-
 .../secdispatcher/SecDispatcherProvider.java  | 106 ++++++++++++++++++
 ...4459InMemorySettingsKeptEncryptedTest.java |   2 +-
 .../it/MavenITmng8379SettingsDecryptTest.java |  78 +++++++++++++
 .../apache/maven/it/TestSuiteOrdering.java    |   1 +
 .../mng-8379-decrypt-settings/README.md       |   9 ++
 .../home/.m2/settings-security4.xml           |  20 ++++
 .../home/.m2/settings.xml                     |  38 +++++++
 .../legacyhome/.m2/settings-security.xml      |   4 +
 .../legacyhome/.m2/settings.xml               |  38 +++++++
 pom.xml                                       |   2 +-
 16 files changed, 355 insertions(+), 49 deletions(-)
 create mode 100644 impl/maven-impl/src/main/java/org/apache/maven/internal/impl/secdispatcher/SecDispatcherProvider.java
 create mode 100644 its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java
 create mode 100644 its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/README.md
 create mode 100644 its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings-security4.xml
 create mode 100644 its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings.xml
 create mode 100644 its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings-security.xml
 create mode 100644 its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings.xml

diff --git a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/crypto/DefaultSettingsDecrypter.java b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/crypto/DefaultSettingsDecrypter.java
index f427a95b74..26bd1c5524 100644
--- a/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/crypto/DefaultSettingsDecrypter.java
+++ b/compat/maven-settings-builder/src/main/java/org/apache/maven/settings/crypto/DefaultSettingsDecrypter.java
@@ -46,7 +46,7 @@ public class DefaultSettingsDecrypter implements SettingsDecrypter {
     private final SecDispatcher securityDispatcher;
 
     @Inject
-    public DefaultSettingsDecrypter(SecDispatcher securityDispatcher) {
+    public DefaultSettingsDecrypter(MavenSecDispatcher securityDispatcher) {
         this.securityDispatcher = securityDispatcher;
     }
 
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/ConsolePasswordPrompt.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/ConsolePasswordPrompt.java
index a3981a7897..95a8cab0a5 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/ConsolePasswordPrompt.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvnenc/ConsolePasswordPrompt.java
@@ -18,14 +18,13 @@
  */
 package org.apache.maven.cling.invoker.mvnenc;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import org.apache.maven.api.di.Inject;
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Singleton;
 import org.apache.maven.api.services.Prompter;
 import org.apache.maven.api.services.PrompterException;
 import org.codehaus.plexus.components.secdispatcher.MasterSource;
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
index 65acf7ebad..b44a172da5 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
@@ -47,10 +47,6 @@ import org.apache.maven.rtinfo.RuntimeInformation;
 import org.apache.maven.settings.Mirror;
 import org.apache.maven.settings.Proxy;
 import org.apache.maven.settings.Server;
-import org.apache.maven.settings.building.SettingsProblem;
-import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
-import org.apache.maven.settings.crypto.SettingsDecrypter;
-import org.apache.maven.settings.crypto.SettingsDecryptionResult;
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 import org.eclipse.aether.ConfigurationProperties;
 import org.eclipse.aether.RepositoryListener;
@@ -123,8 +119,6 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
 
     private final RepositorySystem repoSystem;
 
-    private final SettingsDecrypter settingsDecrypter;
-
     private final EventSpyDispatcher eventSpyDispatcher;
 
     private final RuntimeInformation runtimeInformation;
@@ -141,7 +135,6 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
     @Inject
     DefaultRepositorySystemSessionFactory(
             RepositorySystem repoSystem,
-            SettingsDecrypter settingsDecrypter,
             EventSpyDispatcher eventSpyDispatcher,
             RuntimeInformation runtimeInformation,
             TypeRegistry typeRegistry,
@@ -149,7 +142,6 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
             Map<String, MavenExecutionRequestExtender> requestExtenders,
             Map<String, RepositorySystemSessionExtender> sessionExtenders) {
         this.repoSystem = repoSystem;
-        this.settingsDecrypter = settingsDecrypter;
         this.eventSpyDispatcher = eventSpyDispatcher;
         this.runtimeInformation = runtimeInformation;
         this.typeRegistry = typeRegistry;
@@ -211,22 +203,6 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
             sessionBuilder.setVersionFilter(versionFilter);
         }
 
-        DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
-        decrypt.setProxies(request.getProxies());
-        decrypt.setServers(request.getServers());
-        SettingsDecryptionResult decrypted = settingsDecrypter.decrypt(decrypt);
-        for (SettingsProblem problem : decrypted.getProblems()) {
-            if (problem.getSeverity() == SettingsProblem.Severity.WARNING) {
-                logger.warn(problem.getMessage());
-            } else if (problem.getSeverity() == SettingsProblem.Severity.ERROR) {
-                logger.error(
-                        problem.getMessage(),
-                        request.isShowErrors()
-                                ? problem.getException()
-                                : problem.getException().getMessage());
-            }
-        }
-
         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
         for (Mirror mirror : request.getMirrors()) {
             mirrorSelector.add(
@@ -241,7 +217,7 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
         sessionBuilder.setMirrorSelector(mirrorSelector);
 
         DefaultProxySelector proxySelector = new DefaultProxySelector();
-        for (Proxy proxy : decrypted.getProxies()) {
+        for (Proxy proxy : request.getProxies()) {
             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
             authBuilder.addUsername(proxy.getUsername()).addPassword(proxy.getPassword());
             proxySelector.add(
@@ -254,7 +230,7 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
         // Note: we do NOT use WagonTransportConfigurationKeys here as Maven Core does NOT depend on Wagon Transport
         // and this is okay and "good thing".
         DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
-        for (Server server : decrypted.getServers()) {
+        for (Server server : request.getServers()) {
             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
             authBuilder.addUsername(server.getUsername()).addPassword(server.getPassword());
             authBuilder.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactoryTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactoryTest.java
index afccfcabec..1c418322aa 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactoryTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactoryTest.java
@@ -36,7 +36,6 @@ import org.apache.maven.execution.MavenExecutionRequest;
 import org.apache.maven.internal.impl.DefaultTypeRegistry;
 import org.apache.maven.rtinfo.RuntimeInformation;
 import org.apache.maven.settings.Server;
-import org.apache.maven.settings.crypto.SettingsDecrypter;
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 import org.codehaus.plexus.testing.PlexusTest;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
@@ -69,9 +68,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
     @Inject
     protected EventSpyDispatcher eventSpyDispatcher;
 
-    @Inject
-    protected SettingsDecrypter settingsDecrypter;
-
     @Inject
     protected org.eclipse.aether.RepositorySystem aetherRepositorySystem;
 
@@ -88,7 +84,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
     void isNoSnapshotUpdatesTest() throws InvalidRepositoryException {
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -112,7 +107,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
     void isSnapshotUpdatesTest() throws InvalidRepositoryException {
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -148,7 +142,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -192,7 +185,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -230,7 +222,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -272,7 +263,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -308,7 +298,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -350,7 +339,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
 
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -369,7 +357,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
     void transportConfigurationTest() throws InvalidRepositoryException {
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
@@ -416,7 +403,6 @@ public class DefaultRepositorySystemSessionFactoryTest {
     void versionFilteringTest() throws InvalidRepositoryException {
         DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
                 aetherRepositorySystem,
-                settingsDecrypter,
                 eventSpyDispatcher,
                 information,
                 defaultTypeRegistry,
diff --git a/impl/maven-impl/pom.xml b/impl/maven-impl/pom.xml
index 94f656c74d..6f1ea9a4cc 100644
--- a/impl/maven-impl/pom.xml
+++ b/impl/maven-impl/pom.xml
@@ -109,6 +109,10 @@ under the License.
       <groupId>org.apache.maven.resolver</groupId>
       <artifactId>maven-resolver-impl</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-sec-dispatcher</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.junit.jupiter</groupId>
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java
index 1dc9635cdb..9f994856a4 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java
@@ -51,6 +51,7 @@ import org.apache.maven.api.settings.Settings;
 import org.apache.maven.internal.impl.model.DefaultInterpolator;
 import org.apache.maven.settings.v4.SettingsMerger;
 import org.apache.maven.settings.v4.SettingsTransformer;
+import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
 
 /**
  * Builds the effective settings from a user settings file and/or a global settings file.
@@ -65,13 +66,23 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
 
     private final Interpolator interpolator;
 
+    private final SecDispatcher secDispatcher;
+
+    /**
+     * This ctor is used in legacy components, and when in legacy, {@link SecDispatcher} is {@code null} and
+     * Maven3 exposes decryption with other means.
+     */
     public DefaultSettingsBuilder() {
-        this(new DefaultInterpolator());
+        this(new DefaultInterpolator(), null);
     }
 
+    /**
+     * In Maven4 the {@link SecDispatcher} is injected and build settings are fully decrypted as well.
+     */
     @Inject
-    public DefaultSettingsBuilder(Interpolator interpolator) {
+    public DefaultSettingsBuilder(Interpolator interpolator, SecDispatcher secDispatcher) {
         this.interpolator = interpolator;
+        this.secDispatcher = secDispatcher;
     }
 
     @Override
@@ -198,6 +209,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
         }
 
         settings = interpolate(settings, request, problems);
+        settings = decrypt(settingsSource, settings, request, problems);
 
         settingsValidator.validate(settings, isProjectSettings, problems);
 
@@ -237,6 +249,41 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
                 .visit(settings);
     }
 
+    private Settings decrypt(
+            Source settingsSource, Settings settings, SettingsBuilderRequest request, List<BuilderProblem> problems) {
+        if (secDispatcher == null) {
+            return settings;
+        }
+        Function<String, String> decryptFunction = str -> {
+            if (secDispatcher.isAnyEncryptedString(str)) {
+                if (secDispatcher.isLegacyEncryptedString(str)) {
+                    // add a problem
+                    problems.add(new DefaultBuilderProblem(
+                            settingsSource.getLocation(),
+                            -1,
+                            -1,
+                            null,
+                            "Pre-Maven 4 legacy encrypted password detected "
+                                    + " - configure password encryption with the help of mvnenc to be compatible with Maven 4.",
+                            BuilderProblem.Severity.WARNING));
+                }
+                try {
+                    return secDispatcher.decrypt(str);
+                } catch (Exception e) {
+                    problems.add(new DefaultBuilderProblem(
+                            settingsSource.getLocation(),
+                            -1,
+                            -1,
+                            e,
+                            "Could not decrypt password (fix the corrupted password or remove it, if unused) " + str,
+                            BuilderProblem.Severity.ERROR));
+                }
+            }
+            return str;
+        };
+        return new SettingsTransformer(decryptFunction).visit(settings);
+    }
+
     @Override
     public List<BuilderProblem> validate(Settings settings, boolean isProjectSettings) {
         ArrayList<BuilderProblem> problems = new ArrayList<>();
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/secdispatcher/SecDispatcherProvider.java b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/secdispatcher/SecDispatcherProvider.java
new file mode 100644
index 0000000000..76e8ba900f
--- /dev/null
+++ b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/secdispatcher/SecDispatcherProvider.java
@@ -0,0 +1,106 @@
+/*
+ * 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.maven.internal.impl.secdispatcher;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.di.Named;
+import org.apache.maven.api.di.Provides;
+import org.codehaus.plexus.components.secdispatcher.Cipher;
+import org.codehaus.plexus.components.secdispatcher.Dispatcher;
+import org.codehaus.plexus.components.secdispatcher.MasterSource;
+import org.codehaus.plexus.components.secdispatcher.SecDispatcher;
+import org.codehaus.plexus.components.secdispatcher.internal.DefaultSecDispatcher;
+import org.codehaus.plexus.components.secdispatcher.internal.cipher.AESGCMNoPadding;
+import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher;
+import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.MasterDispatcher;
+import org.codehaus.plexus.components.secdispatcher.internal.sources.EnvMasterSource;
+import org.codehaus.plexus.components.secdispatcher.internal.sources.GpgAgentMasterSource;
+import org.codehaus.plexus.components.secdispatcher.internal.sources.PinEntryMasterSource;
+import org.codehaus.plexus.components.secdispatcher.internal.sources.SystemPropertyMasterSource;
+
+/**
+ * Delegate that offers just the minimal surface needed to decrypt settings.
+ */
+@SuppressWarnings("unused")
+@Named
+public class SecDispatcherProvider {
+
+    private static final String FILE_NAME = "settings-security4.xml";
+
+    @Provides
+    public static SecDispatcher secDispatcher(Map<String, Dispatcher> dispatchers) {
+        return new DefaultSecDispatcher(dispatchers, configurationFile());
+    }
+
+    @Provides
+    @Named(LegacyDispatcher.NAME)
+    public static Dispatcher legacyDispatcher() {
+        return new LegacyDispatcher();
+    }
+
+    @Provides
+    @Named(MasterDispatcher.NAME)
+    public static Dispatcher masterDispatcher(
+            Map<String, Cipher> masterCiphers, Map<String, MasterSource> masterSources) {
+        return new MasterDispatcher(masterCiphers, masterSources);
+    }
+
+    @Provides
+    @Named(AESGCMNoPadding.CIPHER_ALG)
+    public static Cipher aesGcmNoPaddingCipher() {
+        return new AESGCMNoPadding();
+    }
+
+    @Provides
+    @Named(EnvMasterSource.NAME)
+    public static MasterSource envMasterSource() {
+        return new EnvMasterSource();
+    }
+
+    @Provides
+    @Named(GpgAgentMasterSource.NAME)
+    public static MasterSource gpgAgentMasterSource() {
+        return new GpgAgentMasterSource();
+    }
+
+    @Provides
+    @Named(PinEntryMasterSource.NAME)
+    public static MasterSource pinEntryMasterSource() {
+        return new PinEntryMasterSource();
+    }
+
+    @Provides
+    @Named(SystemPropertyMasterSource.NAME)
+    public static MasterSource systemPropertyMasterSource() {
+        return new SystemPropertyMasterSource();
+    }
+
+    private static Path configurationFile() {
+        String mavenUserConf = System.getProperty(Constants.MAVEN_USER_CONF);
+        if (mavenUserConf != null) {
+            return Paths.get(mavenUserConf, FILE_NAME);
+        }
+        // this means we are in UT or alike
+        return Paths.get(System.getProperty("user.home"), ".m2", FILE_NAME);
+    }
+}
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4459InMemorySettingsKeptEncryptedTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4459InMemorySettingsKeptEncryptedTest.java
index dbdd0ca38c..2671db2bf3 100644
--- a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4459InMemorySettingsKeptEncryptedTest.java
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng4459InMemorySettingsKeptEncryptedTest.java
@@ -34,7 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 public class MavenITmng4459InMemorySettingsKeptEncryptedTest extends AbstractMavenIntegrationTestCase {
 
     public MavenITmng4459InMemorySettingsKeptEncryptedTest() {
-        super("[2.1.0,3.0-alpha-1),[3.0-alpha-5,)");
+        super("[2.1.0,3.0-alpha-1),[3.0-alpha-5,4.0.0-beta-6)");
     }
 
     /**
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java
new file mode 100644
index 0000000000..02505eb87b
--- /dev/null
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.maven.it;
+
+import java.io.File;
+
+import org.apache.maven.shared.verifier.util.ResourceExtractor;
+import org.junit.jupiter.api.Test;
+
+/**
+ * This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-8379">MNG-8379</a>.
+ */
+class MavenITmng8379SettingsDecryptTest extends AbstractMavenIntegrationTestCase {
+
+    MavenITmng8379SettingsDecryptTest() {
+        super("[4.0.0-beta-6,)");
+    }
+
+    /**
+     *  Verify that all settings are decrypted
+     */
+    @Test
+    void testLegacy() throws Exception {
+        File testDir = ResourceExtractor.simpleExtractResources(getClass(), "/mng-8379-decrypt-settings");
+
+        Verifier verifier = newVerifier(testDir.getAbsolutePath());
+        verifier.setLogFileName("log-legacy.txt");
+        verifier.setForkJvm(true);
+        ItUtils.setUserHome(verifier, new File(testDir, "legacyhome"));
+        verifier.addCliArgument("org.apache.maven.plugins:maven-help-plugin:3.3.0:effective-settings");
+        verifier.addCliArgument("-DshowPasswords");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // there is a warning and all fields decrypted
+        verifier.verifyTextInLog("[WARNING] Pre-Maven 4 legacy encrypted password detected");
+        verifier.verifyTextInLog("<password>testtest</password>");
+        verifier.verifyTextInLog("<value>testtest</value>");
+    }
+
+    /**
+     *  Verify that all settings are decrypted
+     */
+    @Test
+    void testModern() throws Exception {
+        File testDir = ResourceExtractor.simpleExtractResources(getClass(), "/mng-8379-decrypt-settings");
+
+        Verifier verifier = newVerifier(testDir.getAbsolutePath());
+        verifier.setLogFileName("log-modern.txt");
+        verifier.setForkJvm(true);
+        verifier.setEnvironmentVariable("MAVEN_MASTER_PASSWORD", "master");
+        ItUtils.setUserHome(verifier, new File(testDir, "home"));
+        verifier.addCliArgument("org.apache.maven.plugins:maven-help-plugin:3.3.0:effective-settings");
+        verifier.addCliArgument("-DshowPasswords");
+        verifier.execute();
+        verifier.verifyErrorFreeLog();
+
+        // there is no warning and all fields decrypted
+        verifier.verifyTextInLog("<password>testtest</password>");
+        verifier.verifyTextInLog("<value>secretHeader</value>");
+    }
+}
diff --git a/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java b/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
index 48bfe43fd2..afc6b4edcc 100644
--- a/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
+++ b/its/core-it-suite/src/test/java/org/apache/maven/it/TestSuiteOrdering.java
@@ -100,6 +100,7 @@ public class TestSuiteOrdering implements ClassOrderer {
          * the tests are to finishing. Newer tests are also more likely to fail, so this is
          * a fail fast technique as well.
          */
+        suite.addTestSuite(MavenITmng8379SettingsDecryptTest.class);
         suite.addTestSuite(MavenITmng8336UnknownPackagingTest.class);
         suite.addTestSuite(MavenITmng8340GeneratedPomInTargetTest.class);
         suite.addTestSuite(MavenITmng8360SubprojectProfileActivationTest.class);
diff --git a/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/README.md b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/README.md
new file mode 100644
index 0000000000..504d67704c
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/README.md
@@ -0,0 +1,9 @@
+# MNG-8379 IT Settings Decryption
+
+## legacyhome:
+master pw: unsure what (copied from MNG-0553)
+server "test": testuser/testtest and secret-header "testest"
+
+## home
+master pw: master
+server "test": testuser/testtest and secret-header "secretHeader"
\ No newline at end of file
diff --git a/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings-security4.xml b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings-security4.xml
new file mode 100644
index 0000000000..a39d1fbb22
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings-security4.xml
@@ -0,0 +1,20 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<settingsSecurity xmlns="http://codehaus-plexus.github.io/plexus-sec-dispatcher/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://codehaus-plexus.github.io/plexus-sec-dispatcher/4.0.0 https://codehaus-plexus.github.io/xsd/plexus-sec-dispatcher-4.0.0.xsd">
+  <modelVersion>4.0</modelVersion>
+  <defaultDispatcher>master</defaultDispatcher>
+  <configurations>
+    <configuration>
+      <name>master</name>
+      <properties>
+        <property>
+          <name>source</name>
+          <value>env:MAVEN_MASTER_PASSWORD</value>
+        </property>
+        <property>
+          <name>cipher</name>
+          <value>AES/GCM/NoPadding</value>
+        </property>
+      </properties>
+    </configuration>
+  </configurations>
+</settingsSecurity>
diff --git a/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings.xml b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings.xml
new file mode 100644
index 0000000000..c00198c92d
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/home/.m2/settings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<settings>
+  <servers>
+    <server>
+      <id>test</id>
+      <username>testuser</username>
+      <password>{[name=master,cipher=AES/GCM/NoPadding,version=4.0]w+C6vJwryWolUYkLtAMCkMGCWfB09haER/AiingAH4NEDr37euLdwMYYLvxaSQe/97OJ+A==}</password>
+      <configuration>
+        <httpHeaders>
+          <property>
+            <name>secret-header</name>
+            <value>{[name=master,cipher=AES/GCM/NoPadding,version=4.0]/EbAnkmLQnYmz75KTY+5+A4iG/N9AAE8o/SvaWy/E/gOIcyDBuJ5DDFeNj0SI4rtVGPnDbFOHis=}</value>
+          </property>
+        </httpHeaders>
+      </configuration>
+    </server>
+  </servers>
+</settings>
diff --git a/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings-security.xml b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings-security.xml
new file mode 100644
index 0000000000..f61a96994f
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings-security.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<settingsSecurity>
+  <master>{1wQaa6S/o8MH7FnaTNL53XmhT5O0SEGXQi3gC49o6OY=}</master>
+</settingsSecurity>
diff --git a/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings.xml b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings.xml
new file mode 100644
index 0000000000..f28fa5fed4
--- /dev/null
+++ b/its/core-it-suite/src/test/resources/mng-8379-decrypt-settings/legacyhome/.m2/settings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<settings>
+  <servers>
+    <server>
+      <id>testserver</id>
+      <username>testuser</username>
+      <password>{BteqUEnqHecHM7MZfnj9FwLcYbdInWxou1C929Txa0A=}</password>
+      <configuration>
+        <httpHeaders>
+          <property>
+            <name>secret-header</name>
+            <value>{BteqUEnqHecHM7MZfnj9FwLcYbdInWxou1C929Txa0A=}</value>
+          </property>
+        </httpHeaders>
+      </configuration>
+    </server>
+  </servers>
+</settings>
diff --git a/pom.xml b/pom.xml
index 56a84e45a5..86341f6f64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -163,7 +163,7 @@ under the License.
     <plexusTestingVersion>1.4.0</plexusTestingVersion>
     <plexusXmlVersion>4.0.4</plexusXmlVersion>
     <resolverVersion>2.0.3</resolverVersion>
-    <securityDispatcherVersion>4.0.1</securityDispatcherVersion>
+    <securityDispatcherVersion>4.0.2</securityDispatcherVersion>
     <sisuVersion>0.9.0.M3</sisuVersion>
     <slf4jVersion>2.0.16</slf4jVersion>
     <stax2ApiVersion>4.2.2</stax2ApiVersion>