19 Commits

Author SHA1 Message Date
d41d73fa1c v1.3.1 pom 2023-08-20 12:59:23 -04:00
a9aa47d412 Merge branch 'develop' into stable 2023-08-20 12:58:55 -04:00
cfb13f3170 v1.3.0 pom 2023-08-20 12:04:15 -04:00
75f8175a1c Merge branch 'develop' into stable 2023-08-20 12:03:52 -04:00
58eb0ec013 v1.2.4 pom 2023-08-20 11:35:52 -04:00
dec181aa71 Merge branch 'develop' into stable 2023-08-20 11:35:29 -04:00
b6c7323203 v1.2.3 pom 2023-08-16 12:38:18 -04:00
9644bbe154 Merge branch 'develop' into stable 2023-08-16 12:37:38 -04:00
118b40f3f9 v1.2.2 pom 2023-06-30 12:42:52 -04:00
db3061a3a6 Merge branch 'develop' into stable 2023-06-30 12:42:25 -04:00
f991975923 v1.2.1 pom 2023-05-30 09:37:29 -04:00
e4f14a81da Merge branch 'develop' into stable 2023-05-30 09:36:26 -04:00
f749926f92 v1.2.0 pom 2023-05-29 11:13:11 -04:00
8c3ce9f069 Merge branch 'develop' into stable 2023-05-29 11:11:02 -04:00
cf2fdf42fa v1.1.1 pom 2023-05-25 15:24:41 -04:00
3d7d8bb769 Merge branch 'develop' into stable 2023-05-25 15:24:08 -04:00
01768754f0 v1.1.0 pom 2023-05-25 13:22:00 -04:00
e241137209 Merge branch 'develop' into stable 2023-05-25 13:19:36 -04:00
ff2a8b89e5 v1.0.0 pom 2023-05-25 08:56:24 -04:00
13 changed files with 131 additions and 655 deletions

198
README.md
View File

