Skip to content

Commit 50aecc7

Browse files
authored
[MNG-8419][MNG-8424] Too aggressive warning for pre-Maven4 passwords (#1970)
Toned down the Maven4 sec dispatcher messages. Also, IF maven3 passwords detected AND there was `.mvn/extensions.xml` the warnings were doubled. Examples: Failure to start Maven (due non-decryptable passwords): ``` $ mvn clean [ERROR] Error executing Maven. [ERROR] Error building settings * FATAL: Could not decrypt password (fix the corrupted password or remove it, if unused) {xL6L/HbmrY++sNkphnq3fguYepTpM04WlIXb8nB1pk=} * WARNING: Detected 2 pre-Maven 4 legacy encrypted password(s) - configure password encryption with the help of mvnenc for increased security. $ ``` Warning at start (due Maven3 passwords): ``` $ mvn clean [INFO] [INFO] Some problems were encountered while building the effective settings (use -X to see details) [INFO] [INFO] Scanning for projects... [INFO] -------------------------------------------------------------------------------------------------------------------------- [INFO] Reactor Build Order: [INFO] [INFO] Apache Maven ... ``` --- https://issues.apache.org/jira/browse/MNG-8424 https://issues.apache.org/jira/browse/MNG-8419
1 parent 79d7739 commit 50aecc7

File tree

8 files changed

+118
-43
lines changed

8 files changed

+118
-43
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.api.services;
20+
21+
import java.util.ArrayList;
22+
import java.util.Comparator;
23+
import java.util.List;
24+
25+
import org.apache.maven.api.annotations.Experimental;
26+
27+
/**
28+
* Base class for all maven exceptions carrying {@link BuilderProblem}s.
29+
*
30+
* @since 4.0.0
31+
*/
32+
@Experimental
33+
public abstract class MavenBuilderException extends MavenException {
34+
35+
private final List<BuilderProblem> problems;
36+
37+
public MavenBuilderException(String message, Throwable cause) {
38+
super(message, cause);
39+
problems = List.of();
40+
}
41+
42+
public MavenBuilderException(String message, List<BuilderProblem> problems) {
43+
super(buildMessage(message, problems), null);
44+
this.problems = problems;
45+
}
46+
47+
/**
48+
* Formats message out of problems: problems are sorted (in natural order of {@link BuilderProblem.Severity})
49+
* and then a list is built. These exceptions are usually thrown in "fatal" cases (and usually prevent Maven
50+
* from starting), and these exceptions may end up very early on output.
51+
*/
52+
protected static String buildMessage(String message, List<BuilderProblem> problems) {
53+
StringBuilder msg = new StringBuilder(message);
54+
ArrayList<BuilderProblem> sorted = new ArrayList<>(problems);
55+
sorted.sort(Comparator.comparing(BuilderProblem::getSeverity));
56+
for (BuilderProblem problem : sorted) {
57+
msg.append("\n * ")
58+
.append(problem.getSeverity().name())
59+
.append(": ")
60+
.append(problem.getMessage());
61+
}
62+
return msg.toString();
63+
}
64+
65+
public List<BuilderProblem> getProblems() {
66+
return problems;
67+
}
68+
}

api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderException.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import java.io.Serial;
2222
import java.util.List;
23-
import java.util.stream.Collectors;
2423

2524
import org.apache.maven.api.annotations.Experimental;
2625

@@ -30,28 +29,20 @@
3029
* @since 4.0.0
3130
*/
3231
@Experimental
33-
public class SettingsBuilderException extends MavenException {
32+
public class SettingsBuilderException extends MavenBuilderException {
3433

3534
@Serial
3635
private static final long serialVersionUID = 4714858598345418083L;
3736

38-
private final List<BuilderProblem> problems;
39-
4037
/**
4138
* @param message the message to give
4239
* @param e the {@link Exception}
4340
*/
4441
public SettingsBuilderException(String message, Exception e) {
4542
super(message, e);
46-
this.problems = List.of();
4743
}
4844

4945
public SettingsBuilderException(String message, List<BuilderProblem> problems) {
50-
super(message + ": " + problems.stream().map(BuilderProblem::toString).collect(Collectors.joining(", ")), null);
51-
this.problems = List.copyOf(problems);
52-
}
53-
54-
public List<BuilderProblem> getProblems() {
55-
return problems;
46+
super(message, problems);
5647
}
5748
}

api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderException.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import java.io.Serial;
2222
import java.util.List;
23-
import java.util.stream.Collectors;
2423

2524
import org.apache.maven.api.annotations.Experimental;
2625

@@ -30,28 +29,20 @@
3029
* @since 4.0.0
3130
*/
3231
@Experimental
33-
public class ToolchainsBuilderException extends MavenException {
32+
public class ToolchainsBuilderException extends MavenBuilderException {
3433

3534
@Serial
3635
private static final long serialVersionUID = 7899871809665729349L;
3736

38-
private final List<BuilderProblem> problems;
39-
4037
/**
4138
* @param message the message to give
4239
* @param e the {@link Exception}
4340
*/
4441
public ToolchainsBuilderException(String message, Exception e) {
4542
super(message, e);
46-
this.problems = List.of();
4743
}
4844

4945
public ToolchainsBuilderException(String message, List<BuilderProblem> problems) {
50-
super(message + ": " + problems.stream().map(BuilderProblem::toString).collect(Collectors.joining(", ")), null);
51-
this.problems = List.copyOf(problems);
52-
}
53-
54-
public List<BuilderProblem> getProblems() {
55-
return problems;
46+
super(message, problems);
5647
}
5748
}

impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/LookupInvoker.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,18 @@ protected void postCommands(C context) throws Exception {
465465
}
466466

467467
protected void settings(C context) throws Exception {
468-
settings(context, context.lookup.lookup(SettingsBuilder.class));
468+
settings(context, true, context.lookup.lookup(SettingsBuilder.class));
469469
}
470470

471-
protected void settings(C context, SettingsBuilder settingsBuilder) throws Exception {
471+
/**
472+
* This method is invoked twice during "normal" LookupInvoker level startup: once when (if present) extensions
473+
* are loaded up during Plexus DI creation, and once afterward as "normal" boot procedure.
474+
* <p>
475+
* If there are Maven3 passwords presents in settings, this results in doubled warnings emitted. So Plexus DI
476+
* creation call keeps "emitSettingsWarnings" false. If there are fatal issues, it will anyway "die" at that
477+
* spot before warnings would be emitted.
478+
*/
479+
protected void settings(C context, boolean emitSettingsWarnings, SettingsBuilder settingsBuilder) throws Exception {
472480
Options mavenOptions = context.invokerRequest.options();
473481

474482
Path userSettingsFile = null;
@@ -557,14 +565,17 @@ protected void settings(C context, SettingsBuilder settingsBuilder) throws Excep
557565
context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
558566
context.localRepositoryPath = localRepositoryPath(context);
559567

560-
if (!settingsResult.getProblems().isEmpty()) {
561-
context.logger.warn("");
562-
context.logger.warn("Some problems were encountered while building the effective settings");
568+
if (emitSettingsWarnings && !settingsResult.getProblems().isEmpty()) {
569+
context.logger.info("");
570+
context.logger.info(
571+
"Some problems were encountered while building the effective settings (use -X to see details)");
563572

564-
for (BuilderProblem problem : settingsResult.getProblems()) {
565-
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
573+
if (context.invokerRequest.options().verbose().orElse(false)) {
574+
for (BuilderProblem problem : settingsResult.getProblems()) {
575+
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
576+
}
566577
}
567-
context.logger.warn("");
578+
context.logger.info("");
568579
}
569580
}
570581

impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/PlexusContainerCapsuleFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public ContainerCapsule createContainerCapsule(LookupInvoker<C> invoker, C conte
7575
return new PlexusContainerCapsule(
7676
context, Thread.currentThread().getContextClassLoader(), container(invoker, context));
7777
} catch (Exception e) {
78-
throw new InvokerException("Failed to create plexus container capsule", e);
78+
throw new InvokerException("Failed to create Plexus DI Container", e);
7979
}
8080
}
8181

@@ -279,7 +279,7 @@ protected void configure() {
279279
container.getLoggerManager().setThresholds(toPlexusLoggingLevel(context.loggerLevel));
280280
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
281281

282-
invoker.settings(context, container.lookup(SettingsBuilder.class));
282+
invoker.settings(context, false, container.lookup(SettingsBuilder.class));
283283

284284
MavenExecutionRequest mer = new DefaultMavenExecutionRequest();
285285
invoker.populateRequest(context, new DefaultLookup(container), mer);

impl/maven-impl/src/main/java/org/apache/maven/internal/impl/DefaultSettingsBuilder.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.ArrayList;
3030
import java.util.List;
3131
import java.util.Map;
32+
import java.util.concurrent.atomic.AtomicInteger;
3233
import java.util.function.Function;
3334
import java.util.function.Supplier;
3435

@@ -273,18 +274,12 @@ private Settings decrypt(
273274
return settings;
274275
}
275276
SecDispatcher secDispatcher = new DefaultSecDispatcher(dispatchers, getSecuritySettings(request.getSession()));
277+
final AtomicInteger preMaven4Passwords = new AtomicInteger(0);
276278
Function<String, String> decryptFunction = str -> {
277279
if (str != null && !str.isEmpty() && !str.contains("${") && secDispatcher.isAnyEncryptedString(str)) {
278280
if (secDispatcher.isLegacyEncryptedString(str)) {
279281
// add a problem
280-
problems.add(new DefaultBuilderProblem(
281-
settingsSource.getLocation(),
282-
-1,
283-
-1,
284-
null,
285-
"Pre-Maven 4 legacy encrypted password detected "
286-
+ " - configure password encryption with the help of mvnenc to be compatible with Maven 4.",
287-
BuilderProblem.Severity.WARNING));
282+
preMaven4Passwords.incrementAndGet();
288283
}
289284
try {
290285
return secDispatcher.decrypt(str);
@@ -295,12 +290,23 @@ private Settings decrypt(
295290
-1,
296291
e,
297292
"Could not decrypt password (fix the corrupted password or remove it, if unused) " + str,
298-
BuilderProblem.Severity.ERROR));
293+
BuilderProblem.Severity.FATAL));
299294
}
300295
}
301296
return str;
302297
};
303-
return new SettingsTransformer(decryptFunction).visit(settings);
298+
Settings result = new SettingsTransformer(decryptFunction).visit(settings);
299+
if (preMaven4Passwords.get() > 0) {
300+
problems.add(new DefaultBuilderProblem(
301+
settingsSource.getLocation(),
302+
-1,
303+
-1,
304+
null,
305+
"Detected " + preMaven4Passwords.get() + " pre-Maven 4 legacy encrypted password(s) "
306+
+ "- configure password encryption with the help of mvnenc for increased security.",
307+
BuilderProblem.Severity.WARNING));
308+
}
309+
return result;
304310
}
305311

306312
private Path getSecuritySettings(ProtoSession session) {

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng3748BadSettingsXmlTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.File;
2222
import java.util.List;
2323

24+
import org.junit.jupiter.api.Disabled;
2425
import org.junit.jupiter.api.Test;
2526

2627
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -33,7 +34,10 @@
3334
*
3435
* @author jdcasey
3536
*
37+
* NOTE (cstamas): this IT was written to test that settings.xml is STRICT, while later changes modified
38+
* this very IT into the opposite: to test that parsing is LENIENT.
3639
*/
40+
@Disabled("This is archaic test; we should strive to make settings.xml parsing strict again")
3741
public class MavenITmng3748BadSettingsXmlTest extends AbstractMavenIntegrationTestCase {
3842

3943
public MavenITmng3748BadSettingsXmlTest() {

its/core-it-suite/src/test/java/org/apache/maven/it/MavenITmng8379SettingsDecryptTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ void testLegacy() throws Exception {
4747
verifier.verifyErrorFreeLog();
4848

4949
// there is a warning and all fields decrypted
50-
verifier.verifyTextInLog("[WARNING] Pre-Maven 4 legacy encrypted password detected");
50+
verifier.verifyTextInLog(
51+
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
5152
verifier.verifyTextInLog("<password>testtest</password>");
5253
verifier.verifyTextInLog("<value>testtest</value>");
5354
}
@@ -69,6 +70,9 @@ void testModern() throws Exception {
6970
verifier.verifyErrorFreeLog();
7071

7172
// there is no warning and all fields decrypted
73+
verifier.verifyTextNotInLog("[WARNING]");
74+
verifier.verifyTextNotInLog(
75+
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
7276
verifier.verifyTextInLog("<password>testtest</password>");
7377
verifier.verifyTextInLog("<value>secretHeader</value>");
7478
}

0 commit comments

Comments
 (0)