initial checkin

This commit is contained in:
2023-05-24 23:51:09 -04:00
commit 7f8baa6fd1
12 changed files with 1728 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin-log4j-all</artifactId>
<version>@pom.version@</version>
<packaging>jar</packaging>
<name>Log4j Ban Plugin Tests</name>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>@pom.version@</version>
<extensions>true</extensions>
<configuration>
<includes>
<artifact>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</artifact>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin-log4j-old</artifactId>
<version>@pom.version@</version>
<packaging>jar</packaging>
<name>Log4j Ban Plugin Tests</name>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>@pom.version@</version>
<extensions>true</extensions>
<configuration>
<includes>
<artifact>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</artifact>
</includes>
<excludes>
<artifact>log4j:log.+:[1.2.17,)</artifact>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

30
src/it/ban-none/pom.xml Normal file
View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin-unsued</artifactId>
<version>@pom.version@</version>
<packaging>jar</packaging>
<name>Log4j Ban Plugin Tests</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>@pom.version@</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>

35
src/it/ban-unused/pom.xml Normal file
View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin-unsued</artifactId>
<version>@pom.version@</version>
<packaging>jar</packaging>
<name>Log4j Ban Plugin Tests</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>${project.groupId}</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>@pom.version@</version>
<extensions>true</extensions>
<configuration>
<includes>
<artifact>log4j:log.*</artifact>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,185 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Plugin;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
public class BanConfigurationParser implements DependencyFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Pattern artifactPattern = Pattern.compile("^([^:]+):([^:]+)(:([^:]+))?$");
private final Pattern notRegexPattern = Pattern.compile("^[A-Za-z0-9_\\.]*$");
private final List<ArtifactFilter> includeArtifacts;
private final List<ArtifactFilter> excludeArtifacts;
private boolean failFast = false;
public BanConfigurationParser(Plugin plugin) {
Xpp3Dom rootDom = (Xpp3Dom) plugin.getConfiguration();
if (rootDom == null) {
this.includeArtifacts = Collections.emptyList();
this.excludeArtifacts = Collections.emptyList();
return;
}
Xpp3Dom includesDom = rootDom.getChild("includes");
if (includesDom != null) {
this.includeArtifacts = this.parseArtifacts(includesDom);
this.logger.debug("Include artifacts: {}", this.includeArtifacts);
} else {
this.includeArtifacts = Collections.emptyList();
}
Xpp3Dom excludesDom = rootDom.getChild("excludes");
if (excludesDom != null) {
this.excludeArtifacts = this.parseArtifacts(excludesDom);
this.logger.debug("Exclude artifacts: {}", this.excludeArtifacts);
} else {
this.excludeArtifacts = Collections.emptyList();
}
}
public void setFailFast(boolean failFast) {
this.failFast = failFast;
}
private List<ArtifactFilter> parseArtifacts(Xpp3Dom artifactsDom) {
List<ArtifactFilter> filters = new LinkedList<>();
for (Xpp3Dom artifactDom : artifactsDom.getChildren("artifact")) {
ArtifactFilter filter = new ArtifactFilter();
String versionSpec = null;
if (artifactDom.getChildCount() == 0) {
Matcher matcher = this.artifactPattern.matcher(artifactDom.getValue());
if (!matcher.matches()) {
this.logger.warn("The artifact format '{}' does not match the expected regular expression: {}; ignoring ...", artifactDom.getValue(), this.artifactPattern.pattern());
continue;
}
if (this.notRegexPattern.matcher(matcher.group(1)).matches()) {
filter.setGroupId(StringUtils.trimToNull(matcher.group(1)));
} else {
filter.setGroupIdRegex(StringUtils.trimToNull(matcher.group(1)));
}
if (this.notRegexPattern.matcher(matcher.group(1)).matches()) {
filter.setArtifactId(StringUtils.trimToNull(matcher.group(2)));
} else {
filter.setArtifactIdRegex(StringUtils.trimToNull(matcher.group(2)));
}
versionSpec = StringUtils.trimToNull(matcher.group(4));
} else {
filter.setGroupId(this.getChildValue(artifactDom, "groupId"));
filter.setGroupIdRegex(this.getChildValue(artifactDom, "groupIdRegex"));
filter.setArtifactId(this.getChildValue(artifactDom, "artifactId"));
filter.setArtifactIdRegex(this.getChildValue(artifactDom, "artifactIdRegex"));
versionSpec = this.getChildValue(artifactDom, "version");
}
if (versionSpec != null) {
try {
VersionRange versionRange = VersionRange.createFromVersionSpec(versionSpec);
filter.setVersionRange(versionRange);
} catch (InvalidVersionSpecificationException ivse) {
this.logger.warn("The artifact '{}' has an invalid version specification; the artifact element will be ignored: {}", ivse.getMessage());
continue;
}
}
filters.add(filter);
}
return filters;
}
private String getChildValue(Xpp3Dom dom, String child) {
Xpp3Dom childDom = dom.getChild(child);
return childDom == null ? null : StringUtils.trimToNull(childDom.getValue());
}
@Override
public boolean accept(DependencyNode node, List<DependencyNode> parents) {
this.logger.debug("Evaluating dependency '{}'", node);
boolean ban = false;
for (ArtifactFilter afilter : this.includeArtifacts) {
Artifact depArtifact = node.getArtifact();
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban inclusion filter", depArtifact);
ban = true;
break;
}
}
if (!ban)
return false;
for (ArtifactFilter afilter : this.excludeArtifacts) {
Artifact depArtifact = node.getArtifact();
if (this.matches(afilter.getGroupId(), afilter.getGroupIdRegex(), depArtifact.getGroupId()) &&
this.matches(afilter.getArtifactId(), afilter.getArtifactIdRegex(), depArtifact.getArtifactId()) &&
this.withinRange(afilter.getVersionRange(), depArtifact.getVersion())) {
this.logger.debug("The dependency '{}' matches the ban exlusion filter", depArtifact);
return false;
}
}
if (this.failFast) {
// plugin resolution downloads banned dependencies unless we fail now; not later
throw new RuntimeException("Banned dependency detected: " + node + " => " + parents);
} else {
return true;
}
}
private boolean matches(String exactFilter, String regexFilter, String value) {
if (exactFilter == null && regexFilter == null) {
return true;
} else if (exactFilter != null) {
return exactFilter.equals(value);
} else {
Pattern filterPattern = Pattern.compile(regexFilter);
Matcher matcher = filterPattern.matcher(value);
return matcher.matches();
}
}
private boolean withinRange(VersionRange versionRange, String version) {
return versionRange == null || versionRange.containsVersion(new DefaultArtifactVersion(version));
}
}