@@ -3,170 +3,9 @@
This is a maven plugin that allows for developers and organizations to ban Maven artifacts. We are keenly aware of the capability in the `maven-enforcer-plugin`. Instead of simply generating an error when a banned artifact is referenced, this plugin prevents the artifact from being downloaded as well. This is crucial within certain organizations with strict security scans that crawl the Maven cache.
## Extension
When using as an extension, it will enforce a ban on the configured dependencies and plugins and recursively their dependencies. See the snippet below for how the plugin should be declared in your project's `pom.xml`.
```xml
<project>
...
<build>
...
<plugins>
...
<plugin>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>...</version>
<extensions>true</extensions>
<configuration>
...
</configuration>
</plugin>
...
</plugins>
...
</build>
...
</project>
```
The `extensions` elements is critical. Without it, the plugin does nothing as far as banning artifacts/dependencies. With it, the plugin is able to not only detect banned artifacts, but do it before they are downloaded. This keeps libraries from even reaching your local Maven repository cache.
### Configuration
| Element | Description |
| ------------------- | ----------- |
| `import/file` | Import a ban configuration file from the project. |
| `import/url` | Import a ban configuration file from the URL. |
| `import/artifact` | Import a ban configuration file from the XML artifact of the specified artifact in the notation `groupId:artifactId:version`. |
| `includes/artifact` | Include the specified artifact and version(s) in the list of banned artifacts. See the section below on how to specify the `artifact` element. |
| `excludes/artifact` | Exclude the specified artifact and version(s) from the list of banned artifacts. See the section below on how to specify the `artifact` element. |
When specifying `artifact`, you can use any of the following formats. The example is of this library; just replace the values to match the artifacts you want to ban.
- Exact artifact/version: `com.inteligr8:ban-maven-plugin:1.0.0`
- Exact artifact; all versions: `com.inteligr8:ban-maven-plugin`
- Exact artifact; version range: `com.inteligr8:ban-maven-plugin:[,1.4.0)`
- All artifacts/versions in group: `org.springframework:`
- All artifacts in group; version range: `org.springframework::[,6.0.0)`
- All artifacts in group/subgroups; version range: `org.springframework.*::[,6.0.0)`
The same `artifact` element can use the long notation:
```xml
<artifact>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>[,1.4.0)</version>
</artifact>
```
It supports the use of regular expressions with `groupIdRegex` and `artifactIdRegex`.
```xml
<artifact>
<groupId>com.inteligr8</groupId>
<artifactIdRegex>ban-.+</artifactIdRegex>
<version>[,1.4.0)</version>
</artifact>
```
If no `includes` are provided, then no artifacts will be banned. An *included* artifact is a banned artifact. An *excluded* artifact is not banned. It is the opposite of what you may think. If no `excludes` are provided, then no banned artifacts are granted an exception.
The `artifact` element supports the descriptive `groupId`/`artifactId`/`version` elements or the abbreviated colon-based notation. When using the colon-based notation, the group ID and artifact ID may be treated as `groupIdRegex` and `artifactIdRegex` (see below). If you only use acceptable `groupId` and `artifactId` characters (letters/numbers/dashes/underscores/dots), it will not. But if you include any other characters, like `\.` or `*`, then it will be treated as regex. How it is treated will impact the functionality of `purge-repo` goal, if you are using it.
If `groupId` or `artifactId` or `version` are not provided, they are ignored in the matching process. So it will match all applicable artifacts and the constraint will be only for what was specified. This means that `<includes><artifact>:</artifact></includes>` will ban every artifact and all their versions.
If `groupId` and `groupIdRegex` are both provided, only `groupId` is used. The same is true for `artifactId` and `artifactIdRegex`. The `*Regex` element values use standard Java regular expression parsing. If using regular expressions, remember to escape the dots (`\.`) in group IDs. If you do use `groupIdRegex` or use regular expressions in the colon-notation, the matching artifacts will not be purged using the `purge-repo` goal. So if you intend to use that goal, group ID regular expression matching needs be avoided.
The `version` element supports the standard Maven specification. You can match a specific version like `1.0.0`. Or you can match all versions before `1.2.17` like `[,1.2.17)`. You can match all future versions after `1.2.17` (inclusive) with `[1.2.17,)`.
There is nothing stopping you from specifying two `artifact` elements with the exact same values. So you can ban multiple version ranges of the same artifact by using multiple `artifact` elements.
If you *include* all versions by omitting the `version` element, you can still *exclude* (un-ban) certain versions, like `[1.2.17,)`.
Order does not matter. All include specifications are processed, followed by all exclude specifications.
### Import
The `import` file, URL, and artifact are to reference XML files that conform to the same `configuration` element as described here. In fact, the root element of that XML should be `configuration`. It will only support the `includes` and `excludes` elements. so you cannot do recursive imports.
You can create a Maven `pom` packaging type project that deploys a configuration XML to your Maven repository. Then use an `import` to allow you to change banned dependencies without making changes to each individual project. Just like with the `version` notation in the `includes` and `excludes` elements, your `import` `artifact` element supports a version range. This way the latest banned dependencies can be side-loaded into all projects. This means previously functioning builds may eventually start failing. That is by design in this scenario.
The `import` elements supports multiple `url` or `artifact` declarations. All imported and directly specified include specifications are processed before all exclude specifications. You cannot change an include when importing, but you can add new ones, that may cover more versions; and you can exclude versions that may have been included by the import.
The `excludes` element is a way to provide project-by-project exceptions to imported banned artifacts where warranted.
### Examples
The recommended use of this plugin is for its use across whole organizations. First, you will want a simple Maven project that is referenced by all other Maven projects. That simple project will declare the banned artifacts and potentially purge existing ones. See the `examples/ban-config` project for a full example.
```xml
<configuration>
<includes>
<!-- CVE-2019-17571 -->
<artifact>org.apache.logging.log4j::[,2.17.1)</artifact>
<artifact>log4j:log4j</artifact>
</includes>
</configuration>
```
Deploying that project will result in the publication of the `ban-config.xml` to your Maven repository. That is where it can be picked up by all other projects so they can enforce the ban. If you do not have a local Maven repository, then you will have to upload the `ban-config.xml` to some other URL-accessible location by some other means.
Once you have that in place, you will want to add the following to every single Maven project that should be governed by the aforementioned `ban-config`. See the `examples/governed-artifact` project for a full example.
```xml
<plugin>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>...</version>
<extensions>true</extensions>
<configuration>
<import>
<artifact>com.inteligr8:ban-config:[2025.03,)</artifact>
</import>
</configuration>
</plugin>
```
## Goals
Within a project, this is typically only used as an extension with no execution/goal. There is one goal for general execution though.
### `purge-repo`
This goal will purge the local Maven repository of banned artifacts. The most common use is without any real project, but Maven requires one to exist in the directory of execution. This executes in the `clean` phase by default.
```bash
mvn -Dban.file=ban-config.xml com.inteligr8:ban-maven-plugin:1.4.1:purge-repo clean
```
This goal does **NOT support** `groupIdRegex` or blank `groupId` specifications. So any of those will be ignored not be purged/removed (if in `includes`).
#### Configuration
The configuration is identical to what is documented above. However, this table focuses on the properties available as you may not be defining this in a `pom.xml`.
| Element | Maven/Java Property |
| ------------------- | ------------------- |
| `import/file` | `ban.file` |
| `import/url` | `ban.url` |
| `import/artifact` | `ban.artifact` |
The following additional elements/properties are supported:
| Element | Maven/Java Property | Default | Description |
| -------- | ------------------- | ------- | ----------- |
| `skip` | `ban.skip` | `false` | `true` to skip the purge. |
| `dryRun` | `ban.dryRun` | `false` | `true` to not actually delete any files or directories. |
| `eager` | `ban.eager | `false` | `true` to delete non-artifact (e.g. `pom` and `_remote.repositories`) files. |
## Usage
### Prevent Banned Artifacts
Here is a pseudo-code example of all the options this plugin provides.
Here is an example of the primary reason why this plugin is useful.
```xml
<project>
@@ -182,7 +21,6 @@ Here is a pseudo-code example of all the options this plugin provides.
<extensions>true</extensions>
<configuration>
<import>
<file>project-file.xml</file>
<url>https://host:port/path/file.xml</url>
<artifact>groupId:artifactId:version</artifact>
</import>
@@ -216,9 +54,9 @@ Here is a pseudo-code example of all the options this plugin provides.
</project>
```
### Purge Banned Artifacts
The `extensions` elements is critical. Without it, the plugin does nothing for banning artifacts/dependencies. With it, the plugin is able to not only detect ban artifacts, but do it before they are downloaded. This keeps libraries from even reaching your local Maven repository cache.
Here is an example of the non-extension use case for the plugin. You could use the same plugin for both preventing banned artifacts and cleaning up previously downloaded ones. Just set `extensions` to `true` in those cases, as highlighted in the previous section.
Here is an example of the non-extension use case for the plugin:
```xml
<project>
@@ -231,6 +69,7 @@ Here is an example of the non-extension use case for the plugin. You could use
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>...</version>
<extensions>true</extensions>
<configuration>
...
</configuration>
@@ -250,3 +89,32 @@ Here is an example of the non-extension use case for the plugin. You could use
</project>
```
The `purge-repo` goal will remove all banned artifacts from your local Maven cache. It does not support `groupIdRegex` or blank `groupId` specifications. So any of those will not be purged/removed.
## Configuration
If no `includes` are provided, then no artifacts will be banned. An *included* artifact is a banned artifact. An *excluded* artifact is not banned. It is the opposite of what you may think. If no `excludes` are provided, then no banned artifacts are granted an exception.
The `artifact` element supports the descriptive `groupId`/`artifactId`/`version` elements or the abbreviated colon-based notation. When using the colon-based notation, the group ID and artifact ID are treated as `groupIdRegex` and `artifactIdRegex` (see below).
If `groupId` or `artifactId` or `version` are not provided, they are ignored in the matching process. So it will match all applicable artifacts and the constraint will be only for what was specified. This means that `<includes><artifact>:</artifact></includes>` will ban every artifact and all their versions.
If `groupId` and `groupIdRegex` are both provided, only `groupId` is used. The same is true for `artifactId` and `artifactIdRegex`. The `*Regex` element values use standard Java regular expression parsing. If using regular expressions, remember to escape the dots (`\.`) in group IDs. If you do use `groupIdRegex` or use regular expressions in the colon-notation, the matching artifacts will not be purged using the `purge-repo` goal. So if you intend to use that goal, group ID regular expression matching needs be avoided.
The `version` element supports the standard Maven specification. You can match a specific version like `1.0.0`. Or you can match all versions before `1.2.17` like `[,1.2.17)`. You can match all future versions after `1.2.17` (inclusive) with `[1.2.17,)`.
There is nothing stopping you from specifying two `artifact` elements with the exact same values. So you can ban multiple version ranges of the same artifact by using multiple `artifact` elements.
If you *include* all versions by omitting the `version` element, you can still *exclude* (un-ban) certain versions, like `[1.2.17,)`.
Order does not matter. All include specifications are processed, followed by all exclude specifications.
## Import
The `import` URL and artifact are to reference XML files that conform to the same `configuration` element as described here. In fact, the root elmenet of that XML should be `configuration`. It will only support the `includes` and `excludes` elements. so you cannot do recursive imports.
You can create a Maven `pom` packaging type project that deploys a configuration XML to your Maven repository. Then use an `import` to allow you to change banned dependencies without making changes to each individual project. Just like with the `version` notation in the `includes` and `excludes` elements, your `import` `artifact` element supports a version range. This way the latest banned dependencies can be side-loaded into all projects. This means previously functioning builds may eventually start failing. That is by design in this scenario.
The `import` elements supports multiple `url` or `artifact` declarations. All imported and directly specified include specifications are processed before all exclude specifications. You cannot change an include when importing, but you can add new ones, that may cover more versions; and you can exclude versions that may have been included by the import.
The `excludes` element is a way to provide project-by-project exceptions to imported banned artifacts where warranted.

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<includes>
<!-- CVE-2019-17571 -->
<artifact>org.apache.logging.log4j::[,2.17.1)</artifact>
<artifact>log4j:log4j</artifact>
</includes>
</configuration>

View File

@@ -1,85 +0,0 @@
<?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-config</artifactId>
<packaging>pom</packaging>
<!-- A monthly cadence is reasonable at most organizations -->
<version>2025.03</version>
<name>Banned Artifact Configuration</name>
<build>
<resources>
<resource>
<directory>.</directory>
<includes>
<include>*-config.xml</include>
</includes>
<targetPath>${project.build.directory}</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<id>purge-maven-repo</id>
<phase>clean</phase>
<goals><goal>purge-repo</goal></goals>
<configuration>
<import>
<file>ban-config.xml</file>
</import>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>filter</id>
<goals><goal>resources</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<configuration>
<skip>true</skip>
<file>${project.build.directory}/ban-config.xml</file>
</configuration>
<executions>
<execution>
<id>install-xml</id>
<phase>install</phase>
<goals><goal>install-file</goal></goals>
<configuration>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<packaging>xml</packaging>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<file>ban-config.xml</file>
<pomFile>pom.xml</pomFile>
<packaging>xml</packaging>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,31 +0,0 @@
<?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>governed-artifact</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Any Governed Artifact</name>
<build>
<plugins>
<plugin>
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>1.4.0</version>
<extensions>true</extensions>
<configuration>
<import>
<artifact>com.inteligr8:ban-config:[2025.03,)</artifact>
</import>
</configuration>
</plugin>
</plugins>
</build>
</project>

68
pom.xml
View File

@@ -7,11 +7,11 @@
<groupId>com.inteligr8</groupId>
<artifactId>ban-maven-plugin</artifactId>
<version>1.4-SNAPSHOT</version>
<version>1.3.1</version>
<packaging>maven-plugin</packaging>
<name>Ban Artifacts Maven Plugin</name>
<description>A Maven plugin for banning dependencies and plugins from being downloaded or used</description>
<name>Ban Dependencies Maven Plugin</name>
<description>A Maven plugin for banning dependencies from being downloaded or used</description>
<url>https://bitbucket.org/inteligr8/ban-maven-plugin</url>
<licenses>
@@ -22,9 +22,9 @@
</licenses>
<scm>
<connection>scm:git:https://git.inteligr8.com:inteligr8/ban-maven-plugin.git</connection>
<developerConnection>scm:git:git@git.inteligr8.com:inteligr8/ban-maven-plugin.git</developerConnection>
<url>https://git.inteligr8.com/inteligr8/ban-maven-plugin</url>
<connection>scm:git:https://bitbucket.org/inteligr8/ban-maven-plugin.git</connection>
<developerConnection>scm:git:git@bitbucket.org:inteligr8/ban-maven-plugin.git</developerConnection>
<url>https://bitbucket.org/inteligr8/ban-maven-plugin</url>
</scm>
<organization>
<name>Inteligr8</name>
@@ -43,14 +43,14 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.version>3.9.9</maven.version>
<maven.version>3.9.0</maven.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
@@ -66,7 +66,7 @@
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.15.1</version>
<version>3.7.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -88,43 +88,18 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.12.0</version>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.15.1</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version>
</plugin>
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
<version>0.9.0.M2</version>
</plugin>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>2.2.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.7.1</version>
<executions>
<execution>
<id>default-descriptor</id>
@@ -146,6 +121,7 @@
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
<version>0.3.5</version>
<executions>
<execution>
<id>generate-index</id>
@@ -158,6 +134,7 @@
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<goals>
@@ -168,6 +145,7 @@
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.4.0</version>
<configuration>
<projectsDirectory>${basedir}/src/it</projectsDirectory>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
@@ -203,6 +181,7 @@
<plugins>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<id>run-its</id>
@@ -222,7 +201,7 @@
</build>
</profile>
<profile>
<id>central-publish</id>
<id>ossrh-release</id>
<build>
<plugins>
<plugin>
@@ -259,13 +238,14 @@
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.8.0</version>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>

View File

@@ -18,7 +18,6 @@ import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,42 +43,11 @@ import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
/**
* This class handles the parsing of the configuration specification supported
* by this plugin. It does not implement the banning execution; just the
* collection of configurations to decide what artifacts should be banned.
*
* The specification is as follows:
*
* ```xml
* &lt;configuration&gt;
* &lt;import&gt;
* &lt;file&gt;relative/path/file.xml&lt;/file&gt;
* &lt;url&gt;https://host.domain/path/file.xml&lt;/url&gt;
* &lt;artifact&gt;domain.host:artifact-id:[1.0,)&lt;/artifact&gt;
* &lt;/import&gt;
* &lt;includes&gt;
* &lt;artifact&gt;log4j:log4j&lt;/artifact&gt;
* &lt;artifact&gt;org.apache.logging:log4j-impl:[,2.16.1)&lt;/artifact&gt;
* &lt;/includes&gt;
* &lt;excludes&gt;
* &lt;artifact&gt;...&lt;/artifact&gt;
* &lt;/excludes&gt;
* &lt;/configuration&gt;
* ```
*
* The imports are recursively processed under the same specification. The
* included artifacts are the ones that are banned. Any artifact matching both
* includes and excludes will be excluded and therefore NOT banned.
*
* The `groupId` and `artifactId` of the standard shorthand Maven `artifact`
* nomenclature may use regular expressions.
*/
public abstract class AbstractBanConfiguration implements BanConfiguration {
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 Pattern notRegexPattern = Pattern.compile("^[A-Za-z0-9_\\.]*$");
protected final List<ArtifactFilter> includeArtifacts = new LinkedList<>();
protected final List<ArtifactFilter> excludeArtifacts = new LinkedList<>();
@@ -95,122 +63,53 @@ public abstract class AbstractBanConfiguration implements BanConfiguration {
}
public void init(Xpp3Dom rootDom) throws IOException, MojoFailureException {
this.init(rootDom, null);
}
public void init(Xpp3Dom rootDom, Properties userProperties) throws IOException, MojoFailureException {
if (userProperties != null) {
if (userProperties.containsKey("ban.file"))
this.processFileImport(StringUtils.trimToNull(userProperties.getProperty("ban.file")));
if (userProperties.containsKey("ban.url"))
this.processUrlImport(StringUtils.trimToNull(userProperties.getProperty("ban.url")));
if (userProperties.containsKey("ban.artifact"))
this.processArtifactImport(StringUtils.trimToNull(userProperties.getProperty("ban.artifact")));
}
if (rootDom != null) {
Xpp3Dom importDom = rootDom.getChild("import");
if (importDom != null)
this.processImports(importDom);
this.processIncludesExcludes(rootDom);
}
if (rootDom == null)
return;
Xpp3Dom importDom = rootDom.getChild("import");
if (importDom != null)
this.processImports(importDom);
this.processIncludesExcludes(rootDom);
}
private void processImports(Xpp3Dom importDom) throws IOException, MojoFailureException {
for (Xpp3Dom child : importDom.getChildren()) {
if (child.getName().equals("file")) {
this.processFileImport(StringUtils.trimToNull(child.getValue()));
} else if (child.getName().equals("url")) {
this.processUrlImport(StringUtils.trimToNull(child.getValue()));
BanConfigurationDownloader downloader = null;
if (child.getName().equals("url")) {
String url = StringUtils.trimToNull(child.getValue());
downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, url);
} else if (child.getName().equals("artifact")) {
this.processArtifactImport(child.getValue());
} else {
this.logger.debug("Unrecognized configuration element ignored: {}: {}", child.getName(), child.getValue());
Artifact artifact = new DefaultArtifact(child.getValue());
if (!"xml".equals(artifact.getExtension()))
artifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), "xml", artifact.getVersion());
VersionRangeRequest vrrequest = new VersionRangeRequest(artifact, this.session.getCurrentProject().getRemoteProjectRepositories(), null);
try {
VersionRangeResult vrresult = this.versionRangeResolver.resolveVersionRange(this.session.getRepositorySession(), vrrequest);
if (vrresult.getVersions().isEmpty()) {
this.logger.error("The artifact version range could not be resolved; skipping: {}", child.getValue());
} else {
Version version = vrresult.getHighestVersion();
artifact = artifact.setVersion(version.toString());
ArtifactRequest arequest = new ArtifactRequest(artifact, this.session.getCurrentProject().getRemoteProjectRepositories(), null);
try {
ArtifactResult aresult = this.artifactResolver.resolveArtifact(this.session.getRepositorySession(), arequest);
File file = aresult.getArtifact().getFile();
downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
} catch (ArtifactResolutionException are) {
this.logger.warn("The artifact version could not be resolved; skipping: {}", artifact, version);
}
}
} catch (VersionRangeResolutionException vrre) {
this.logger.error("The artifact version range could not be resolved; skipping: {}", child.getValue());
}
}
}
}
private BanConfigurationDownloader getFileDownloader(String filename) throws IOException, MojoFailureException {
File file = new File(filename);
return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
}
private BanConfigurationDownloader getUrlDownloader(String url) throws IOException, MojoFailureException {
url = StringUtils.trimToNull(url);
return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, url);
}
private BanConfigurationDownloader getArtifactDownloader(Artifact artifact, String logId) throws IOException, MojoFailureException {
if (!"xml".equals(artifact.getExtension()))
artifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getClassifier(), "xml", artifact.getVersion());
Version latestVersion = this.findLatestVersion(artifact, logId);
Artifact latestArtifact = this.findLatestArtifact(artifact, logId);
if (latestArtifact == null && latestVersion != null) {
this.logger.debug("A latest version was found, but could not resolve the artifact using the range; trying to resolve the artifact with the specific version: {}: {}", latestVersion, logId);
artifact = artifact.setVersion(latestVersion.toString());
latestArtifact = this.findLatestArtifact(artifact, logId);
}
if (latestArtifact != null && latestArtifact.getFile() != null) {
this.logger.debug("The latest artifact was found: {}", latestArtifact);
File file = latestArtifact.getFile();
return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
} else if (artifact != null) {
File file = artifact.getFile();
return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
} else {
return null;
}
}
private void processFileImport(String filename) throws IOException, MojoFailureException {
BanConfigurationDownloader downloader = this.getFileDownloader(filename);
this.includeArtifacts.addAll(downloader.getIncludeArtifacts());
this.excludeArtifacts.addAll(downloader.getExcludeArtifacts());
}
private void processUrlImport(String url) throws IOException, MojoFailureException {
BanConfigurationDownloader downloader = this.getUrlDownloader(url);
this.includeArtifacts.addAll(downloader.getIncludeArtifacts());
this.excludeArtifacts.addAll(downloader.getExcludeArtifacts());
}
private void processArtifactImport(String artifactSpec) throws IOException, MojoFailureException {
Artifact artifact = new DefaultArtifact(artifactSpec);
BanConfigurationDownloader downloader = this.getArtifactDownloader(artifact, artifactSpec);
this.includeArtifacts.addAll(downloader.getIncludeArtifacts());
this.excludeArtifacts.addAll(downloader.getExcludeArtifacts());
}
private Version findLatestVersion(Artifact artifact, String logId) {
this.logger.trace("Inspecting the local and remote repositories to select the version to import: {}", logId);
VersionRangeRequest vrrequest = new VersionRangeRequest(artifact, this.session.getCurrentProject().getRemoteProjectRepositories(), null);
try {
VersionRangeResult vrresult = this.versionRangeResolver.resolveVersionRange(this.session.getRepositorySession(), vrrequest);
if (vrresult.getVersions().isEmpty()) {
this.logger.info("The artifact version range could not be resolved: {}", logId);
return null;
} else {
this.logger.debug("The artifact version discovered: {}: {}", vrresult.getHighestVersion(), logId);
return vrresult.getHighestVersion();
if (downloader != null) {
this.includeArtifacts.addAll(downloader.getIncludeArtifacts());
this.excludeArtifacts.addAll(downloader.getExcludeArtifacts());
}
} catch (VersionRangeResolutionException vrre) {
this.logger.error("The artifact version range could not be resolved; skipping: {}", logId);
return null;
}
}
private Artifact findLatestArtifact(Artifact artifact, String logId) {
this.logger.trace("Inspecting the local/remote repositories to select the artifact to import: {}", logId);
ArtifactRequest arequest = new ArtifactRequest(artifact, this.session.getCurrentProject().getRemoteProjectRepositories(), null);
try {
ArtifactResult aresult = this.artifactResolver.resolveArtifact(this.session.getRepositorySession(), arequest);
this.logger.debug("This artifact version discovered: {}: {}", aresult.getArtifact().getVersion(), logId);
return aresult.getArtifact();
} catch (ArtifactResolutionException are) {
this.logger.warn("The artifact could not be resolved; skipping: {}", artifact);
return null;
}
}

View File

@@ -31,24 +31,10 @@ import org.eclipse.aether.impl.VersionRangeResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class parses the a configuration file that conforms with the
* specification defined by this plugin.
*/
public class BanConfigurationDownloader extends AbstractBanConfiguration {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* This constructs the downloader based on an accessible URL.
*
* @param session A Maven session object.
* @param artifactResolver A Maven Aether artifact resolver service.
* @param versionRangeResolver A Maven Aether version range resolver service.
* @param url The URL of a ban configuration file.
* @throws IOException The file could not be downloaded for any number of reasons.
* @throws MojoFailureException The file could not be parsed due to bad formatting or unexpected response.
*/
public BanConfigurationDownloader(MavenSession session, ArtifactResolver artifactResolver, VersionRangeResolver versionRangeResolver, String url) throws IOException, MojoFailureException {
super(session, artifactResolver, versionRangeResolver);
@@ -59,17 +45,7 @@ public class BanConfigurationDownloader extends AbstractBanConfiguration {
throw new MojoFailureException(xppe.getMessage(), xppe);
}
}
/**
* This constructs the downloader based on a file object.
*
* @param session A Maven session object.
* @param artifactResolver A Maven Aether artifact resolver service.
* @param versionRangeResolver A Maven Aether version range resolver service.
* @param file A file, typically local, though technically may be remote.
* @throws IOException The file could not be read for any number of reasons.
* @throws MojoFailureException The file could not be parsed due to bad formatting.
*/
public BanConfigurationDownloader(MavenSession session, ArtifactResolver artifactResolver, VersionRangeResolver versionRangeResolver, File file) throws IOException, MojoFailureException {
super(session, artifactResolver, versionRangeResolver);
@@ -81,14 +57,6 @@ public class BanConfigurationDownloader extends AbstractBanConfiguration {
}
}
/**
* This method loads the configuration from the specified URL.
*
* @param url A URL of a ban configuration file.
* @return A Maven Plexus XML DOM object.
* @throws IOException The file could not be downloaded for any number of reasons.
* @throws MojoFailureException The file could not be parsed due to bad formatting or unexpected response.
*/
private Xpp3Dom load(URL url) throws IOException, XmlPullParserException {
InputStream istream = url.openStream();
BufferedInputStream bistream = new BufferedInputStream(istream, 16384);
@@ -99,15 +67,7 @@ public class BanConfigurationDownloader extends AbstractBanConfiguration {
bistream.close();
}
}
/**
* This method loads the configuration from the specified file.
*
* @param file A file, typically local, though technically may be remote.
* @return A Maven Plexus XML DOM object.
* @throws IOException The file could not be read for any number of reasons.
* @throws MojoFailureException The file could not be parsed due to bad formatting.
*/
private Xpp3Dom load(File file) throws IOException, XmlPullParserException {
FileInputStream fistream = new FileInputStream(file);
BufferedInputStream bistream = new BufferedInputStream(fistream, 16384);

View File

@@ -23,18 +23,13 @@ import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.impl.ArtifactResolver;
import org.eclipse.aether.impl.VersionRangeResolver;
/**
* This class parses the POM plugin configuration block for this plugin. The
* specification for that configuration block is shared with the one found in
* the ban configuration file.
*/
public class BanPluginConfigurationParser extends AbstractBanConfiguration {
public class BanConfigurationParser extends AbstractBanConfiguration {
public BanPluginConfigurationParser(MavenSession session, ArtifactResolver artifactResolver, VersionRangeResolver versionRangeResolver, Plugin plugin) throws IOException, MojoFailureException {
public BanConfigurationParser(MavenSession session, ArtifactResolver artifactResolver, VersionRangeResolver versionRangeResolver, Plugin plugin) throws IOException, MojoFailureException {
super(session, artifactResolver, versionRangeResolver);
Xpp3Dom rootDom = plugin == null ? null : (Xpp3Dom) plugin.getConfiguration();
this.init(rootDom, session.getUserProperties());
Xpp3Dom rootDom = (Xpp3Dom) plugin.getConfiguration();
this.init(rootDom);
}
}

View File

@@ -28,18 +28,6 @@ import org.slf4j.LoggerFactory;
import com.inteligr8.maven.model.ArtifactFilter;
/**
* This class implements the banned artifact detection logic.
*
* It will recursively scan every dependency and plugin dependency to find any
* artifacts that reference a banned artifact and its banned versions. If one
* is found, the filter will fail and stop the build immediately. It will
* prevent the banned artifact from being downloaded.
*
* The complicated part is that when multiple versions of the same artifact
* show up in the dependency tree, only one may be selected. So this filter
* will ignore the versions that are not selected.
*/
public class BanDependencyFilter implements DependencyFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());

View File

@@ -29,7 +29,6 @@ import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.internal.PluginDependenciesResolver;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.DefaultDependencyResolutionRequest;
import org.apache.maven.project.DependencyResolutionException;
import org.apache.maven.project.DependencyResolutionResult;
@@ -45,8 +44,6 @@ import org.eclipse.aether.impl.VersionRangeResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.inteligr8.maven.ban.model.ImportConfig;
@Named("ban")
@Singleton
public class BanExtension extends AbstractMavenLifecycleParticipant {
@@ -68,18 +65,6 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
@Inject
private PluginDependenciesResolver pluginDepResolver;
/**
* The configuration is parsed manually. This is here to prevent warning messages with IDEs and builders.
*/
@Parameter(name = "import")
private ImportConfig importConfig;
@Parameter(name = "includes")
private List<String> includes;
@Parameter(name = "excludes")
private List<String> excludes;
@Override
public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
@@ -87,9 +72,7 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
if (config == null)
return;
BanDependencyFilter depFilter = new BanDependencyFilter(
config.getIncludeArtifacts(),
config.getExcludeArtifacts());
BanDependencyFilter depFilter = new BanDependencyFilter(config.getIncludeArtifacts(), config.getExcludeArtifacts());
depFilter.setFailFast(true);
MavenProject project = session.getCurrentProject();
@@ -134,7 +117,7 @@ public class BanExtension extends AbstractMavenLifecycleParticipant {
return null;
} else {
try {
return new BanPluginConfigurationParser(session, this.artifactResolver, this.versionRangeResolver, plugin);
return new BanConfigurationParser(session, this.artifactResolver, this.versionRangeResolver, plugin);
} catch (IOException | MojoFailureException e) {
throw new MavenExecutionException(e.getMessage(), project.getFile());
}

View File

@@ -16,7 +16,6 @@ package com.inteligr8.maven.ban;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
@@ -42,7 +41,6 @@ import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
@@ -52,12 +50,7 @@ import org.eclipse.aether.impl.VersionRangeResolver;
import com.inteligr8.maven.model.ArtifactFilter;
@Mojo(
name = "purge-repo",
threadSafe = true,
defaultPhase = LifecyclePhase.CLEAN,
requiresProject = false
)
@Mojo( name = "purge-repo", threadSafe = true )
@Component( role = org.apache.maven.plugin.Mojo.class )
public class PurgeRepoMojo extends AbstractMojo {
@@ -70,15 +63,12 @@ public class PurgeRepoMojo extends AbstractMojo {
@Inject
private VersionRangeResolver versionRangeResolver;
@Parameter(name = "skip", defaultValue = "false", property = "ban.skip")
@Parameter(name = "skip", defaultValue = "false")
private boolean skip = false;
@Parameter(name = "dryRun", defaultValue = "false", property = "ban.dryRun")
@Parameter(name = "dryRun", defaultValue = "false")
private boolean dryRun = false;
@Parameter(name = "eager", defaultValue = "false", property = "ban.eager")
private boolean eager = false;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.skip) {
@@ -97,7 +87,7 @@ public class PurgeRepoMojo extends AbstractMojo {
private void purge() throws MojoFailureException, IOException {
List<Path> includePaths = new LinkedList<>();
BanPluginConfigurationParser config = this.getConfiguration();
BanConfigurationParser config = this.getConfiguration();
for (ArtifactFilter afilter : config.getIncludeArtifacts()) {
if (afilter.getGroupId() == null) {
@@ -168,23 +158,25 @@ public class PurgeRepoMojo extends AbstractMojo {
this.getLog().info("DRYRUN: Would have deleted certain paths from local Maven cache: " + repoPath);
this.getLog().info("DRYRUN: Would have deleted these paths: " + includePaths);
} else {
for (Path versionPath : includePaths) {
Path fullVersionPath = repoPath.resolve(versionPath);
if (Files.exists(fullVersionPath)) {
this.getLog().info("Deleting version from Maven cache: " + versionPath);
Files.walkFileTree(fullVersionPath, new DeleteArtifactVisitor(this.eager));
for (Path path : includePaths) {
Path fullpath = repoPath.resolve(path);
if (Files.exists(fullpath)) {
this.getLog().info("Deleting version from Maven cache: " + path);
Files.walkFileTree(fullpath, new DeleteDirectoryVisitor());
} else {
// this will probably never happen
this.getLog().debug("Maven cache does not exist: " + versionPath);
this.getLog().debug("Maven cache does not exist: " + path);
}
}
}
}
private BanPluginConfigurationParser getConfiguration() throws MojoFailureException, IOException {
private BanConfigurationParser getConfiguration() throws MojoFailureException, IOException {
MavenProject project = this.session.getCurrentProject();
Plugin plugin = project.getPlugin(BanExtension.THIS_PLUGIN_KEY);
return new BanPluginConfigurationParser(this.session, this.artifactResolver, this.versionRangeResolver, plugin);
if (plugin == null)
throw new MojoFailureException("The plugin is executing but it cannot be found");
return new BanConfigurationParser(this.session, this.artifactResolver, this.versionRangeResolver, plugin);
}
private Path resolveGroupPath(ArtifactFilter afilter) {
@@ -227,27 +219,14 @@ public class PurgeRepoMojo extends AbstractMojo {
Files.list(repoPath.resolve(groupPath)).forEach(new Consumer<Path>() {
@Override
public void accept(Path fullArtifactPath) {
public void accept(Path t) {
if (artifactPattern == null) {
// these may include sub-groups and not just artifacts
// which will lead to paths with artifacts as versions
// so we are looping through versions to see if it is indeed an artifact
try {
Files.list(fullArtifactPath).findFirst().ifPresent(new Consumer<Path>() {
@Override
public void accept(Path fullVersionPath) {
if (Files.exists(fullVersionPath.resolve("_remote.repositories")))
paths.add(repoPath.relativize(fullArtifactPath));
}
});
} catch (IOException ie) {
getLog().error(ie.getMessage(), ie);
}
paths.add(repoPath.relativize(t));
} else {
Matcher matcher = artifactPattern.matcher(fullArtifactPath.getFileName().toString());
Matcher matcher = artifactPattern.matcher(t.getFileName().toString());
if (matcher.matches()) {
getLog().debug("The artifact directory '" + fullArtifactPath.getFileName() + "' qualifies as included");
paths.add(repoPath.relativize(fullArtifactPath));
getLog().debug("The artifact directory '" + t.getFileName() + "' qualifies as included");
paths.add(repoPath.relativize(t));
}
}
}
@@ -287,15 +266,7 @@ public class PurgeRepoMojo extends AbstractMojo {
private class DeleteArtifactVisitor implements FileVisitor<Path> {
private final Pattern versionPathPattern = Pattern.compile("/([^/]+)/([^/]+)/[^/]+$");
private final boolean eager;
public DeleteArtifactVisitor(boolean eager) {
this.eager = eager;
}
private class DeleteDirectoryVisitor implements FileVisitor<Path> {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
@@ -304,33 +275,12 @@ public class PurgeRepoMojo extends AbstractMojo {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isDirectory()) {
getLog().warn("An unexpected directory was found: " + file);
return FileVisitResult.SKIP_SUBTREE;
}
Matcher matcher = this.versionPathPattern.matcher(file.toString());
if (!matcher.find()) {
getLog().debug("Ignoring file from purge: " + file);
return FileVisitResult.CONTINUE;
}
String artifactId = matcher.group(1);
String version = matcher.group(2);
String includeName = artifactId + "-" + version;
String excludeName = artifactId + "-" + version + ".pom";
if (this.eager ||
file.getFileName().toString().startsWith(includeName) &&
!file.getFileName().toString().startsWith(excludeName)) {
try {
getLog().info("Deleting: " + file);
try {
if (!attrs.isDirectory())
Files.delete(file);
} catch (IOException ie) {
getLog().debug(ie);
getLog().warn("The file failed to delete: " + file);
return FileVisitResult.SKIP_SIBLINGS;
}
} catch (IOException ie) {
getLog().debug(ie);
getLog().warn("The file failed to delete: " + file);
}
return FileVisitResult.CONTINUE;
@@ -345,8 +295,6 @@ public class PurgeRepoMojo extends AbstractMojo {
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
try {
Files.delete(dir);
} catch (DirectoryNotEmptyException dnee) {
getLog().debug("The folder will not be deleted as it is not empty: " + dir);
} catch (IOException ie) {
getLog().debug(ie);
getLog().warn("The folder failed to delete: " + dir);

View File

@@ -1,11 +0,0 @@
package com.inteligr8.maven.ban.model;
import java.util.List;
public class Config {
public ImportConfig importConfigs;
public List<String> includes;
public List<String> excludes;
}

View File

@@ -1,10 +0,0 @@
package com.inteligr8.maven.ban.model;
import java.util.List;
public class ImportConfig {
public List<String> url;
public List<String> artifact;
}