Jenkins Pipeline: SonarQube and the OWASP Dependency-Check

Maarten Smeets

The OWASP Foundation plays an important role in helping to improve security of software worldwide. They have created a popular and well-known awareness document called the ‘OWASP Top 10‘. This document lists the following risk: using components with known vulnerabilities.

Software nowadays can be quite complex consisting of many direct and indirect dependencies. How do you know the components and versions of those components you are using in your software, do not contain known vulnerabilities? Luckily the OWASP foundation has also provided a dependency-check tool and plugins for various build tools and CI/CD platforms to make detecting this more easy. In this blog post I’ll show how you can incorporate this in a Jenkins pipeline running on Kubernetes and using Jenkens and SonarQube to display the results of the scan.

Prerequisites

I used the environment described here. This includes a ready configured kubectl and helm installation. For the Jenkins installation and basic pipeline configuration I used the following here. In order to execute the below steps you should have at least a Kubernetes and Jenkins installation ready. If you want to use the literal code samples, you also require the Jenkins configuration as described including this.

OWASP dependency-check

The OWASP foundation provided Dependency-Check plugins for various build tools such as Ant, Gradle and Maven and a Jenkins plugin. They also have a standalone CLI tool available. Mind that the more specific a plugin you use, the more relevant the findings will be. You can for example use the Dependency-Check Jenkins plugin to perform a scan, but it will not understand how dependencies inside a pom.xml work so will not give sufficiently useful results. You will get something like below:

Dependency-Check results using the generic plugin from Jenkins

When you implement the Maven Dependency-Check plugin to produce results and the Jenkins Dependency-Check plugin to get those results visible in Jenkins, you get results which are specific to a Maven build of your Java application. This is quite useful! When using this you will get more accurate results like below.

Dependency-Check using the Java specific Maven plugin

The Jenkins Dependency-Check plugin (which can be used within a pipeline) also produces trend graphs and html reports inside Jenkins.

Trend graphs

Thus use the Maven Dependency-Check plugin to scan your project and use the Jenkins plugin to publish the results generated from the scan to Jenkins. After you have installed and configured SonarQube, you can use the same results to publish them to SonarQube.

Maven plugin configuration

You can find the pom.xml file I used here. You can execute the scan by running mvn dependency-check:check.

       <plugin>  
         <groupId>org.owasp</groupId>  
         <artifactId>dependency-check-maven</artifactId>  
         <version>6.0.2</version>  
         <executions>  
           <execution>  
             <goals>  
               <goal>check</goal>  
             </goals>  
           </execution>  
         </executions>  
         <configuration>  
           <failBuildOnCVSS>7</failBuildOnCVSS>  
           <!-- Generate all report formats -->  
           <format>ALL</format>  
           <!-- Don't use Nexus Analyzer -->  
           <centralAnalyzerEnabled>false</centralAnalyzerEnabled>  
           <!-- Am I the latest version? -->  
           <versionCheckEnabled>true</versionCheckEnabled>  
         </configuration>  
       </plugin>  

Notice the format ALL is specified. This generates an HTML, JSON, XML and CSV report in the target folder. SonarQube will use the JSON report and Jenkins the XML report. I have not looked at the central analyzer (this appears to be a feature which is part of the commercial Nexus distribution). This might help reduce build time since the vulnerability files can be shared across scans and do not need to be downloaded every time.

You can browse the HTML report yourself if you like. Using this you can confirm that the dependencies were identified and actually scanned. My scan found 0 vulnerabilities so I was worried the scan was not executed correctly but the results showed the different dependencies and how they were evaluated so they were correct. A new Spring Boot version does not contain vulnerable dependencies as it should!

Dependency-Check report shows results per dependency scanned

Installing SonarQube

There are various ways you can install SonarQube on Kubernetes. There are Docker images available which require you to create your own Kubernetes resources, but also a Helm chart. The Helm chart is really easy to use. For example, it creates a PostgreSQL database for the SonarQube installation for you without additional effort. The PostgreSQL database which is created, is of course not highly available, clustered, etc. Using Helm value overrides, you can specify your own PostgreSQL DB to use which you can setup to fit your availability needs. The default username and password is admin. Of course change this in a production environment. A small drawback is that the chart is not 100% up to date. Currently it installs version 8.3 while 8.5 is already available. I’m not sure if this is an indication not much maintenance is being done on the chart. I hope this is not the case of course, else I would not recommend it. If you do use it, keep this in mind. For a lab scenario like this one I do not care whether I get version 8.3 or 8.5.

Installing SonarQube on Kubernetes can be done with the following commands using the helm chart;

 helm repo add oteemocharts https://oteemo.github.io/charts  
 helm repo update  
 kubectl create ns sonar  
 helm install -n sonar sonar-release oteemocharts/sonarqube  

After the installation is complete, you can access it with:

 export POD_NAME=$(kubectl get pods --namespace sonar -l "app=sonarqube,release=sonar-release" -o jsonpath="{.items[0].metadata.name}")  
 kubectl -n sonar port-forward $POD_NAME 8080:9000  

And going to http://localhost:8080. Username and password are as indicated admin

SonarQube 8.3 start screen after login

Configuring SonarQube in Jenkins

In the previously described setup, you can access Jenkins with:

 export POD_NAME=$(kubectl get pods --namespace jenkins -l "app.kubernetes.io/component=jenkins-master" -l "app.kubernetes.io/instance=my-jenkins-release" -o jsonpath="{.items[0].metadata.name}")  
 printf $(kubectl get secret --namespace jenkins my-jenkins-release -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo  
 kubectl --namespace jenkins port-forward $POD_NAME 8081:8080   

The second line gives the password of the admin user you can use to login. The last command makes a Jenkins pod available on localhost port 8081. 

In SonarQube you can obtain a secret. The SonarQube pipeline plugin in Jenkins can be configured to use the secret to store results from the build/dependency-check in SonarQube.

Important to mind is that you should not configure the SonarQube Scanner in Jenkins. This is a generic scanner when there is no specific scanner for your build available. Since we are using a Maven build and there is a Maven scanner as plugin available, it is preferable to use that one. A similar logic holds for the dependency-check; preferably use one inside your build over a generic scanner.

Jenkins pipeline

I used the following stages in my pipeline to perform the scan and load the results in Jenkins and SonarQube.

   stage ('OWASP Dependency-Check Vulnerabilities') {  
    steps {  
     withMaven(maven : 'mvn-3.6.3') {  
      sh 'mvn dependency-check:check'  
     }  
   
     dependencyCheckPublisher pattern: 'target/dependency-check-report.xml'  
    }  
   }  
   
   stage('SonarQube analysis') {  
    steps {  
     withSonarQubeEnv(credentialsId: 'sonarqube-secret', installationName: 'sonarqube-server') {  
      withMaven(maven : 'mvn-3.6.3') {  
       sh 'mvn sonar:sonar -Dsonar.dependencyCheck.jsonReportPath=target/dependency-check-report.json -Dsonar.dependencyCheck.xmlReportPath=target/dependency-check-report.xml -Dsonar.dependencyCheck.htmlReportPath=target/dependency-check-report.html'  
      }  
     }  
    }  
   }  

As you can see they have several dependencies. They depend on the OWASP Dependency-Check plugin for publishing results and the SonarQube Scanner for Jenkins to set environment variables which contain the required credentials/secret/hostname to access SonarQube. I’m also using withMaven from the Pipeline Maven Integration plugin. As you can see, I’m using the Maven build to perform the scan and not the dependencyCheck from the Jenkins plugin. To process the results I’m using the SonarQube plugin from Maven instead of the SonarQube Scanner from Jenkins.

A challenge which I didn’t manage to solve on short notice is that Jenkins creates a temporary build container in which it executes the pipeline. After the build, the container will be disposed of. During the build process the OWASP Dependency-Check downloads vulnerability data. This takes a while (around 7 minutes on my machine). 

During a next build, since the build container had been destroyed already, it has to download all the vulnerability data again. The data needs to be stored in a persistent way so only an update of vulnerability data is required which saves a lot of time. Maybe mount a prepared tools container during the build or use an external analyzer (as indicated Sonatype Nexus provides one in their commercial version).

Executing the pipeline

When you execute the pipeline, SonarQube gets fed with various results and can also produce a measure of technical debt in your project. In this case test coverage (produced by the Maven Jacoco plugin) and data produced by the OWASP Dependency-Check. 

SonarQube makes a verdict on whether the build passes or not and this is displayed in Jenkins by the SonarQube Scanner plugin. Of course the Maven plugins can themselves also decide to break the build. The Dependency-Check can do this when high or critical vulnerabilities are discovered (scoring of 7 as specified in the pom.xml, check here).

You can of course further expand this to include more thorough code quality checks by including Maven plugins such as Checkstyle, PMD and JDepend. Using plugins like these help by making people aware of the quality of their code and can help enforce quality rules during the build process. Since it is relatively easy to implement, there is no good reason not to do this.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.