View File

@@ -0,0 +1,126 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.ban;
import java.util.LinkedList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.MavenExecutionException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.apache.maven.project.DefaultDependencyResolutionRequest;
import org.apache.maven.project.DependencyResolutionException;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectDependenciesResolver;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Named("ban")
@Singleton
public class BanExtension extends AbstractMavenLifecycleParticipant {
private static final String THIS_PLUGIN_GROUP_ID = "com.inteligr8";
private static final String THIS_PLUGIN_ARTIFACT_ID = "ban-maven-plugin";
private static final String THIS_PLUGIN_KEY = THIS_PLUGIN_GROUP_ID + ":" + THIS_PLUGIN_ARTIFACT_ID;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Inject
private ProjectDependenciesResolver projDepResolver;
@Inject
private PluginDependenciesResolver pluginDepResolver;
@Override
public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
MavenProject project = session.getCurrentProject();
BanConfigurationParser config = this.getConfiguration(project);
if (config == null)
return;
config.setFailFast(true);
try {
for (Plugin plugin : project.getBuildPlugins()) {
this.logger.debug("Evaluating plugin dependencies: {}", plugin);
Artifact artifact = new DefaultArtifact(plugin.getId());
DependencyNode depNodeRoot = this.pluginDepResolver.resolve(plugin, artifact, config, project.getRemotePluginRepositories(), session.getRepositorySession());
List<Dependency> bannedDependencies = this.crawlDependencyTree(depNodeRoot, config);
if (!bannedDependencies.isEmpty())
throw new MavenExecutionException("Banned dependencies were detected in plugin '" + plugin + "': " + bannedDependencies, project.getFile());
}
} catch (PluginResolutionException pre) {
throw new MavenExecutionException(pre.getMessage(), pre);
}
config.setFailFast(false);
DefaultDependencyResolutionRequest request = new DefaultDependencyResolutionRequest(project, session.getRepositorySession());
request.setResolutionFilter(config);
try {
DependencyResolutionResult result = this.projDepResolver.resolve(request);
List<Dependency> bannedDependencies = result.getDependencies();
if (!bannedDependencies.isEmpty())
throw new MavenExecutionException("Banned dependencies were detected: " + bannedDependencies, project.getFile());
} catch (DependencyResolutionException dre) {
throw new MavenExecutionException(dre.getMessage(), dre);
}
}
private BanConfigurationParser getConfiguration(MavenProject project) throws MavenExecutionException {
Plugin plugin = project.getPlugin(THIS_PLUGIN_KEY);
if (plugin == null)
throw new MavenExecutionException("The plugin is executing but it cannot be found", project.getFile());
if (!plugin.isExtensions()) {
this.logger.warn("The '{}' plugin must be defined with '<extensions>true</extensions>'; ignoring plugin", plugin.getId());
return null;
} else {
return new BanConfigurationParser(plugin);
}
}
private List<Dependency> crawlDependencyTree(DependencyNode depNode, DependencyFilter depFilter) {
return this.crawlDependencyTree(depNode, new LinkedList<>(), depFilter);
}
private List<Dependency> crawlDependencyTree(DependencyNode depNode, List<DependencyNode> parentDepNodes, DependencyFilter depFilter) {
List<Dependency> dependencies = new LinkedList<>();
if (depFilter.accept(depNode, parentDepNodes))
dependencies.add(depNode.getDependency());
parentDepNodes.add(0, depNode);
for (DependencyNode childDepNode : depNode.getChildren())
dependencies.addAll(this.crawlDependencyTree(childDepNode, parentDepNodes, depFilter));
parentDepNodes.remove(0);
return dependencies;
}
}

