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.

Maven Enforcer Plugin

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)

  • [2.17,2.17.1] – matches the exact version within the brackets
  •  <exclude>org.apache.logging.log4j:log4j-core:[2.17.0,2.17.1]</exclude>Code language: Java (java)

  • (,2.17.0) – matches all previous versions up to and excluding 2.17.0
  • <exclude>org.apache.logging.log4j:log4j-core:(,2.17.0)</exclude>

  • (2.17.0,) – matches all versions above 2.17.0 and excluding 2.17.0
  • <exclude>org.apache.logging.log4j:log4j-core:(2.17.0,)</exclude>Code language: Java (java)

  • [2.17.0,] – matches all versions above 2.17.0 and including 2.17.0
  • <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

    RangeMeaning
    1.0x >= 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.0Code 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.

    Similar Posts