Commit 2c6a1c99 authored by Brian Long's avatar Brian Long
Browse files

Merge branch 'develop' into stable

parents 1577bbf2 7083750b
Loading
Loading
Loading
Loading
+158 −76
Original line number Diff line number Diff line
@@ -3,11 +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.

## Usage

### Prevent Banned Artifacts
## Extension

Here is a pseudo-code example of all the options this plugin provides.
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>
@@ -22,31 +20,7 @@ Here is a pseudo-code example of all the options this plugin provides.
                <version>...</version>
                <extensions>true</extensions>
                <configuration>
                    <import>
                        <file>project-file.xml</file>
                        <url>https://host:port/path/file.xml</url>
                        <artifact>groupId:artifactId:version</artifact>
                    </import>
                    <includes>
                        <artifact>
                            <groupId>...<groupId>
                            <artifactId>...<artifactId>
                            <version>...</version>
                        </artifact>
                        <artifact>
                            <groupIdRegex>...<groupIdRegex>
                            <artifactIdRegex>...<artifactIdRegex>
                            <version>...</version>
                        </artifact>
                        <artifact>com.inteligr8:ban-maven-plugin:[,1.0.0)</artifact>
                        <artifact>log4j:log4j</artifact>
                        <artifact>org\.springframe.+::[,4.0.0.RELEASE)</artifact>
                    </includes>
                    <excludes>
                    	   <artifact>
                    	       ....
                    	   </artifact>
                    </excludes>
                	...
                </configuration>
            </plugin>
            ...
@@ -57,60 +31,47 @@ Here is a pseudo-code example of all the options this plugin provides.
</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 works with both dependencies and plugins.  This keeps libraries from even reaching your local Maven repository cache.
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.

### Purge Banned Artifacts
### Configuration

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.
| 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
<project>
    ...
    <build>
        ...
        <plugins>
            ...
            <plugin>
<artifact>
	<groupId>com.inteligr8</groupId>
	<artifactId>ban-maven-plugin</artifactId>
                <version>...</version>
                <configuration>
                    ...
                </configuration>
                <executions>
                    <execution>
                        <id>clean</id>
                        <phase>clean</phase>
                        <goals><goal>purge-repo</goal></goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
        ...
    </build>
    ...
</project>
	<version>[,1.4.0)</version>
</artifact>
```

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.

For instance, you can use the following and expect it to work for preventing and purging banned dependencies and plugins:
It supports the use of regular expressions with `groupIdRegex` and `artifactIdRegex`.

```xml
<include>
<artifact>
        <groupId>...<groupId>
        <artifactId>...<artifactId>
        <version>...</version>
	<groupId>com.inteligr8</groupId>
	<artifactIdRegex>ban-.+</artifactIdRegex>
	<version>[,1.4.0)</version>
</artifact>
    <artifact>com.inteligr8:ban-maven-plugin:[,1.0.0)</artifact>
    <artifact>log4j:log4j</artifact>
</include>
```

## 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 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.
@@ -127,9 +88,9 @@ If you *include* all versions by omitting the `version` element, you can still *

Order does not matter.  All include specifications are processed, followed by all exclude specifications.

## Import
### 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 elmenet of that XML should be `configuration`.  It will only support the `includes` and `excludes` elements. so you cannot do recursive imports.
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.

@@ -137,7 +98,7 @@ The `import` elements supports multiple `url` or `artifact` declarations. All i

The `excludes` element is a way to provide project-by-project exceptions to imported banned artifacts where warranted.

## Examples
### 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.

@@ -168,3 +129,124 @@ Once you have that in place, you will want to add the following to every single
    </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.

```xml
<project>
    ...
    <build>
        ...
        <plugins>
            ...
            <plugin>
                <groupId>com.inteligr8</groupId>
                <artifactId>ban-maven-plugin</artifactId>
                <version>...</version>
                <extensions>true</extensions>
                <configuration>
                    <import>
                        <file>project-file.xml</file>
                        <url>https://host:port/path/file.xml</url>
                        <artifact>groupId:artifactId:version</artifact>
                    </import>
                    <includes>
                        <artifact>
                            <groupId>...<groupId>
                            <artifactId>...<artifactId>
                            <version>...</version>
                        </artifact>
                        <artifact>
                            <groupIdRegex>...<groupIdRegex>
                            <artifactIdRegex>...<artifactIdRegex>
                            <version>...</version>
                        </artifact>
                        <artifact>com.inteligr8:ban-maven-plugin:[,1.0.0)</artifact>
                        <artifact>log4j:log4j</artifact>
                        <artifact>org\.springframe.+::[,4.0.0.RELEASE)</artifact>
                    </includes>
                    <excludes>
                    	   <artifact>
                    	       ....
                    	   </artifact>
                    </excludes>
                </configuration>
            </plugin>
            ...
        </plugins>
        ...
    </build>
    ...
</project>
```

### Purge Banned Artifacts

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.

```xml
<project>
    ...
    <build>
        ...
        <plugins>
            ...
            <plugin>
                <groupId>com.inteligr8</groupId>
                <artifactId>ban-maven-plugin</artifactId>
                <version>...</version>
                <configuration>
                    ...
                </configuration>
                <executions>
                    <execution>
                        <id>clean</id>
                        <phase>clean</phase>
                        <goals><goal>purge-repo</goal></goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
        ...
    </build>
    ...
</project>
```
+24 −17
Original line number Diff line number Diff line
@@ -10,8 +10,8 @@
	<version>1.4.1</version>
	<packaging>maven-plugin</packaging>

	<name>Ban Dependencies Maven Plugin</name>
	<description>A Maven plugin for banning dependencies from being downloaded or used</description>
	<name>Ban Artifacts Maven Plugin</name>
	<description>A Maven plugin for banning dependencies and plugins 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://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>
		<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>
	</scm>
	<organization>
		<name>Inteligr8</name>
@@ -88,9 +88,9 @@
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.13.2</version>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>5.12.0</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
@@ -106,6 +106,16 @@
					<artifactId>maven-invoker-plugin</artifactId>
					<version>3.9.0</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>
@@ -132,7 +142,6 @@
			<plugin>
				<groupId>org.eclipse.sisu</groupId>
				<artifactId>sisu-maven-plugin</artifactId>
				<version>0.3.5</version>
				<executions>
					<execution>
						<id>generate-index</id>
@@ -145,7 +154,6 @@
			<plugin>
				<groupId>org.codehaus.plexus</groupId>
				<artifactId>plexus-component-metadata</artifactId>
				<version>2.2.0</version>
				<executions>
					<execution>
						<goals>
@@ -210,7 +218,7 @@
			</build>
		</profile>
		<profile>
			<id>ossrh-release</id>
			<id>central-publish</id>
			<build>
				<plugins>
					<plugin>
@@ -247,14 +255,13 @@
						</executions>
					</plugin>
					<plugin>
						<groupId>org.sonatype.plugins</groupId>
						<artifactId>nexus-staging-maven-plugin</artifactId>
						<version>1.7.0</version>
						<groupId>org.sonatype.central</groupId>
						<artifactId>central-publishing-maven-plugin</artifactId>
						<version>0.8.0</version>
						<extensions>true</extensions>
						<configuration>
							<serverId>ossrh</serverId>
							<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
							<autoReleaseAfterClose>true</autoReleaseAfterClose>
							<publishingServerId>central</publishingServerId>
							<autoPublish>true</autoPublish>
						</configuration>
					</plugin>
				</plugins>
+107 −37
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ 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;

@@ -43,6 +44,37 @@ 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
 * <configuration>
 *   <import>
 *      <file>relative/path/file.xml</file>
 *      <url>https://host.domain/path/file.xml</url>
 *      <artifact>domain.host:artifact-id:[1.0,)</artifact>
 *   </import>
 *   <includes>
 *      <artifact>log4j:log4j</artifact>
 *      <artifact>org.apache.logging:log4j-impl:[,2.16.1)</artifact>
 *   </includes>
 *   <excludes>
 *      <artifact>...</artifact>
 *   </excludes>
 * </configuration>
 * ```
 * 
 * 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());
@@ -63,54 +95,92 @@ public abstract class AbstractBanConfiguration implements BanConfiguration {
    }
    
    public void init(Xpp3Dom rootDom) throws IOException, MojoFailureException {
        if (rootDom == null)
            return;
        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);
        }
    }
    
    private void processImports(Xpp3Dom importDom) throws IOException, MojoFailureException {
        for (Xpp3Dom child : importDom.getChildren()) {
            BanConfigurationDownloader downloader = null;
            if (child.getName().equals("file")) {
                File file = new File(StringUtils.trimToNull(child.getValue()));
                downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
                this.processFileImport(StringUtils.trimToNull(child.getValue()));
            } else if (child.getName().equals("url")) {
                String url = StringUtils.trimToNull(child.getValue());
                downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, url);
                this.processUrlImport(StringUtils.trimToNull(child.getValue()));
            } else if (child.getName().equals("artifact")) {
                Artifact artifact = new DefaultArtifact(child.getValue());
                this.processArtifactImport(child.getValue());
            } else {
                this.logger.debug("Unrecognized configuration element ignored: {}: {}", child.getName(), 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, child.getValue());
                Artifact latestArtifact = this.findLatestArtifact(artifact, child.getValue());
        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, child.getValue());
            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, child.getValue());
            latestArtifact = this.findLatestArtifact(artifact, logId);
        }
        
        if (latestArtifact != null && latestArtifact.getFile() != null) {
            this.logger.debug("The latest artifact was found: {}", latestArtifact);
            File file = latestArtifact.getFile();
                    downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
            return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
        } else if (artifact != null) {
            File file = artifact.getFile();
                    downloader = new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
                }
            return new BanConfigurationDownloader(this.session, this.artifactResolver, this.versionRangeResolver, file);
        } else {
                this.logger.debug("Unrecognized configuration element ignored: {}: {}", child.getName(), child.getValue());
            return null;
        }
    }
    
            if (downloader != 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) {
+42 −2

File changed.

Preview size limit exceeded, changes collapsed.

+12 −0
Original line number Diff line number Diff line
@@ -28,6 +28,18 @@ 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());
Loading