mirror of https://github.com/apache/maven.git
[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
This commit is contained in:
parent
79d7739dcc
commit
50aecc7432
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* 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.api.services;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all maven exceptions carrying {@link BuilderProblem}s.
|
||||||
|
*
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@Experimental
|
||||||
|
public abstract class MavenBuilderException extends MavenException {
|
||||||
|
|
||||||
|
private final List<BuilderProblem> problems;
|
||||||
|
|
||||||
|
public MavenBuilderException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
problems = List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MavenBuilderException(String message, List<BuilderProblem> problems) {
|
||||||
|
super(buildMessage(message, problems), null);
|
||||||
|
this.problems = problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats message out of problems: problems are sorted (in natural order of {@link BuilderProblem.Severity})
|
||||||
|
* and then a list is built. These exceptions are usually thrown in "fatal" cases (and usually prevent Maven
|
||||||
|
* from starting), and these exceptions may end up very early on output.
|
||||||
|
*/
|
||||||
|
protected static String buildMessage(String message, List<BuilderProblem> problems) {
|
||||||
|
StringBuilder msg = new StringBuilder(message);
|
||||||
|
ArrayList<BuilderProblem> sorted = new ArrayList<>(problems);
|
||||||
|
sorted.sort(Comparator.comparing(BuilderProblem::getSeverity));
|
||||||
|
for (BuilderProblem problem : sorted) {
|
||||||
|
msg.append("\n * ")
|
||||||
|
.append(problem.getSeverity().name())
|
||||||
|
.append(": ")
|
||||||
|
.append(problem.getMessage());
|
||||||
|
}
|
||||||
|
return msg.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BuilderProblem> getProblems() {
|
||||||
|
return problems;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,6 @@ package org.apache.maven.api.services;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.maven.api.annotations.Experimental;
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
|
||||||
|
@ -30,28 +29,20 @@ import org.apache.maven.api.annotations.Experimental;
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
@Experimental
|
@Experimental
|
||||||
public class SettingsBuilderException extends MavenException {
|
public class SettingsBuilderException extends MavenBuilderException {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 4714858598345418083L;
|
private static final long serialVersionUID = 4714858598345418083L;
|
||||||
|
|
||||||
private final List<BuilderProblem> problems;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param message the message to give
|
* @param message the message to give
|
||||||
* @param e the {@link Exception}
|
* @param e the {@link Exception}
|
||||||
*/
|
*/
|
||||||
public SettingsBuilderException(String message, Exception e) {
|
public SettingsBuilderException(String message, Exception e) {
|
||||||
super(message, e);
|
super(message, e);
|
||||||
this.problems = List.of();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsBuilderException(String message, List<BuilderProblem> problems) {
|
public SettingsBuilderException(String message, List<BuilderProblem> problems) {
|
||||||
super(message + ": " + problems.stream().map(BuilderProblem::toString).collect(Collectors.joining(", ")), null);
|
super(message, problems);
|
||||||
this.problems = List.copyOf(problems);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BuilderProblem> getProblems() {
|
|
||||||
return problems;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.maven.api.services;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.maven.api.annotations.Experimental;
|
import org.apache.maven.api.annotations.Experimental;
|
||||||
|
|
||||||
|
@ -30,28 +29,20 @@ import org.apache.maven.api.annotations.Experimental;
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
@Experimental
|
@Experimental
|
||||||
public class ToolchainsBuilderException extends MavenException {
|
public class ToolchainsBuilderException extends MavenBuilderException {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 7899871809665729349L;
|
private static final long serialVersionUID = 7899871809665729349L;
|
||||||
|
|
||||||
private final List<BuilderProblem> problems;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param message the message to give
|
* @param message the message to give
|
||||||
* @param e the {@link Exception}
|
* @param e the {@link Exception}
|
||||||
*/
|
*/
|
||||||
public ToolchainsBuilderException(String message, Exception e) {
|
public ToolchainsBuilderException(String message, Exception e) {
|
||||||
super(message, e);
|
super(message, e);
|
||||||
this.problems = List.of();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ToolchainsBuilderException(String message, List<BuilderProblem> problems) {
|
public ToolchainsBuilderException(String message, List<BuilderProblem> problems) {
|
||||||
super(message + ": " + problems.stream().map(BuilderProblem::toString).collect(Collectors.joining(", ")), null);
|
super(message, problems);
|
||||||
this.problems = List.copyOf(problems);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BuilderProblem> getProblems() {
|
|
||||||
return problems;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -465,10 +465,18 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void settings(C context) throws Exception {
|
protected void settings(C context) throws Exception {
|
||||||
settings(context, context.lookup.lookup(SettingsBuilder.class));
|
settings(context, true, context.lookup.lookup(SettingsBuilder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void settings(C context, SettingsBuilder settingsBuilder) throws Exception {
|
/**
|
||||||
|
* This method is invoked twice during "normal" LookupInvoker level startup: once when (if present) extensions
|
||||||
|
* are loaded up during Plexus DI creation, and once afterward as "normal" boot procedure.
|
||||||
|
* <p>
|
||||||
|
* If there are Maven3 passwords presents in settings, this results in doubled warnings emitted. So Plexus DI
|
||||||
|
* creation call keeps "emitSettingsWarnings" false. If there are fatal issues, it will anyway "die" at that
|
||||||
|
* spot before warnings would be emitted.
|
||||||
|
*/
|
||||||
|
protected void settings(C context, boolean emitSettingsWarnings, SettingsBuilder settingsBuilder) throws Exception {
|
||||||
Options mavenOptions = context.invokerRequest.options();
|
Options mavenOptions = context.invokerRequest.options();
|
||||||
|
|
||||||
Path userSettingsFile = null;
|
Path userSettingsFile = null;
|
||||||
|
@ -557,14 +565,17 @@ public abstract class LookupInvoker<C extends LookupContext> implements Invoker
|
||||||
context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
|
context.interactive = mayDisableInteractiveMode(context, context.effectiveSettings.isInteractiveMode());
|
||||||
context.localRepositoryPath = localRepositoryPath(context);
|
context.localRepositoryPath = localRepositoryPath(context);
|
||||||
|
|
||||||
if (!settingsResult.getProblems().isEmpty()) {
|
if (emitSettingsWarnings && !settingsResult.getProblems().isEmpty()) {
|
||||||
context.logger.warn("");
|
context.logger.info("");
|
||||||
context.logger.warn("Some problems were encountered while building the effective settings");
|
context.logger.info(
|
||||||
|
"Some problems were encountered while building the effective settings (use -X to see details)");
|
||||||
|
|
||||||
for (BuilderProblem problem : settingsResult.getProblems()) {
|
if (context.invokerRequest.options().verbose().orElse(false)) {
|
||||||
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
|
for (BuilderProblem problem : settingsResult.getProblems()) {
|
||||||
|
context.logger.warn(problem.getMessage() + " @ " + problem.getLocation());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
context.logger.warn("");
|
context.logger.info("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class PlexusContainerCapsuleFactory<C extends LookupContext> implements C
|
||||||
return new PlexusContainerCapsule(
|
return new PlexusContainerCapsule(
|
||||||
context, Thread.currentThread().getContextClassLoader(), container(invoker, context));
|
context, Thread.currentThread().getContextClassLoader(), container(invoker, context));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new InvokerException("Failed to create plexus container capsule", e);
|
throw new InvokerException("Failed to create Plexus DI Container", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ public class PlexusContainerCapsuleFactory<C extends LookupContext> implements C
|
||||||
container.getLoggerManager().setThresholds(toPlexusLoggingLevel(context.loggerLevel));
|
container.getLoggerManager().setThresholds(toPlexusLoggingLevel(context.loggerLevel));
|
||||||
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
|
Thread.currentThread().setContextClassLoader(container.getContainerRealm());
|
||||||
|
|
||||||
invoker.settings(context, container.lookup(SettingsBuilder.class));
|
invoker.settings(context, false, container.lookup(SettingsBuilder.class));
|
||||||
|
|
||||||
MavenExecutionRequest mer = new DefaultMavenExecutionRequest();
|
MavenExecutionRequest mer = new DefaultMavenExecutionRequest();
|
||||||
invoker.populateRequest(context, new DefaultLookup(container), mer);
|
invoker.populateRequest(context, new DefaultLookup(container), mer);
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -273,18 +274,12 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
SecDispatcher secDispatcher = new DefaultSecDispatcher(dispatchers, getSecuritySettings(request.getSession()));
|
SecDispatcher secDispatcher = new DefaultSecDispatcher(dispatchers, getSecuritySettings(request.getSession()));
|
||||||
|
final AtomicInteger preMaven4Passwords = new AtomicInteger(0);
|
||||||
Function<String, String> decryptFunction = str -> {
|
Function<String, String> decryptFunction = str -> {
|
||||||
if (str != null && !str.isEmpty() && !str.contains("${") && secDispatcher.isAnyEncryptedString(str)) {
|
if (str != null && !str.isEmpty() && !str.contains("${") && secDispatcher.isAnyEncryptedString(str)) {
|
||||||
if (secDispatcher.isLegacyEncryptedString(str)) {
|
if (secDispatcher.isLegacyEncryptedString(str)) {
|
||||||
// add a problem
|
// add a problem
|
||||||
problems.add(new DefaultBuilderProblem(
|
preMaven4Passwords.incrementAndGet();
|
||||||
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 {
|
try {
|
||||||
return secDispatcher.decrypt(str);
|
return secDispatcher.decrypt(str);
|
||||||
|
@ -295,12 +290,23 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
|
||||||
-1,
|
-1,
|
||||||
e,
|
e,
|
||||||
"Could not decrypt password (fix the corrupted password or remove it, if unused) " + str,
|
"Could not decrypt password (fix the corrupted password or remove it, if unused) " + str,
|
||||||
BuilderProblem.Severity.ERROR));
|
BuilderProblem.Severity.FATAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
return new SettingsTransformer(decryptFunction).visit(settings);
|
Settings result = new SettingsTransformer(decryptFunction).visit(settings);
|
||||||
|
if (preMaven4Passwords.get() > 0) {
|
||||||
|
problems.add(new DefaultBuilderProblem(
|
||||||
|
settingsSource.getLocation(),
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
null,
|
||||||
|
"Detected " + preMaven4Passwords.get() + " pre-Maven 4 legacy encrypted password(s) "
|
||||||
|
+ "- configure password encryption with the help of mvnenc for increased security.",
|
||||||
|
BuilderProblem.Severity.WARNING));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getSecuritySettings(ProtoSession session) {
|
private Path getSecuritySettings(ProtoSession session) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.maven.it;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -33,7 +34,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
*
|
*
|
||||||
* @author jdcasey
|
* @author jdcasey
|
||||||
*
|
*
|
||||||
|
* NOTE (cstamas): this IT was written to test that settings.xml is STRICT, while later changes modified
|
||||||
|
* this very IT into the opposite: to test that parsing is LENIENT.
|
||||||
*/
|
*/
|
||||||
|
@Disabled("This is archaic test; we should strive to make settings.xml parsing strict again")
|
||||||
public class MavenITmng3748BadSettingsXmlTest extends AbstractMavenIntegrationTestCase {
|
public class MavenITmng3748BadSettingsXmlTest extends AbstractMavenIntegrationTestCase {
|
||||||
|
|
||||||
public MavenITmng3748BadSettingsXmlTest() {
|
public MavenITmng3748BadSettingsXmlTest() {
|
||||||
|
|
|
@ -47,7 +47,8 @@ class MavenITmng8379SettingsDecryptTest extends AbstractMavenIntegrationTestCase
|
||||||
verifier.verifyErrorFreeLog();
|
verifier.verifyErrorFreeLog();
|
||||||
|
|
||||||
// there is a warning and all fields decrypted
|
// there is a warning and all fields decrypted
|
||||||
verifier.verifyTextInLog("[WARNING] Pre-Maven 4 legacy encrypted password detected");
|
verifier.verifyTextInLog(
|
||||||
|
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
|
||||||
verifier.verifyTextInLog("<password>testtest</password>");
|
verifier.verifyTextInLog("<password>testtest</password>");
|
||||||
verifier.verifyTextInLog("<value>testtest</value>");
|
verifier.verifyTextInLog("<value>testtest</value>");
|
||||||
}
|
}
|
||||||
|
@ -69,6 +70,9 @@ class MavenITmng8379SettingsDecryptTest extends AbstractMavenIntegrationTestCase
|
||||||
verifier.verifyErrorFreeLog();
|
verifier.verifyErrorFreeLog();
|
||||||
|
|
||||||
// there is no warning and all fields decrypted
|
// there is no warning and all fields decrypted
|
||||||
|
verifier.verifyTextNotInLog("[WARNING]");
|
||||||
|
verifier.verifyTextNotInLog(
|
||||||
|
"[INFO] Some problems were encountered while building the effective settings (use -X to see details)");
|
||||||
verifier.verifyTextInLog("<password>testtest</password>");
|
verifier.verifyTextInLog("<password>testtest</password>");
|
||||||
verifier.verifyTextInLog("<value>secretHeader</value>");
|
verifier.verifyTextInLog("<value>secretHeader</value>");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue