Complete Guide to Maven Enforcer Plugin
Maven Enforcer Plugin is a plugin that helps you enforce rules such as which dependencies to use in your project, avoiding duplicate dependencies, banned dependencies and enforcing restrictions on transitive dependencies.
The plugin also provides goals to control certain environmental constraints such as Maven version, JDK version and OS family along with many more built-in rules and user created rules
This blog post will demonstrate how to use Maven Enforcer plugin in your project.
Log4j security vulnerabilities
In the month of December 2021, Apache Software Foundation disclosed 2 critical vulnerabilities, a Denial of Service condition ( CVE-2021-45046) and Remote Code Execution( CVE-2021-44228 ) that affects the Log4j logging library from versions 2.0-beta9 to 2.16.
The development teams all around the world have updated the vulnerable lo4j dependencies to 2.17.0 in response. As time passes, it will be forgotten and and we will proceed as normal. In the future a team member might add a dependency which has transitive dependency on vulnerable log4j version or we might add vulnerable log4j version if we are creating a new project.
We require a method to ensure that developers do not add vulnerable dependencies.
Maven Enforcer Plugin
Maven enforcer plugin helps us to enforce the project rules.
I am going to demonstrate how to use below 4 rules with the help of enforcer plugin.
- Enforce Banned Dependencies
- Require Java Version
- Require Maven Version
- Dependency Convergence
Enforce Banned Dependencies
Due to security reasons or various other reasons organization or development team might have decided not to use certain jar dependencies or specific versions of jar files in the project.
We can configure enforcer plugin like below in pom.xml file to ban the selected dependencies/jars.
Below configuration fails the build if the project contain log4j-core dependencies below the version 2.17.0 in the project. It also checks for the transitive dependencies
<build>
<plugins>
....
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>block-vulnerable-log4j-versions</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>org.apache.logging.log4j:log4j-core:(,2.17.0)</exclude>
</excludes>
<searchTransitive>true</searchTransitive>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Code language: HTML, XML (xml)
[icon name=”clipboard” prefix=”fas”] Note
searchTransitive configutation is true
by default.
If a developer tries to include a banned log4j-core dependency , enforcer plugin will fail the build phase.
Banning dependencies follow the pattern of:
groupId[:artifactId][:version][:type][:scope][:classifier]
Everything after the groupId is optional. The banned dependencies also follow the normal Maven pattern matching:
- * or (*) – bans all versions of jar
<exclude>org.apache.logging.log4j:log4j-core:*</exclude>
<exclude>org.apache.logging.log4j:log4j-core:(*)</exclude>
Code language: Java (java)
<exclude>org.apache.logging.log4j:log4j-core:[2.17.0,2.17.1]</exclude>
Code language: Java (java)
<exclude>org.apache.logging.log4j:log4j-core:(,2.17.0)</exclude>
<exclude>org.apache.logging.log4j:log4j-core:(2.17.0,)</exclude>
Code language: Java (java)
<exclude>org.apache.logging.log4j:log4j-core:[2.17.0,]</exclude>
Banned dependencies rule can also be used to specify allow specific version of banned dependency in the project
<includes>
tag specifies the list of artifacts to include. These are exceptions to the excludes. It is meant to allow wide exclusion rules with wildcards and fine tune using includes. If nothing has been excluded, then the includes have no effect. To put it another way, <includes>
tag only remove artefacts that meet an exclude rule.
In below configuration, build will fail if maven dependencies include any version of log4j-core other than the 2.17.0
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>block-vulnerable-log4j-versions</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>org.apache.logging.log4j:log4j-core:*</exclude>
</excludes>
<includes>
<include>org.apache.logging.log4j:log4j-core:2.17.0</include>
</includes>
<searchTransitive>true</searchTransitive>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Code language: HTML, XML (xml)
Require Java Version
This rule enforces use certain Java JDK versions while building a project with Maven.
Below configuration will fail if Maven uses JDK version less than 18.
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>required-java-versions</id>
<phase>compile</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>18</version>
</requireJavaVersion>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Code language: HTML, XML (xml)
The RequireJavaVersion rules use the standard Maven version range syntax .Please refer to the table for rules.
Require Maven Version
This rule enforces certain Maven versions.
<build>
<plugins>
....
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>required-maven-version</id>
<phase>compile</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>(3.6,3.8)</version>
</requireMavenVersion>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Code language: HTML, XML (xml)
Dependency Convergence
Version Range Specification
The RequireMavenVersion and RequireJavaVersion rules use the below standard Maven version range syntax
Range | Meaning |
1.0 | x >= 1.0 * The default Maven meaning for 1.0 is everything (,) but with 1.0 recommended. Obviously this doesn’t work for enforcing versions here, so it has been redefined as a minimum version. |
(,1.0] | x <= 1.0 |
(,1.0) | x < 1.0 |
[1.0] | x == 1.0 |
[1.0,) | x >= 1.0 |
(1.0,) | x > 1.0 |
(1.0,2.0) | 1.0 < x < 2.0 |
[1.0,2.0] | 1.0 <= x <= 2.0 |
(,1.0],[1.2,) | x <= 1.0 or x >= 1.2. Multiple sets are comma-separated |
(,1.1),(1.1,) | x != 1.1 |
Dependency Convergence
This rule requires that dependency version numbers converge. If a project has two dependencies, A and B, both depending on the same artifact, C, this rule will fail the build if A depends on a different version of C than the version of C depended on by B.
To find out the dependency convergence issues, we should configure enforcer plugin like below
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
...
</project>
Code language: Java (java)
Here is the sample example.
If pom.xml contains
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
Code language: Java (java)
Following error message will be logged during compilation
Dependency convergence error for org.slf4j:slf4j-api1.6.1 paths to dependency are:
[ERROR]
Dependency convergence error for org.slf4j:slf4j-api:1.6.1 paths to dependency are:
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-jdk14:1.6.1
+-org.slf4j:slf4j-api:1.6.1
and
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-nop:1.6.0
+-org.slf4j:slf4j-api:1.6.0
Code language: Java (java)
To resolve the issue , we should exclude the dependency
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.6.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
Code language: Java (java)
Maven Enforcer Plugin ships with various other rules. Please check the documentation for all the rules. The plugin also provides maven-enforcer-rule-api
to write your own custom rules. You can check sample custom rule implementation from the documentation page
Adopting a Maven Enforcer Plugin will give project team members and company peace of mind and save the time and effort by enforcing predefined rules automatically.