View File

@@ -0,0 +1,102 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.maven.model;
import org.apache.maven.artifact.versioning.VersionRange;
public class ArtifactFilter {
private String groupId;
private String groupIdRegex;
private String artifactId;
private String artifactIdRegex;
private VersionRange versionRange;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getGroupIdRegex() {
return groupIdRegex;
}
public void setGroupIdRegex(String groupIdRegex) {
this.groupIdRegex = groupIdRegex;
}
public String getArtifactId() {
return artifactId;
}
public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
public String getArtifactIdRegex() {
return artifactIdRegex;
}
public void setArtifactIdRegex(String artifactIdRegex) {
this.artifactIdRegex = artifactIdRegex;
}
public VersionRange getVersionRange() {
return versionRange;
}
public void setVersionRange(VersionRange versionRange) {
this.versionRange = versionRange;
}
@Override
public String toString() {
StringBuilder afilter = new StringBuilder();
if (this.groupId != null) {
afilter.append(this.groupId);
} else if (this.groupIdRegex != null) {
afilter.append(this.groupIdRegex);
} else {
afilter.append('*');
}
afilter.append(':');
if (this.artifactId != null) {
afilter.append(this.artifactId);
} else if (this.artifactIdRegex != null) {
afilter.append(this.artifactIdRegex);
} else {
afilter.append('*');
}
afilter.append(':');
if (this.versionRange != null) {
afilter.append(this.versionRange.toString());
} else {
afilter.append('*');
}
return afilter.toString();
}
}